Skip to content

feat: Phase 3 — migrate call sites to object-form css() + token.*#2792

Merged
viniciusdacal merged 16 commits into
mainfrom
feat/migrate-classnames-call-sites
Apr 18, 2026
Merged

feat: Phase 3 — migrate call sites to object-form css() + token.*#2792
viniciusdacal merged 16 commits into
mainfrom
feat/migrate-classnames-call-sites

Conversation

@viniciusdacal
Copy link
Copy Markdown
Contributor

Final phase of the drop-classname-utilities feature. Migrates all first-party consumers from the legacy shorthand-string arrays to object-form css()/variants() with the typed token.* helper, and fixes the five blocker classes the first adversarial review caught.

Public API Changes

Additions (non-breaking):

  • SelectorKey now accepts ancestor-selector patterns (`${string} &`, `${string} &${string}`) so [data-theme="dark"] & passes StrictStyleBlock.
  • CSSPropertyName gains scrollbar-width, scrollbar-color, scrollbar-gutter.
  • @vertz/ui re-exports SelectorKey, StyleBlock, StyleDeclarations.

No breaking changes in this PR. The shorthand parser still lives; Phase 4 removes it.

Summary

  1. Mapper raw-alias expansion (B1-B3, S1-S2)transition:colors now expands to the full 7-prop chain; tracking:tight-0.025em; grid-cols:Nrepeat(N, minmax(0, 1fr)); aspect:{square,video,photo} → ratio values; top:N/inset:N use the spacing scale. Covered by new fixture 13-raw-aliases.tsx.
  2. theme-shadcn completion (B4) — all 43 style files now object-form. Button/badge/sheet/drawer/skeleton/slider hand-migrated where the rewriter couldn't reach identifier-referenced configs or kebab-case keys inside strict StyleBlock contexts. bgOpacity helper now emits camelCase backgroundColor.
  3. landing migration (B5) — 22 components and pages migrated. Vendor-prefixed properties now use strict camelCase (WebkitOverflowScrolling, WebkitBackdropFilter).
  4. Downstream re-run — examples (Linear, task-manager, entity-todo), dev-orchestrator site, ui-auth OAuth button, and create-vertz-app templates re-swept after the mapper fix so none of them ship invalid CSS.
  5. Type system extensions (ui package)SelectorKey/CSSPropertyName extended; types re-exported from @vertz/ui.

Review Findings & Resolutions

Blocker Status Commit
B1. transition aliases → literal invalid CSS ✅ resolved 67e62fe25 + ef9b1767a + c0f9b6a4a
B2. tracking aliases → invalid values ✅ resolved 67e62fe25 + c0f9b6a4a
B3. grid-cols:N → numeric string ✅ resolved 67e62fe25 + ef9b1767a
B4. theme-shadcn incomplete (574 strings) ✅ resolved (0 remaining) c0f9b6a4a
B5. landing not migrated (344 strings) ✅ resolved 4f3ff5586
B6. ui-auth dist stale ✅ resolved (rebuild + proper CSS in dist) ef9b1767a

Re-reviewed by an independent adversarial agent — all six blockers verified resolved.

Phases

  • Phase 1: Object-form css() end-to-end ✅
  • Phase 2: token.* helper + typed augmentation ✅
  • Phase 3: Migrate first-party call sites ✅ (this PR)
  • Phase 4: Remove token parser + resolver + s() (next)
  • Phase 5: Docs + changeset + retrospective (next)

Test Plan

  • vtz test scripts/migrate-classnames — 130/130 passing
  • vtz test in packages/theme-shadcn — 136/136 passing
  • vtz test in packages/ui — 2756/2756 passing
  • tsgo --noEmit clean in theme-shadcn, ui-auth, ui
  • Landing typecheck clean except pre-existing Cloudflare Workers errors in presence-room.ts (unchanged on this branch)
  • Quality gates green across touched packages
  • GitHub CI green

🤖 Generated with Claude Code

viniciusdacal and others added 16 commits April 17, 2026 23:54
…tation

Switches TokenPath from an open `[key: string]: TokenPath` index signature
to a mapped type over a finite union of framework-known scale keys. This
bypasses `noUncheckedIndexedAccess` widening so every dot-chain access
(e.g. `token.color.primary[500]`) stays `TokenPath` rather than
`TokenPath | undefined`.

Adds `VertzThemeRadius` and `VertzThemeShadow` augmentation interfaces
alongside the existing `radius`/`shadow` entries on `VertzThemeTokens`.
Extends the classname migration mapper to emit `token.radius.*` and
`token.shadow.*` references for `rounded:*` and `shadow:*` shorthands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Converts 38 style modules from array-form shorthand strings to the new
object-form css() input backed by `token.*` references. 143 rewritten
sites, 224 token references. Tests and typecheck pass against the new
TokenPath shape.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Identifiers like `2xl` / `3xl` cannot appear as dot-access property
names in JS. The mapper now routes `font:2xl`, `rounded:2xl`, and
`shadow:2xl` through `token.<ns>['2xl']`. Also expands the `raw`
value path to append a `px` suffix for the `border-r|l|t|b` sides
to match the runtime resolver's existing behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-runs the classname migration against theme-shadcn/src after the
mapper fix. `border-r/l/t/b:N` now emits `'<N>px'` instead of bare
`'<N>'`, matching the CSS grammar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…token.*

Converts 17 component / page files from array-form shorthand strings
to the new object-form css() input backed by `token.*` references.
69 rewritten sites, 162 token references. Tests and typecheck pass
against the new TokenPath shape. Array-form entries that mix with
object literals were preserved as-is for follow-up hand-migration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…selector unwrap

