feat(check): add data-layout-allow-caption-zone waiver (#2853)

* feat(check): add data-layout-allow-caption-zone waiver

Opt intentional lower-third copy out of caption_zone_collision.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(check): address caption-zone waiver review nits

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(skills): document caption-zone waiver on CLI agent path

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(cli): document caption-zone waiver under check, not inspect

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xuanru Li
2026-07-28 15:56:43 -07:00
committed by GitHub
co-authored by Cursor
parent a996e91e5d
commit 3a7950fd63
8 changed files with 77 additions and 14 deletions
+20 -8
View File
@@ -238,6 +238,21 @@ function geometryIssueAnchor(candidate: CheckGeometryCandidate, time: number) {
};
}
function captionCenterInZone(
rect: CheckGeometryCandidate["rect"],
zone: NonNullable<CheckOptions["captionZone"]>,
canvas: Canvas,
): { inside: boolean; cy: number } {
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const inside =
cx >= zone.x0 * canvas.width &&
cx <= zone.x1 * canvas.width &&
cy >= zone.y0 * canvas.height &&
cy <= zone.y1 * canvas.height;
return { inside, cy };
}
function captionFinding(
candidate: CheckGeometryCandidate,
options: CheckOptions,
@@ -246,13 +261,9 @@ function captionFinding(
): { key: string; issue: AnchoredLayoutIssue } | null {
const zone = options.captionZone;
if (!zone || candidate.kind !== "text" || !candidateIsSized(candidate, canvas)) return null;
const cx = candidate.rect.left + candidate.rect.width / 2;
const cy = candidate.rect.top + candidate.rect.height / 2;
const inside =
cx >= zone.x0 * canvas.width &&
cx <= zone.x1 * canvas.width &&
cy >= zone.y0 * canvas.height &&
cy <= zone.y1 * canvas.height;
// Backstop for mocks/non-browser sources; browser already strips via closest() (own attrs only here).
if ("data-layout-allow-caption-zone" in candidate.dataAttributes) return null;
const { inside, cy } = captionCenterInZone(candidate.rect, zone, canvas);
if (!inside) return null;
const text = candidate.text.slice(0, 48);
const pctFromBottom = Math.round(((canvas.height - cy) / canvas.height) * 100);
@@ -264,7 +275,8 @@ function captionFinding(
severity: zone.severity === "error" ? "error" : "warning",
text,
message: `<${candidate.tag}> "${text}" is centred in the reserved caption band (~${pctFromBottom}% up from the bottom).`,
fixHint: "Keep main content outside the configured caption band.",
fixHint:
"Keep main content outside the configured caption band, or mark intentional lower-third copy with data-layout-allow-caption-zone.",
},
};
}