Skip to content

refactor(ui): semantic design-token system + hardened primitives#4

Merged
DCCA merged 1 commit into
mainfrom
refactor/design-system-tokens
Jun 20, 2026
Merged

refactor(ui): semantic design-token system + hardened primitives#4
DCCA merged 1 commit into
mainfrom
refactor/design-system-tokens

Conversation

@DCCA

@DCCA DCCA commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves the full component audit from the high-end-visual-design review. Two structural problems were the core: the token layer was half-built and bypassed by every primitive, and there was no shared motion language. Everything else was detail polish — all addressed here.

Tokens & theming

  • Semantic HSL tokens (--primary/secondary/muted/accent/destructive + hover, --border, --input, --ring) in globals.css, mapped in tailwind.config.js. Primitives now reference tokens instead of hardcoded emerald-700/slate-300 literals → single source of truth.
  • .dark token block, opt-in via class="dark" (light stays default). Deliberately kept outside @layer base so Tailwind doesn't tree-shake the unreferenced .dark selector (verified it ships in the built CSS).
  • Shared ease-swift cubic-bezier; darkMode: "class".

Primitives

  • Button — token colors, transform+shadow motion, active:scale-[0.98], offset focus ring, icon slot (gap-2 [&_svg]), inner top highlight on filled variants, cursor-not-allowed.
  • Card — machined surface (inner highlight + soft tinted shadow, no hard border fighting it), concentric rounded-lg, CardTitle gains an as prop (no longer hardcodes <h3>), refined weight.
  • Input/Select/Textarea — unified --input border, offset focus ring, aria-[invalid=true] error state, shadow/transition parity. Select renders a custom chevron (appearance-none) instead of the native OS arrow.
  • Badge — tokens + shape variant (pill default for counts, square for labels).
  • Separatorrole/aria-orientation + end-faded gradient.

Usage sites

  • Editor: dedicated color-input treatment; h1 page title, h2 sections.
  • Popup: square "New" badge; h1 title.
  • Viewer: h1 title, h2 "Annotated Image".

Verification

  • npm run check — typecheck + lint + 26 tests + build all green
  • Confirmed all token utilities (bg-primary, border-input, ring-ring, ease-swift, aria-[invalid=true], active:scale-[0.98], …) and the .dark block compile into dist/assets/globals.css
  • Playwright browser shot skipped (no Chrome binary in this env)

Notes / scope

  • Dark mode is infrastructure only — tokens + primitives are dark-ready, but the app screens (editor/viewer) still use some hardcoded slate-* utilities, so a fully-correct dark theme would be a follow-up. Default is unchanged (light).
  • Visual values were kept faithful to the prior hardcoded colors, so light-mode appearance is intentionally near-identical — the win is structural.

🤖 Generated with Claude Code

Resolves the full component audit. The token layer was previously
half-built (an unused --ring token, no semantic colors) and bypassed by
every primitive; there was also no shared motion language.

Tokens & theming:
- Add semantic HSL tokens (primary/secondary/muted/accent/destructive +
  hover, border, input, ring) in globals.css, mapped in tailwind.config.js.
  Components now reference tokens instead of hardcoded Tailwind literals,
  giving a single source of truth.
- Add a .dark token block (opt-in via class="dark"; light stays default).
  Kept outside @layer base so Tailwind does not tree-shake the selector.
- Add a shared `ease-swift` cubic-bezier and enable darkMode: "class".

Primitives:
- Button: token colors, transform+shadow motion on ease-swift,
  active:scale-[0.98] press feedback, offset focus ring (ring-ring +
  ring-offset), icon slot (gap-2 + [&_svg]), inner top highlight on filled
  variants, disabled:cursor-not-allowed.
- Card: token surface with machined depth (inner highlight + soft tinted
  shadow), concentric rounded-lg, CardTitle gains an `as` prop (no longer
  hardcodes <h3>), refined font-bold weight.
- Input/Select/Textarea: unified --input border, offset focus ring,
  aria-[invalid=true] error state, shadow/transition parity. Select now
  renders a custom chevron (appearance-none) instead of the native arrow.
- Badge: tokens + shape variant (pill default for counts, square option).
- Separator: role/aria-orientation semantics + end-faded gradient.

Usage sites:
- Editor: dedicated color-input treatment; page title -> h1, sections -> h2.
- Popup: square "New" badge; title -> h1.
- Viewer: title -> h1, "Annotated Image" -> h2.

Verified: npm run check green; confirmed token utilities and the .dark
block compile into the built CSS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DCCA DCCA merged commit 404ef3c into main Jun 20, 2026
0 of 2 checks passed
@DCCA DCCA deleted the refactor/design-system-tokens branch June 20, 2026 21:56
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