Nuxt Design Tokens
Token-driven design with Tailwind 4 @theme static + Nuxt UI semantic
classes. The point: brand and dark-mode behaviour live in CSS variables,
not Tailwind utility classes. Templates stay semantic; the variables do
the work.
When to use this skill
- Registering custom type scales (display sizes, fluid sizes)
- Adding motion curves, brand palettes, or font stacks as tokens
- Building dark-mode-correct surfaces without raw palette classes
- Adding contextual CSS overrides (e.g. an elevated tone within a section)
- Wiring View Transitions for theme toggles
- Configuring component-wide defaults via
app.config.ts
Core rules
- No raw palette classes in templates. Use
text-default,bg-elevated,border-muted. Nevertext-gray-800orbg-stone-50. Dark mode flips the semantic class via Nuxt UI; raw classes don't flip. - Tokens live in
@theme static, not inline. Custom type sizes, motion curves, font stacks belong inmain.css. Tailwind picks them up as utilities (text-display-xl,ease-out-expo). - Brand palette lives in
app.config.ts. Overrideprimaryandneutralon the Nuxt UI palette; never hand-set the colour token unless you need a non-Tailwind hue. - Arbitrary values are a sharp tool. Prefer the scale even at ±1-2 px.
Reserve
[clamp(...)],[52ch],[var(--foo)]for things the scale genuinely cannot express. - One contextual CSS rule beats five utility overrides. When a
container needs to shift the palette of its children, write a single
html.dark .my-context { --ui-bg: ... }rule.
Reference files
- tokens.md —
@theme staticregistration, fluid type scales with paired weight/tracking/line-height, motion curves, font stacks - semantic-colors.md — Nuxt UI semantic
class catalogue, dark-mode auto-flip, brand palette via
app.config.ts, component default variants - contextual-overrides.md — when
to rebind
--ui-*vars in a section, the.section-darkpattern, scoping rules - view-transitions.md — theme-toggle
radial wipe via View Transitions API, fallback handling,
ClientOnlywrapper pattern - fonts.md —
@nuxt/fontsself-hosting, weight subsetting, fallback metrics to avoid FOUT, preload strategy, variable fonts,font-displaychoice per role
Quick decision table
| Need | Where it lives |
|---|---|
| Brand primary colour | app.config.ts → ui.colors.primary |
| Custom type scale | main.css → @theme static { --text-* } |
| Motion curve | main.css → @theme static { --ease-* } |
| Default form input size | app.config.ts → <component>.defaultVariants.size |
| Section-scoped palette tweak | main.css → single .my-class { --ui-* } rule |
| Smooth theme toggle | ThemeToggle.vue + ::view-transition-* keyframes |
Anti-patterns
- ❌
text-gray-500/bg-stone-100in templates — breaks dark mode - ❌ Defining
--ui-primary-500directly in CSS — useapp.config.tsinstead so Nuxt UI computes the full scale - ❌ One-off
text-[15px]everywhere — register a token if you use it twice - ❌ Putting brand colour decisions in
nuxt.config.ts— wrong file - ❌ Tailwind plugins for custom utilities — Tailwind 4 expects tokens via CSS, not JS config
Related skills
- nuxt-ui — component API, the
uiprop, slot overrides - nuxt-components —
tailwind-variantsfor component-level variant APIs; consumes the tokens defined here - nuxt-config —
app.config.tsplacement