Round-trip csstruth's own selectors, and tighten the invariants - #2
Merged
Conversation
Necessary to stop the overlap check from blaming an innocent cousin when a static child overflows its own container and laps into the neighbor's box. parent-bleed already reports the escaper bleeding its container on the horizontal axis, so the cousin-overlap is redundant noise on a real app dashboard (a list overflowing its card, a wide control spilling a cell). Suppressing it never hides a defect: the escape geometry is impossible without a horizontal parent-bleed at some level between the node and the container, which that rule always surfaces. Scoped to the horizontal axis on purpose. A vertical-only escape is not yet covered by parent-bleed, so it stays flagged — a silently dropped check would be worse than a false positive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Necessary to make the overlap and parent-bleed rules honest on both axes. parent-bleed now measures the vertical axis (a child taller than a height-constrained container), mirroring the existing horizontal check and clamped by the nearest overflow-y the same way. That makes it safe to extend the overlap descendant-escape suppression to vertical: a list overflowing its card and lapping the card below is the escaper's bleed, not the innocent cousin's overlap, and parent-bleed now surfaces the real defect on either axis. Extending parent-bleed to vertical surfaced a pre-existing philosophy gap: it flagged deliberately displaced children (a negative-margin avatar over a card, a positioned nudge), more visibly upward/downward. parent-bleed now exempts a child moved out of its box on purpose — a translating transform, a negative margin, or a non-static position with a real inset offset. Only a genuine 2D translation counts: translateZ(0) and other GPU-compositing no-ops resolve to zero translation and stay flagged, so a real overflow under them is never silently dropped. A bare position:relative with no offset stays in normal flow and still bleeds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Necessary because selectorOf emits raw class names and Tailwind's variant and arbitrary-value classes (md:flex, w-[calc(100%-2rem)], data-[state=open]:flex, content-['x']) carry characters querySelector reads as syntax. csstruth printed those selectors in every layout/check line but resolveNode and inspect could not consume them — the tool's output did not round-trip back into the tool, forcing a backendNodeId detour. resolveNode now retries with an escaped selector after the raw one fails to match, and inspect's browser probe falls back the same way. The escape is an allowlist — every character that is not an identifier or structural selector char — so a new Tailwind offender never has to be enumerated by hand. Retry only fires on a miss, so a selector that matches raw is left untouched. Inner-dot arbitrary values and #-in-brackets still collide with the class and id separators and need a backendNodeId. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Necessary so layout --selector behaves like the intuition inspect and explain already give. findNode matched only the exact rendered selectorOf string, a bare #id, or a single class, so `layout --selector nav` on a classed element and `--selector div.card.featured` (a subset of an element with more than three classes) both silently returned nothing. findNode now parses a simple compound selector (tag + #id + class subset) and matches against the node's classes directly, so Tailwind classes need no escaping here. Anything the parser can't tokenize — combinators, attribute or pseudo selectors, or a dotted/#-bearing arbitrary value like .gap-[0.5rem] or .bg-[#fff] — falls back to the raw selectorOf/#id/.class shorthands the old contract matched, so the change is purely additive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Necessary to stop flagging a text link inside a sentence as a too-small tap target. Such a link's height is constrained by the surrounding line-height — 24px is unreachable without reflowing the prose — which is exactly the case WCAG 2.5.8 exempts, so reporting it is noise on any content-heavy page. The exemption is narrow so it never hides a real bug: the element must carry its own text (an icon-only link is not line-height-constrained — a 16px icon can grow to 24px without reflow — so it still flags), be display:inline, and sit in a parent that has real text around it. Inputs are excluded and keep routing through the label-measurement path, since an appearance:none input can itself compute display:inline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CLAUDE.md gives contributors the full invariant-rule set and the field-tag convention so the rationale for each exemption is one grep away. The skill's new Selectors section tells users csstruth's own output round-trips straight back in — Tailwind variant and arbitrary-value classes included — and that layout scoping now takes a bare tag or a class subset, and folds the built-in exemptions into "Reading violations" so nobody re-investigates a handled case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five improvements that came out of actually running csstruth against real Tailwind pages (trmnl.com) and hitting friction the tool itself created. The two that bit most are self-inconsistencies: csstruth printed selectors it couldn't consume, and
layout --selectormatched differently thaninspect/explaindid.inspect/explainaccept csstruth's own output — Tailwind variant/arbitrary-value classes (md:flex,w-[calc(100%-2rem)],data-[state=open]:flex) now round-trip instead of erroring. Retry only on a miss, so a valid selector is never disturbedlayout --selectora compound matcher (tag +#id+ class subset), sonav(bare tag on a classed element) anddiv.card.featured(a subset of an element with >3 classes) resolve like they already do ininspect/explain. Falls back to the old exact/raw contract for anything it can't tokenize, so a dotted or#-bearing arbitrary value (.gap-[0.5rem],.bg-[#fff]) still resolvesparent-bleedto the vertical axis — a list overflowing its card and lapping the card below is reported as the child bleeding its own container, not miscast as the innocent cousin's overlapparent-bleed. Only a genuine 2D translation counts, sotranslateZ(0)and other GPU-compositing no-ops still flag a real overflowEvery exemption above was tightened by an adversarial review that found a real hidden bug the RED/GREEN tests passed clean over:
translateZ(0)masking a real overflow, a dotted-class scoping regression, and an icon-only link plus anappearance:noneinput slipping through the tap-target exemption. Those are the parts worth the closest look.Validated end-to-end against live trmnl.com — the Tailwind selectors round-trip,
layout --selector navscopes, and inline prose links stop nagging while standalone/icon links still flag. The displacement exemption has no natural repro on that page (the right sign for an exemption); it stays fixture + review covered.Full test suite green — the one intermittent failure is the documented macOS Chrome-shutdown lifecycle test, which passes in isolation.
Todo