From 66baa1eacf62c1f29583c9dc319ae0c1df970b20 Mon Sep 17 00:00:00 2001 From: Xuanru Li <157947275+xuanruli@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:42:09 -0700 Subject: [PATCH] fix(check): bill a clipped text box once, not twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `nearestConstraint` returns the element itself when the element clips, because a clipping box is a constraint candidate. `textOverflowIssues` then reported the same physical defect a second time as `text_box_overflow inside `, so every `clipped_text` arrived as two errors and inflated `errorCount`. The guard is `container === element && containerClips`, not `container === element`: a painted, sized box is also a constraint candidate without clipping anything, and there `clipped_text` cannot speak (it needs `clipsOverflow`). An earlier revision omitted the second condition and silenced a real defect — a painted 200px chip whose text ink ran 520px past its edge reported nothing at all. The new test pins that case; it fails against the over-broad guard and the duplicate test fails against HEAD. Measured over 31 fixtures: 9 changed, all the same shape — the duplicate gone, the `clipped_text` error and `ok: false` kept. Includes four fixtures that are genuine defects wearing `overflow:auto` / `ellipsis` / `line-clamp`; all four still gate. 13 registry examples byte-identical. Co-Authored-By: Claude Opus 5 --- .../cli/src/commands/layout-audit.browser.js | 4 +++ .../src/commands/layout-audit.browser.test.ts | 27 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/layout-audit.browser.js b/packages/cli/src/commands/layout-audit.browser.js index 382184309a..1c8cc381bc 100644 --- a/packages/cli/src/commands/layout-audit.browser.js +++ b/packages/cli/src/commands/layout-audit.browser.js @@ -458,8 +458,12 @@ ? tolerance : Math.max(tolerance, parsePx(elementStyle.fontSize) * 0.2); const containerOverflow = overflowFor(textRect, containerRect, tolerance, verticalTolerance); + // A clipping box that is its own nearest constraint is `clipped_text`; billing the same defect + // here as well double-counted it. A NON-clipping self-constraint still reports — nothing else covers it. + const isClippedSelfConstraint = container === element && containerClips; if ( containerOverflow && + !isClippedSelfConstraint && !hasTextClipOptOut(element) && !clippedByAncestor(element, container) ) { diff --git a/packages/cli/src/commands/layout-audit.browser.test.ts b/packages/cli/src/commands/layout-audit.browser.test.ts index eb551164ba..08882ec20a 100644 --- a/packages/cli/src/commands/layout-audit.browser.test.ts +++ b/packages/cli/src/commands/layout-audit.browser.test.ts @@ -219,7 +219,9 @@ describe("layout-audit.browser", () => { .map((issue) => issue.code) .filter((code) => code === "clipped_text" || code === "text_box_overflow"); - expect(textOverflowCodes()).toEqual(["clipped_text", "text_box_overflow"]); + // One code: a clipping box that is its own nearest constraint is `clipped_text`, and billing the + // same defect as `text_box_overflow` too counted it twice. + expect(textOverflowCodes()).toEqual(["clipped_text"]); document.querySelector("#overflow-optout")?.setAttribute("data-layout-allow-overflow", ""); expect(textOverflowCodes()).toEqual([]); document.querySelector("#overflow-optout")?.removeAttribute("data-layout-allow-overflow"); @@ -227,6 +229,29 @@ describe("layout-audit.browser", () => { expect(textOverflowCodes()).toEqual([]); }); + it("still flags a painted, NON-clipping box that is its own nearest constraint", () => { + // `container === element` does not imply the element clips: a painted, sized box is a + // constraint candidate too. `clipped_text` cannot speak for it (nothing is clipped), so the + // de-duplication guard must not swallow this — it would leave the defect with no code at all. + // `#bubble` is the harness's painted, radiused box — here it carries the text itself, so it is + // its own nearest constraint while clipping nothing. + document.body.innerHTML = ` +
+
Enterprise plan includes unlimited renders
+
+ `; + installGeometry({ + root: rect({ left: 0, top: 0, width: 640, height: 360 }), + bubble: rect({ left: 40, top: 60, width: 200, height: 40 }), + text: rect({ left: 40, top: 65, width: 520, height: 30 }), + }); + installAuditScript(); + + const found = runAudit().filter((issue) => issue.code === "text_box_overflow"); + expect(found).toHaveLength(1); + expect(found[0]?.selector).toBe("#bubble"); + }); + it("does not flag glyph-ink vertical spill within the font-metric band on a non-clipping box", () => { // A painted, non-clipping caption-word-like box whose glyph ink (text rect) exceeds its snug // line-height box by a few px vertically — normal typography, nothing is clipped. (fontSize