Theming
Components are styled with semantic color tokens, not fixed colors. Change the tokens and every component follows, in light and dark.
Approach
- The token names are the standard shadcn set. A project that already has a shadcn theme needs no changes.
- Neutrals carry a slight warm tint, like paper and ink, so surfaces do not look blue-grey next to photographs.
- There is one accent, a vermilion. It is used for the focus ring and the first data series (
ringandchart-1) and almost nowhere else. - Depth comes from hairline borders first. Shadows are reserved for surfaces that float above the page.
- Corner radii derive from a single
--radiusvalue of 0.5rem.
Tokens
| Token | Light | Dark | Used for |
|---|---|---|---|
| --background | oklch(0.985 0.003 85) | oklch(0.135 0.003 70) | Page background |
| --foreground | oklch(0.21 0.006 70) | oklch(0.95 0.004 85) | Default text |
| --card | oklch(1 0 0) | oklch(0.165 0.003 70) | Raised surfaces such as cards and code blocks |
| --card-foreground | oklch(0.21 0.006 70) | oklch(0.95 0.004 85) | Text on cards |
| --popover | oklch(1 0 0) | oklch(0.185 0.003 70) | Floating surfaces: menus, dialogs, toasts |
| --popover-foreground | oklch(0.21 0.006 70) | oklch(0.95 0.004 85) | Text on floating surfaces |
| --primary | oklch(0.23 0.006 70) | oklch(0.95 0.004 85) | Primary buttons, selected states, tooltips |
| --primary-foreground | oklch(0.985 0.003 85) | oklch(0.17 0.005 70) | Text on primary |
| --secondary | oklch(0.955 0.004 85) | oklch(0.22 0.003 70) | Secondary buttons and neutral badges |
| --secondary-foreground | oklch(0.23 0.006 70) | oklch(0.95 0.004 85) | Text on secondary |
| --muted | oklch(0.96 0.004 85) | oklch(0.21 0.003 70) | Tracks, skeletons and quiet fills |
| --muted-foreground | oklch(0.47 0.008 70) | oklch(0.72 0.006 80) | Secondary text |
| --accent | oklch(0.945 0.005 85) | oklch(0.24 0.003 70) | Hover and highlighted rows |
| --accent-foreground | oklch(0.23 0.006 70) | oklch(0.95 0.004 85) | Text on accent |
| --destructive | oklch(0.53 0.2 27) | oklch(0.7 0.18 25) | Errors and destructive actions |
| --border | oklch(0.905 0.005 85) | oklch(0.26 0.003 70) | Hairline borders and dividers |
| --input | oklch(0.85 0.006 85) | oklch(0.34 0.003 70) | Borders of form controls, switch tracks |
| --ring | oklch(0.6 0.2 35) | oklch(0.7 0.17 38) | Focus rings |
| --chart-1 | oklch(0.6 0.2 35) | oklch(0.7 0.17 38) | First data series, the accent |
| --chart-2 | oklch(0.48 0.09 250) | oklch(0.7 0.1 250) | Second data series |
| --chart-3 | oklch(0.7 0.13 85) | oklch(0.8 0.12 85) | Third data series |
| --chart-4 | oklch(0.56 0.08 150) | oklch(0.72 0.1 150) | Fourth data series |
| --chart-5 | oklch(0.52 0.06 330) | oklch(0.72 0.07 330) | Fifth data series |
Changing the accent
Override the two accent tokens in your global stylesheet. Check the focus ring against both backgrounds: it needs a 3:1 contrast ratio to be visible.
:root {
--ring: oklch(0.55 0.17 255);
--chart-1: oklch(0.55 0.17 255);
}
.dark {
--ring: oklch(0.72 0.14 255);
--chart-1: oklch(0.72 0.14 255);
}Dark mode
Dark values apply under a .dark class on any ancestor, usually html. Use a library such as next-themes to set it from the system preference. Dark mode is not an inversion: surfaces get lighter as they rise, and the accent is lightened so it keeps its contrast.
@custom-variant dark (&:is(.dark *));Radius and type
Set --radius to change every corner at once. Components inherit the font from the page and use font-mono only for keyboard hints and code. They use tabular figures wherever numbers change in place.
:root {
--radius: 0.375rem;
}