Why: earlier migration script rejected any non-string element in a shorthand
array, blocking call sites that mix 'p:4' shorthands with inline selector
objects, spread expressions, and trailing call-expression wrappers. Also
missed extending mapper/generator coverage needed for theme-shadcn and
examples migration.

Changes:
- rewriter: mixed-array handling converts strings via the shorthand pipeline
  while preserving objects, bare identifier/call/property-access spreads,
  and explicit SpreadElement nodes (...base).
- rewriter: single-element wrapper arrays unwrap when the parent key is a
  selector ('&foo', '@media', computed [DARK]) so [animationDecl('x')]
  collapses to animationDecl('x').
- mapper/generator: coverage for additional shorthands (ring variants,
  opacity percentages, keyed tokens like token.color['muted-foreground']).
- tests: unit coverage for every new branch — 24 rewriter scenarios,
  mapper/generator cases, and regression guard for compound variants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ran scripts/migrate-classnames across entity-todo, linear, and task-manager
to drop legacy array-form shorthand strings in favor of object-form css()
with explicit token.* references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ss() with token.*

Follows-up 06c168939 with the sites/ files the improved migrator can now
handle (mixed shorthand + selector arrays, spread helpers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ith token.*

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-form css() with token.*

Add migrate-templates.ts helper that rewrites css()/variants() calls inside
template literals by unescaping the body, running rewriteSource, and merging
the 'token' export into the existing 'vertz/ui' import (multi-line aware).

Also manually update CLAUDE.md documentation code blocks (object-form and
token references) and three templates whose shorthands (font:6xl, font:md,
flex:1) have no mapping — replaced with supported values or raw CSS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…hand tests for Phase 4

- Rewrite variants.test.ts and css.test.ts to object-form css() with token.* references.
  Assertions now match var(--spacing-4), var(--font-weight-bold), etc. instead of
  resolved pixel values.
- Rewrite variants.test-d.ts using augmented token keys only (spacing 1/4/8,
  color.background/foreground/primary[500,700]). Switch ButtonVariants shape from
  StyleEntry[] to StyleBlock so object-form variants type-check.
- Fix one stray shorthand array in native-compiler.test.ts.
- Tag 6 shorthand-only test files with REMOVED-IN-PHASE-4 banners so Phase 4
  deletion is mechanical: s.test.ts, shorthand-parser.test.ts,
  shorthand-coverage.test.ts, token-resolver.test.ts, css.test-d.ts,
  css-integration.test.ts.

Closes the Phase 3 exit criterion: zero array-of-shorthand-strings remain in
first-party call sites (tests/ files still carry the legacy API but are tagged
for deletion).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-B3, S1-S2)

The mapper's 'raw' valueType previously quoted every value literally, which
produced invalid CSS for transition:colors → 'colors', tracking:tight → 'tight',
grid-cols:2 → '2', etc. Port the per-property expansions that
token-resolver.ts:447-526 performed:

- transition:{none,all,colors,shadow,transform,opacity} → full multi-prop chain
- tracking:{tight,tighter,normal,wide,wider,widest} → em letter-spacing values
- grid-cols:N → repeat(N, minmax(0, 1fr))
- aspect:{square,video,photo} → aspect-ratio values
- top/right/bottom/left/inset:N → spacing scale lookup

Fixture 13-raw-aliases.tsx covers the new paths. A companion one-shot
fix-literal-aliases.ts script rewrites already-migrated object literals that
escaped the AST visitor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ration

- SelectorKey now accepts ancestor-selector patterns (\`\${string} &\` and
  \`\${string} &\${string}\`) so \`[data-theme="dark"] &\` descriptors pass
  StrictStyleBlock validation.
- Add scrollbar-width, scrollbar-color, scrollbar-gutter to CSSPropertyName —
  used by the landing hero's scroll overlay and other standard CSS controls
  that were previously rejected by the strict property union.
- Export SelectorKey, StyleBlock, StyleDeclarations from \`@vertz/ui\` so
  theme-shadcn can type its style constants without reaching into internals.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…les (B4)

Re-run the migration on every file under \`packages/theme-shadcn/src/styles/\` now
that the mapper handles mixed arrays, raw-alias expansion, and spread
expressions. Hand-migrate \`button\`, \`badge\`, \`sheet\`, \`drawer\`, \`skeleton\`,
\`slider\`, and \`_helpers\` where the automated script could not reach
identifier-referenced config objects or kebab-case property keys inside strict
StyleBlock contexts.

Key changes:
- \`bgOpacity\` helper now returns camelCase \`backgroundColor\` to satisfy the
  strict CSS-property typing.
- All \`[data-theme="dark"] &\` descriptors use the new SelectorKey pattern.
- \`transition:colors/all/shadow\` expansions inlined; \`tracking:tight\` replaced
  with \`-0.025em\` literal values.
- Outline-variant test updated to assert the new object shape.

136/136 theme-shadcn tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…* (B5)

Phase 3 Task 3 was marked done but no commit had actually touched
\`packages/landing/src\`. Run the migration script across every component and
page, then hand-fix the raw-alias regressions exposed by B1.

Vendor-prefixed properties (WebkitOverflowScrolling, WebkitBackdropFilter) are
now written in the strict camelCase form required by \`StrictStyleBlock\`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…fter raw-alias fix

Sweep every first-party consumer again so the invalid \`transition: 'colors'\`,
\`letterSpacing: 'tight'\`, and \`gridTemplateColumns: '2'\` literals introduced
by the earlier pass get replaced with the real expanded values.

Covers the flagship Linear clone, task-manager, entity-todo examples, the
dev-orchestrator site, ui-auth's OAuth button, and the create-vertz-app
scaffolding templates — all of which were shipping invalid CSS before this fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@viniciusdacal viniciusdacal merged commit 30d4e04 into main Apr 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant