feat(react-grab): resolve style edits to design tokens from CSS variables#487
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
There was a problem hiding this comment.
2 issues found and verified against the latest diff
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="packages/react-grab/src/utils/collect-design-tokens.ts">
<violation number="1" location="packages/react-grab/src/utils/collect-design-tokens.ts:57">
P2: Substring `includes` check causes compound token names to accumulate unintended families. For example, `--line-height` matches both `"line-height"` → line-height family and `"height"` → size family; similarly `--font-size` also picks up the `"size"` → size family. This means `matchLength` can incorrectly suggest a line-height or font-size token for a `width`/`height` property.
Consider either ordering keywords from most-specific to least-specific and short-circuiting on first match, or using word-boundary-aware matching (e.g., splitting on `-` segments) to prevent a compound keyword from also triggering its substring.</violation>
<violation number="2" location="packages/react-grab/src/utils/collect-design-tokens.ts:141">
P2: Token collection rescans all stylesheets per element, creating avoidable O(elements × rules) work during copy/prompt generation. Cache collected custom property names per document snapshot and reuse across elements.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 8 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
32e876e to
cc075e7
Compare
…bles Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
… scale Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
… grid Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
…ken marker scan Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
… token snapping Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
efdabf4 to
cddbb85
Compare
…NumericValue, clearer names Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
… snapping Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c16fb73. Configure here.
…naps down) Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>

What
Makes Style mode "switch to design tokens when available," derived from the CSS custom properties already present in the page's cascade.
Because design tokens surface as CSS variables across essentially every styling system, this is library-agnostic — it works for shadcn/ui, Radix, Chakra, MUI, Tailwind v4
@theme, Panda, vanilla-extract, etc., not just one hard-coded framework. (Today the only library awareness in Style mode is the Tailwind class chip/auto-apply.)It does two things:
How
utils/collect-design-tokens.ts: scansdocument.styleSheetsfor--*declarations (cross-origin sheets skipped safely, grouping rules recursed), then resolves each token against the grabbed element viagetComputedStyleso the active theme/scope wins. It classifies tokens into colors and px/rem lengths and returns a smallDesignTokenResolver(matchColor,matchLength,stepLength,hasTokens).EditPanelState) and reused for both stepping and the copied prompt.format-edit-prompt.tsannotates each declaration whose value matches a token with a/* var(--token) */hint, plus a guidance line nudging the agent to prefer the token.step-property.tsusesstepLengthso a plain←/→on a px property walks the project's token scale.Real-world token compatibility
Validated against the react-grab website (Tailwind v4 + shadcn). Two things were needed for it to actually fire there:
getComputedStylereturns theme colors aslab(...)(Tailwind v4 / shadcn compileoklch(...)down tolab()), which the old parser couldn't read.parseAnyColornow has a canvas rasterize fallback: it paints one pixel and reads it back, so any browser-renderable color —lab(),lch(),oklab(),oklch(),color()— resolves to sRGB. On the live site this took color-token detection from 0 → 14 tokens (--card,--accent,--primary, …).calc(var(--spacing) * N)with a single--spacingbase rather than discrete per-step tokens.stepLengthnow walks that base-unit grid for spacing/sizing when there's no discrete scale, so←/→step16px → 20px → 24pxon the 4px grid.Avoiding false matches
Lengths are numerically ambiguous (a
16pxpadding would otherwise "match" a--text-base: 1remfont-size token), so a length token only matches when its name shares the CSS property's family (spacing / size / radius / font-size / line-height / letter-spacing / border-width). Colors match by value (deterministic shortest-name tie-break). The literal value is always preserved in the prompt — the token is a hint, never a forced replacement — so there's zero visual-regression risk.Arrow-key stepping guard rails
--text-*) snaps to neighbours; above the largest token it falls back to a raw step rather than teleporting; below the smallest it snaps up onto the scale.--spacing), it walks that grid.Testing
edit-panel.spec.ts: length/color prompt annotation, non-token length unannotated,←/→snapping through the spacing scale, and the raw-step fallback at the top of the scale. Definition-only tokens added to the Vite e2e app's:root(no visual change).pnpm build,pnpm typecheck,pnpm lintpass. Fulledit-panel.spec.ts+edit-panel-color.spec.tschromium suites pass (124/124).Notes / follow-ups
Surfacing the matched token directly in the Style panel chip (shift-held, alongside/instead of the Tailwind class) is a natural follow-up, kept out to limit blast radius on the panel's UI e2e tests.
Summary by cubic
Style mode in
react-grabnow resolves edits to CSS‑variable design tokens, snaps Arrow‑key px stepping to the token scale (or Tailwind v4--spacinggrid), and annotates copied CSS with token hints. It’s library‑agnostic, detects wide‑gamut token colors likelab()/oklch(), and caches token discovery for faster sessions.New Features
collect-design-tokens.tsto read--*fromdocument.styleSheets, resolve viagetComputedStyle, and classify px/rem length and color tokens (incl.lab(),lch(),oklab(),oklch(),color()), skipping cross‑origin sheets and recursing grouping rules./* var(--token) */per matching declaration and adds one “Prefer the design token” line only when hints exist; raw values are preserved.--spacingbase‑unit grid. Shift keeps a coarse raw step (×10) and Alt/Option adds a fine ±1px raw step — both bypass snapping. Off‑scale values fall back to raw steps; above‑max values snap down on ArrowLeft, below‑min snap up on ArrowRight. Also snaps to a lone token from nearby values.Refactors
parseNumericValue, and clarified names. Memoized the custom‑property name scan keyed by stylesheet count for better performance.Written for commit c47a0ef. Summary will update on new commits.