What is Semantic tokens?

Semantic tokens: Semantic tokens are design tokens named for their purpose, such as fg-muted or danger, rather than their value, so themes can change values safely.

A semantic token describes a job: the page background, secondary text, a destructive action, the focus ring. It points at a raw value, usually through a primitive token, but the component only knows the job. text-fg-muted says "secondary text"; text-gray-500 says "this particular gray". The first survives a dark theme, a rebrand and a high-contrast mode unchanged. The second has to be found and edited in every file that uses it.

How it works

css
/* primitives */
:root {
  --violet-600: oklch(0.53 0.215 283);
  --violet-400: oklch(0.7 0.165 285);
  --gray-950: oklch(0.185 0.012 268);
  --gray-50: oklch(0.965 0.003 270);
}

/* semantic tokens: the only names components use */
:root { --accent: var(--violet-600); --fg: var(--gray-950); }
.dark { --accent: var(--violet-400); --fg: var(--gray-50); }

A component styled with bg-accent text-on-accent is now correct in both themes without a single dark: class. Adding a third theme means adding a third block of mappings, not touching components.

Naming guidelines

  • Name by role and relationship. bg, surface, raised and sunken for surfaces; fg, fg-muted and fg-subtle for text strength; border and border-strong for lines.
  • Pair foreground and background roles. accent with on-accent, ink with on-ink. The pair is what you test for contrast.
  • Keep status roles equal in weight. success, warning, danger and info should look equally loud, which is much easier to tune in OKLCH.
  • Keep the list short. If two tokens always change together and never differ, merge them.

Common pitfalls

The most common failure is leakage: one component uses bg-white because it was quicker, and dark mode breaks in that one spot. Enforce the rule in review, or with a lint rule that rejects palette classes in component code. Another is naming by appearance, such as --light-bg, which becomes false in dark mode. Finally, avoid baking one theme's assumptions into a name; --text-gray means little on a dark surface. When a component truly needs a one-off value, derive it from a token, for example color-mix(in oklch, var(--accent) 22%, transparent), instead of adding a raw color.

How MiniDev UI uses them

MiniDev UI uses semantic tokens only. Components contain no hex values and no palette steps, and dark mode and the materials redefine tokens, never components. Text roles carry contrast targets from the design spec: fg-muted at 7:1 or better and fg-subtle at 4.5:1 or better. The kit also maps its roles onto shadcn's names (--primary, --muted-foreground, --destructive, --ring), so shadcn components you already have follow the same theme. Status badge and Inline alert show the status roles in use.

Status BadgeuiTailwind status badge with a tinted fill, hairline border and colored dot in neutral, success, warning, danger, accent and info tones that read as one set.npx shadcn@latest add ui.minidev.pro/r/status-badge.json