fix(cli): skip contrast for intentionally covered text (#2366)

This commit is contained in:
Miguel Ángel
2026-07-13 18:22:30 -04:00
committed by GitHub
parent 74fb4dbcc3
commit 8e50e8477f
2 changed files with 72 additions and 0 deletions
@@ -131,6 +131,12 @@ window.__contrastAuditPrepare = function () {
return !paintsAnyProbePoint(el, rect);
}
function isIntentionallyOccluded(el, rect) {
if (typeof document.elementFromPoint !== "function") return false;
if (!el.closest || !el.closest("[data-layout-allow-occlusion]")) return false;
return !paintsAnyProbePoint(el, rect);
}
var out = [];
var restores = [];
// Registered BEFORE the walk starts (not after it finishes) and pushed to
@@ -200,6 +206,10 @@ window.__contrastAuditPrepare = function () {
if (rect.width < 8 || rect.height < 8) continue;
if (rect.right <= 0 || rect.bottom <= 0) continue;
if (isClippedAway(el, rect)) continue;
// The layout audit's explicit occlusion opt-out means this text is allowed
// to sit behind another scene. Skip contrast only while every probe point
// is actually covered; the same copy is audited normally when visible.
if (isIntentionallyOccluded(el, rect)) continue;
// For SVG text, `fill` is the paint that's actually rendered; `color` is
// frequently just the inherited/initial value and unrelated to what's on
@@ -635,6 +635,68 @@ describe("contrast-audit.browser clip-path visibility", () => {
expect(selectors).not.toContain("#rail-label");
});
it("excludes intentionally occluded text from contrast reports", async () => {
document.body.innerHTML = `
<div id="root" data-composition-id="main" data-width="640" data-height="360">
<div id="headline" data-layout-allow-occlusion>Covered copy</div>
<div id="cover"></div>
</div>
`;
vi.spyOn(window, "getComputedStyle").mockImplementation(
() =>
({
display: "block",
visibility: "visible",
opacity: "1",
color: "rgb(255, 255, 255)",
fontSize: "32px",
fontWeight: "400",
clipPath: "none",
}) as unknown as CSSStyleDeclaration,
);
vi.spyOn(document.getElementById("headline")!, "getBoundingClientRect").mockReturnValue(
rect({ left: 100, top: 100, width: 400, height: 40 }),
);
(document as unknown as { elementFromPoint: () => Element | null }).elementFromPoint = () =>
document.getElementById("cover");
installContrastScript();
expect(await runContrastAudit()).toEqual([]);
});
it("still audits visible text that allows occlusion", async () => {
document.body.innerHTML = `
<div id="root" data-composition-id="main" data-width="640" data-height="360">
<div id="headline" data-layout-allow-occlusion>Visible copy</div>
</div>
`;
vi.spyOn(window, "getComputedStyle").mockImplementation(
() =>
({
display: "block",
visibility: "visible",
opacity: "1",
color: "rgb(255, 255, 255)",
fontSize: "32px",
fontWeight: "400",
clipPath: "none",
}) as unknown as CSSStyleDeclaration,
);
vi.spyOn(document.getElementById("headline")!, "getBoundingClientRect").mockReturnValue(
rect({ left: 100, top: 100, width: 400, height: 40 }),
);
(document as unknown as { elementFromPoint: () => Element | null }).elementFromPoint = () =>
document.getElementById("headline");
installContrastScript();
const entries = await runContrastAudit();
expect(entries.map((entry) => entry.selector)).toContain("#headline");
});
it("excludes text that has left the canvas from contrast reports", async () => {
document.body.innerHTML = `
<div id="root" data-composition-id="main" data-width="640" data-height="360">