Theming
The three token layers and how to rebrand the whole system with one file.
Plinth's entire look lives in one stylesheet:
plinth.css. It's organized in three layers, and the layer
boundaries are the theming contract.
Layer 1: Primitives
Raw scales with no opinion about usage: two 12-step color ramps (--gray-*,
--accent-*), status color sets, radius steps, shadows, motion durations and
easings, and the type scale.
The ramps follow the 12-step convention (popularized by Radix Colors), where the step number tells you what the color is for:
| Steps | Use |
|---|---|
| 1–2 | App and subtle backgrounds |
| 3–5 | Component fills: rest, hover, active |
| 6–8 | Borders: subtle, default, strong |
| 9–10 | Solid fills: rest, hover |
| 11–12 | Text: secondary, primary |
Dark mode is a re-declaration of the same primitive variables under the .dark
class. Because everything above resolves through them, the whole system flips
with no component-level dark styles.
Layer 2: Semantic
Role-based aliases that components actually use: --background, --surface,
--muted-foreground, --border-strong, --primary, --ring, and so on. Each
resolves to a primitive. This is the vocabulary of the system; see them rendered
on the Colors page.
Layer 3: Component
Per-component hooks (like --button-height-sm) that exist only where a
component genuinely needs an override point. These live next to each component,
not in the global sheet.
Making your own theme
Override Layer 1 in a stylesheet loaded after plinth.css:
/* acme-theme.css */
:root {
--accent-9: oklch(0.55 0.19 145); /* brand green */
--accent-10: oklch(0.5 0.19 145);
--radius-md: 0.75rem; /* rounder personality */
}Rules of thumb:
- Prefer overriding primitives (ramps, radius, shadows). Semantic roles keep working automatically.
- Override semantic tokens only to change relationships, e.g. making
--primaryyour brand color instead of high-contrast neutral. - Never hardcode values in components. If you need a new knob, add a Layer 3 token.
Fonts
Plinth doesn't ship a font. It reads two hook variables with system fallbacks:
:root {
--plinth-font-sans: "Inter", sans-serif;
--plinth-font-mono: "JetBrains Mono", monospace;
}Set them to whatever you load (next/font, Fontsource, self-hosted).