Theming
One-token rethemes.
SupaUI separates colour, type, and depth into three layers. Components only ever read the top two — primitives stay untouched so consumers can re-skin without rewriting CSS.
The three layers
- Primitives. Raw axes:
--supa-color-blue-500,--supa-size-md,--supa-leading-snug. Components never read these directly. - Semantic roles.
--supa-color-accent,--supa-color-paper,--supa-color-rule. This is what every component actually consumes. - shadcn aliases.
--background,--foreground,--primary. Mapped onto our semantic roles, so shadcn-style utilities (bg-background) keep working out of the box.
Re-skin in three lines
Override the role layer on .supa-root (or :root) and every component that reads accent flips.
css
.supa-root {
--supa-color-accent: var(--supa-color-purple-500);
--supa-color-accent-strong: var(--supa-color-purple-600);
--supa-color-accent-ink: white;
}Dark mode
Toggle data-theme="dark" or the .dark class anywhere in the tree. We rebind the role layer only — palette ramps stay shared.
tsx
<html className={isDark ? "dark" : undefined}>...Type voices
Three font roles + one pixel face. Wired to next/font + a self-hosted Departure Mono.
--supa-font-display→ Gloock — landing headlines, hero pages.--supa-font-serif→ Source Serif 4 — long-form prose.--supa-font-sans→ Inter — body, UI text, forms.--supa-font-pixel→ Departure Mono — buttons, badges, pixel labels, code.