Color
Color in ui-next is two tiers. Primitives are the raw palette — a fixed ramp of values you can reach for directly. Semantic roles sit on top: tokens named for a job (a fill, a border, body text) that point at different primitive steps in light versus dark. You style against the role and the palette handles the mode for you.
Everything is authored in OKLCH (oklch(lightness chroma hue)) in a single file — web/ui-next/src/theme/colors.css. Every swatch on this page links into colorpicker.dev — its hash carries the exact L, C, H, so you can open a value, nudge it, and copy it straight back. Dark mode is the .dark class: every semantic token is declared once under :root and again under .dark, and Tailwind v4 re-exports them as utilities (bg-primary, text-accent-foreground, …) via @theme inline.
The accent ramp
The accent is VESYL’s brand blue expanded into a full 25–950 ramp. Lightness walks a smooth perceptual curve; chroma peaks through the mid steps and tapers at the ends so the lightest and darkest steps stay clean rather than washed-out or neon. Each step is available as a utility — bg-accent-500, text-accent-700, border-accent-200, and so on.
The ramp is fixed — identical in light and dark. That makes it the right tool when you decide the value for a context: chart series, gradients, illustrations, a decorative tint. It is the wrong tool for anything that must adapt between modes, because accent-50 as a surface blinds you on a dark canvas and accent-950 as text vanishes on it. For mode-adaptive UI, reach for a role.
Semantic accent roles
Each role names a job and resolves to a different ramp step per mode — so the same class reads correctly in both. The panels below are pinned to light and dark side by side; toggle the docs theme to confirm the live classes track it.
| Role | Use for | Step · light / dark |
|---|---|---|
| accent / accent-foreground | Solid fill + its text — the primary accent button | 600 · 600 |
| accent-emphasis | Hover / active of the solid fill | 700 · 500 |
| accent-subtle / -foreground | Tinted surface + its text — badges, callouts | 50/700 · 950/200 |
| accent-muted | Borders, dividers, faint outlines | 200 · 800 |
| accent-text | Accent-colored text & links on the page | 700 · 300 |
Composed together, the roles cover the common accent surfaces without any per-mode branching at the call site:
Step or role?
- Reach for a role (
bg-accent,bg-accent-subtle,text-accent-text,border-accent-muted) for anything users interact with or read. It is the default, and it stays correct when the theme flips. - Reach for a raw step (
bg-accent-500) only when the value is yours to own per mode — data visualization, decoration, one-off art direction. If you find yourself writingdark:bg-accent-300to fix a raw step, that is the signal a role already exists for what you are doing.
The semantic system
The accent is one family inside a larger neutral-and-semantic system. These tokens predate the accent ramp and drive nearly all chrome — surfaces, text, borders, status. Surface tokens are paired with a -foreground for text that sits on them; the swatches show that pairing as “Aa”.
A few notes on the set:
background/card/popoverare the surface stack;primaryandsecondaryare the high- and low-emphasis solid fills.muted,subtle, andemphasizedare graded neutral surfaces for receding or lifting content.highlightis the interactive hover/focus/selected surface used by menus, selects, and list items. (It was previously namedaccent— that name now belongs to the brand accent above.)border,border-strong,input, andringhandle outlines and focus rings.
Chart colors
chart-1 through chart-5 are a dedicated categorical sequence for data visualization. They are intentionally fixed across modes so a series keeps its identity when the theme changes.
Working in OKLCH
OKLCH makes the dark-mode mapping tractable: lightness is perceptual, so flipping a token between modes is mostly a matter of moving along the lightness axis while holding hue and chroma. colorpicker.dev is a solid OKLCH picker for dialing this in — and since every swatch above deep-links to it, you can start from an existing step rather than a blank canvas. When adding or adjusting a token, edit only web/ui-next/src/theme/colors.css — declare the value under both :root and .dark, then add the matching --color-* line to the @theme inline block so the Tailwind utility exists.