Theming
REALM UI reads its colours, radius, type, spacing and shadows from CSS custom properties in theme.css. Change a token and every component that uses it follows.
Theme builder
Pick a preset, then change any colour for light and dark mode. The preview uses the real components. The Contrast tab checks each text colour against the surface it sits on, and the CSS tab holds the stylesheet to copy. Your theme is saved in this browser.
Using a theme
Load your theme after theme.css, so your values replace the defaults:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<link rel="stylesheet" href="/my-theme.css">You only need the tokens you change. To make the corners square and the brand colour blue:
:root {
--radius: 0;
--primary: oklch(0.52 0.19 258);
--ring: oklch(0.6 0.17 255);
}Tokens can be set on any element, not only :root. Everything inside it uses the new values:
.billing {
--primary: oklch(0.5 0.13 152);
}A few tokens are calculated from others where theme.css declares them, on :root. --radius-sm to --radius-2xl come from --radius, and --focus-ring comes from --ring. When you change --radius or --ring on an element instead of :root, declare those again on the same element:
.compact {
--radius: 0.25rem;
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
}Dark mode
Dark values apply when the system is set to dark. data-theme="dark" on <html> forces dark mode and data-theme="light" forces light mode. A theme sets the dark values in both places:
:root[data-theme="dark"] {
--primary: oklch(0.7 0.15 250);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--primary: oklch(0.7 0.15 250);
}
}The theme builder writes both blocks for you.
Colour tokens
Each surface colour has a -foreground partner for the text and icons on it. Keep each pair at a contrast of at least 4.5:1.
| Token | Used for |
|---|---|
--background, --foreground |
The page and its text |
--card, --card-foreground |
Cards |
--popover, --popover-foreground |
Popovers, menus, tooltips and dialogs |
--primary, --primary-foreground |
Default buttons, checked controls, links and the brand colour |
--secondary, --secondary-foreground |
Secondary buttons and badges |
--muted, --muted-foreground |
Quiet backgrounds, descriptions and placeholder text |
--accent, --accent-foreground |
Hovered and highlighted items in lists and menus |
--destructive, --destructive-foreground |
Delete buttons, errors and invalid fields |
--border |
Borders and separators |
--input |
Borders of text fields, checkboxes and other controls |
--ring |
The focus ring |
--chart-1 to --chart-5 |
Chart series |
--sidebar, --sidebar-* |
The sidebar, with its own foreground, primary, accent, border and ring |
Other tokens
| Token | Default | Used for |
|---|---|---|
--radius |
0.625rem |
Corner radius. --radius-xs to --radius-2xl are based on it, and --radius-full makes pills |
--spacing |
0.25rem |
The spacing unit. Components use multiples of it, such as calc(var(--spacing) * 4) |
--font-sans, --font-mono |
System fonts | Text and code |
--text-xs to --text-4xl |
0.75rem to 2.25rem |
Font sizes |
--font-weight-normal to --font-weight-bold |
400 to 700 |
Font weights |
--shadow-xs to --shadow-lg |
Shadows | |
--default-transition-duration |
150ms |
Hover and state transitions |
To use the tokens in your own styles, read them with var():
.note {
padding: calc(var(--spacing) * 4);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--muted);
color: var(--muted-foreground);
}