feat(css-trace): inspect CSS rules affecting a DOM element#4
Merged
Conversation
Adds `parity css-trace` — a CLI command that uses Chrome DevTools
Protocol (CSS.getMatchedStylesForNode) to enumerate every CSS rule
contributing to a target element, grouped by stylesheet source.
Two modes:
1. Single URL — `--url ... --selector ...` prints all matched rules,
their source stylesheet, and the computed style for the selected
properties.
2. Comparison — `--prod ... --cand ... --selector ...` traces both
sides and emits a diff of computed-style values.
Use cases:
- Visual regression debugging: when a property's computed value
differs between Fresh prod and the TanStack migration, instantly
see which library / file is responsible.
- Library-introduced gotchas: e.g. daisyUI v5 sets
`scrollbar-gutter: var(--page-scroll-gutter, unset)` on :root and
toggles `--page-scroll-gutter: stable` when a drawer/modal opens,
reserving ~15px on the right edge — `css-trace --url ... --selector
html --filter scrollbar-gutter` surfaces that rule immediately,
saving the usual `grep node_modules/daisyui/**/*.css` round trip.
Implementation notes:
- Filter prop (`--filter scrollbar-gutter,position,...`) trims output
to the properties of interest in both `computed` and the matched
rules.
- `--viewport mobile|tablet|desktop` reuses the same presets as the
main `run` command, so observed differences in vw-dependent rules
(media queries, viewport-units) line up with the regular checks.
- Strips trailing `!important` baked into CDP property values and
derives the flag from either source, so output doesn't render
`!important !important`.
- `:where(...)` / anonymous-selector rules now print as
`(no selector text)` instead of an empty `{`.
There was a problem hiding this comment.
2 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/cli.ts">
<violation number="1" location="src/cli.ts:205">
P2: Documented mutual-exclusion constraint between --url and --prod/--cand is not enforced in the action handler. Add a validation check that exits with a clear error message if conflicting options are provided, and that requires at least one URL mode.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| .description( | ||
| "Inspect which CSS rules (from which stylesheets) are affecting a DOM element. Single URL mode lists every matched rule; --prod + --cand mode diffs computed styles between Fresh and TanStack sides.", | ||
| ) | ||
| .option("--url <url>", "Single URL to inspect (mutually exclusive with --prod/--cand)") |
There was a problem hiding this comment.
P2: Documented mutual-exclusion constraint between --url and --prod/--cand is not enforced in the action handler. Add a validation check that exits with a clear error message if conflicting options are provided, and that requires at least one URL mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli.ts, line 205:
<comment>Documented mutual-exclusion constraint between --url and --prod/--cand is not enforced in the action handler. Add a validation check that exits with a clear error message if conflicting options are provided, and that requires at least one URL mode.</comment>
<file context>
@@ -196,6 +197,37 @@ program
+ .description(
+ "Inspect which CSS rules (from which stylesheets) are affecting a DOM element. Single URL mode lists every matched rule; --prod + --cand mode diffs computed styles between Fresh and TanStack sides.",
+ )
+ .option("--url <url>", "Single URL to inspect (mutually exclusive with --prod/--cand)")
+ .option("--prod <url>", "Production URL (for comparison mode)")
+ .option("--cand <url>", "Candidate URL (for comparison mode)")
</file context>
…ings Biome lint/style/noUnusedTemplateLiteral was failing CI on: - css-trace.ts:209 `✖ Element not found` → "✖ Element not found" - css-trace.ts:231 ` }` → " }" - css-trace.ts:256 `\n── DIFF ...` → "\n── DIFF ..." None of the three had interpolation or special-character handling that would require a template literal. Unblocks the css-trace PR's CI run.
…rod/--cand mutex Two P2 findings from the cubic AI reviewer: 1. css-trace was iterating only `matchedCSSRules` and silently dropping `inherited[].matchedCSSRules`. CSS inheritance carries properties like color, font-*, line-height, visibility from ancestors to the target element, so any computed value that propagates from a wrapper (typography, theme tokens applied to <html>/<body>) was missing from the trace output — "why does this <span> have color: red but no rule shows it?" had no answer in the report. Fix: extract a shared `buildRule` helper, then walk `inherited` (CDP returns it as an array indexed by ancestor distance, 0 = parent, 1 = grandparent, …) and tag each rule with `inheritedFromDistance`. `printResult` shows that as `↑ inherited from ancestor (N)` next to the source URL so the reader can tell direct matches from inherited ones at a glance. 2. `--url` was documented as mutually exclusive with `--prod` / `--cand` but the handler didn't enforce it — passing `--url` together with `--prod` would silently pick the comparison branch (because `isCompare = !!(opts.prod && opts.cand)` was checked first), making `--url` look broken. Fix: explicit validation up front — error if `--url` is combined with either `--prod` or `--cand`, error if comparison mode passes only one of `--prod`/`--cand`, error if no mode given at all. Each branch emits a clear chalk.red message. Identified by cubic.
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.
Summary
New
parity css-tracecommand that uses Chrome DevTools Protocol (CSS.getMatchedStylesForNode) to enumerate every CSS rule contributing to a target element, grouped by stylesheet source. Two modes:parity css-trace --url <url> --selector <sel>— lists all matched rules + computed styles.parity css-trace --prod <url> --cand <url> --selector <sel>— traces both and diffs computed style values.--filter <prop1,prop2,...>scopes the output to specific properties.Why
Real bug encountered debugging the Bagaggio TanStack migration: the delivery drawer hung short of the viewport's right edge on desktop. Root cause was daisyUI v5's
:root { scrollbar-gutter: var(--page-scroll-gutter, unset) }paired with the drawer-toggle rule flipping--page-scroll-guttertostablewhen any drawer opens — reserving ~15px on the right that theposition: fixed; width: 100%drawer couldn't reach.Finding this required
grep -h scrollbar-gutter node_modules/daisyui/**/*.cssto enumerate library rules.parity css-trace --url <site> --selector html --filter scrollbar-gutterwould have surfaced the rule and its source in seconds.Example
$ parity css-trace --url http://localhost:5173 --selector html --filter scrollbar-gutter ── RESULT ──────────────────────────────────────────── URL: http://localhost:5173 Selector: html Computed: scrollbar-gutter: auto Rules (ordered by CDP — most specific last): stylesheet#... (/*! tailwindcss v4.2.4 ...) html { scrollbar-gutter: auto !important; } stylesheet#... (/*! tailwindcss v4.2.4 ...) (no selector text) { scrollbar-gutter: var(--page-scroll-gutter, unset); }Implementation notes
page.context().newCDPSession(page)— no extra deps.CSS.getStyleSheetTextpreviews the first ~100 chars of each stylesheet to disambiguate sources (daisyUI vs app.css vs Tailwind utilities) since CDP doesn't return the URL.!importantbaked into CDP property values and infers the flag, so output doesn't render!important !important.:where(...)/ anonymous-selector rules print as(no selector text)instead of an empty{.--viewport mobile|tablet|desktopreuses the existing presets so viewport-dependent rules (media queries, viewport-units) line up with the regularrunflow.Test plan
bun run checkpasses🤖 Generated with Claude Code
Summary by cubic
Adds a new
parity css-tracecommand to inspect which CSS rules affect a DOM element and optionally diff computed styles between two URLs. Now also traces inherited rules and marks their ancestor level for clearer debugging.New Features
--filterto limit properties,--jsonfor machine-readable output;--viewportpresets and--settle <ms>to wait after load.playwright; strips!importantduplicates and prints anonymous selectors as(no selector text).Bug Fixes
--urlis mutually exclusive with--prod/--cand; require both for comparison; clear error messages.Written for commit afe2a9a. Summary will update on new commits. Review in cubic