Customize
Theming
Every color, radius, shadow and curve is a CSS variable in one file. Change a token and every component follows, in light and dark.
How tokens work
templates/vibeui.css declares each token twice: once for light (on :root and .light) and once for dark (on .dark). A @theme inline block then registers them with Tailwind, so they become normal utilities: bg-surface, text-muted, border-line, ring-ring, shadow-lift.
:root,
.light { --surface: #ffffff; }
.dark { --surface: #181715; }
@theme inline { --color-surface: var(--surface); }
/* → bg-surface, text-surface, border-surface, … */Because the utilities resolve to var(--surface) at runtime, you can override a token on any element and its subtree changes. That is how the landing page's playground retints live components, and how you can scope a dark panel inside a light page.
Color tokens
These are read straight from the token file, so the table is always current.
| Token | Light | Dark |
|---|---|---|
| --canvas | #f8f7f4 | #0f0e0d |
| --surface | #ffffff | #181715 |
| --surface-muted | #f2f0ec | #201e1c |
| --surface-sunken | #eceae4 | #121110 |
| --ink | #1b1a17 | #f4f2ee |
| --ink-soft | #44423d | #d6d3cc |
| --muted | #77746d | #9a968d |
| --faint | #a6a39b | #67645d |
| --line | rgb(27 26 23 / 0.09) | rgb(244 242 238 / 0.09) |
| --line-strong | rgb(27 26 23 / 0.17) | rgb(244 242 238 / 0.18) |
| --overlay | rgb(27 26 23 / 0.42) | rgb(0 0 0 / 0.6) |
| --accent | #1b1a17 | #f4f2ee |
| --accent-ink | #ffffff | #14130f |
| --accent-soft | rgb(27 26 23 / 0.08) | rgb(244 242 238 / 0.1) |
| --ring | rgb(27 26 23 / 0.22) | rgb(244 242 238 / 0.26) |
| --pastel-rose | #fdecec | #3a2323 |
| --pastel-rose-ink | #a02e2c | #f2b5b3 |
| --pastel-sky | #e7f1fb | #1c2e3b |
| --pastel-sky-ink | #1c5f93 | #9ecbea |
| --pastel-sage | #e9f3ea | #223127 |
| --pastel-sage-ink | #2c6a33 | #a9ceae |
| --pastel-sand | #f0eeea | #362f1d |
| --pastel-sand-ink | #1b1a17 | #e8c978 |
Reading the palette
| Role | Tokens | Used for |
|---|---|---|
| Surfaces | canvas, surface, surface-muted, surface-sunken | Page, cards and inputs, hovers and wells, tracks and code. |
| Text | ink, ink-soft, muted, faint | Primary, secondary, supporting, placeholder. |
| Lines | line, line-strong | Hairlines and stronger borders. |
| Accent | accent, accent-ink, accent-soft, ring | Focus, selection, progress, live state. |
| Status | pastel-{rose,sky,sage,sand} and -ink | Alerts, pills, status. Retinted for dark. |
Retint the accent
Both themes are monochrome by default: light mode's accent is the ink itself, dark mode's accent is the light ink. Override both after the token import. Keep --accent-ink readable on top of --accent: it is the text color for accent buttons, checked boxes and selected days.
:root {
--accent: #2563eb;
--accent-ink: #ffffff;
--accent-soft: rgb(37 99 235 / 0.1);
--ring: rgb(37 99 235 / 0.28);
}
.dark {
--accent: #7aa7ff;
--accent-ink: #06142e;
--accent-soft: rgb(122 167 255 / 0.14);
--ring: rgb(122 167 255 / 0.3);
}Radii
| Token | Default | Utility | Used by |
|---|---|---|---|
| --radius-sm | 8px | rounded-sm | Inputs, buttons, small controls |
| --radius-md | 12px | rounded-md | Popovers, menus, alerts, large buttons |
| --radius-lg | 16px | rounded-lg | Cards, tables, code blocks |
| --radius-xl | 22px | rounded-xl | Dialogs, big surfaces |
Want sharper corners? Set the four values smaller (for example 4, 6, 8, 12). Want pills? Raise them. The interactive version of this is on the home page.
:root {
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 12px;
}Shadows
Depth is a hairline plus a soft layered shadow, never a heavy drop shadow. Four tokens cover it.
| Utility | Use |
|---|---|
| shadow-quiet | Resting cards, inputs and controls. |
| shadow-lift | Hovered cards, menus, popovers. |
| shadow-pop | Dialogs, sheets and the command palette. |
| shadow-inset | A faint top highlight on filled buttons. |
Show the shadow values
--shadow-quiet
light: 0 1px 1px rgb(27 26 23 / 0.04), 0 1px 3px rgb(27 26 23 / 0.05)
dark: 0 1px 1px rgb(0 0 0 / 0.3), 0 1px 3px rgb(0 0 0 / 0.3)
--shadow-lift
light: 0 1px 2px rgb(27 26 23 / 0.04), 0 8px 20px -6px rgb(27 26 23 / 0.1), 0 20px 40px -16px rgb(27 26 23 / 0.1)
dark: 0 1px 2px rgb(0 0 0 / 0.3), 0 10px 24px -6px rgb(0 0 0 / 0.45), 0 24px 48px -16px rgb(0 0 0 / 0.4)
--shadow-pop
light: 0 1px 2px rgb(27 26 23 / 0.06), 0 12px 32px -8px rgb(27 26 23 / 0.18), 0 32px 64px -24px rgb(27 26 23 / 0.22)
dark: 0 1px 2px rgb(0 0 0 / 0.4), 0 16px 40px -8px rgb(0 0 0 / 0.6), 0 40px 80px -24px rgb(0 0 0 / 0.55)
--shadow-inset
light: inset 0 1px 0 rgb(255 255 255 / 0.14)
dark: inset 0 1px 0 rgb(255 255 255 / 0.06)
Typography
| Token | Utility | Role |
|---|---|---|
| --font-sans | font-sans | Interface text (Geist) |
| --font-mono | font-mono | Code and numerals (Geist Mono) |
| --font-display | font-display | Headlines and brand moments (Instrument Serif) |
Each falls back to a system stack when its variable is not set, so nothing breaks without fonts. To use your own typefaces, point the variables at them:
:root {
--font-geist-sans: "Inter";
--font-instrument: "Fraunces";
}Add your own tokens
Add the variable for both themes, then register it so Tailwind generates a utility:
:root { --success-surface: #e9f3ea; }
.dark { --success-surface: #223127; }
@theme inline {
--color-success-surface: var(--success-surface);
}
/* now: bg-success-surface */Scoping a theme
Put class="dark" or class="light" on any element and everything inside renders in that palette, regardless of the page. Useful for a dark hero on a light page, an embedded preview, or a theme switcher that shows both at once.
<section className="dark rounded-xl bg-canvas p-8 text-ink">
<Button>Always dark</Button>
</section>