Colors
Roadie uses OKLCH color scales with 14 steps (0-13) per intent. Colors are applied through three utility types: bg-*, text-*, and border-* — each scoped to its own CSS property.
Color scales
Each intent has a 14-step OKLCH scale. Step 0 is the lightest extreme, step 13 the darkest. Dark mode swaps the values — step numbers stay the same.
neutral
0
1
2
3
4
5
6
7
8
9
10
11
12
13
brand
0
1
2
3
4
5
6
7
8
9
10
11
12
13
brand-secondary
0
1
2
3
4
5
6
7
8
9
10
11
12
13
accent
0
1
2
3
4
5
6
7
8
9
10
11
12
13
danger
0
1
2
3
4
5
6
7
8
9
10
11
12
13
success
0
1
2
3
4
5
6
7
8
9
10
11
12
13
warning
0
1
2
3
4
5
6
7
8
9
10
11
12
13
info
0
1
2
3
4
5
6
7
8
9
10
11
12
13
Color utilities
Three utility namespaces for applying colors. Each maps semantic levels to specific scale steps via the current intent.
| Level | bg-* | text-* | border-* |
|---|---|---|---|
| subtler | step 9 @ 6% alpha | step 10 | step 5 |
| subtle | step 9 @ 11% alpha | step 11 | step 6 |
| normal | step 1 | step 12 | step 7 |
| strong | step 9 (neutral: 12) | step 13 | step 9 |
| inverted | step 12 | step 0 | step 12 |
| raised | step 1 (dark: 2-3) | — | — |
| sunken | step 2 (dark: 0) | — | — |
Usage
bg-normalbg-subtlebg-raisedtext-subtletext-strongborder-subtleborder-b-subtlerdivide-subtlerSurface tokens
Four elevation levels in one picture. Pick the surface that matches the element's depth in the layout — sunken for recessed areas, normal for the page, raised for elevated cards, floating for popovers and modals.
Sunken
bg-sunken
Recessed areas, inset panels, empty-state wells. Pairs with emphasis-sunken for the full inset shadow treatment.
Normal
bg-normal
Default page background. The baseline everything else sits on — use it when nothing should feel elevated or recessed.
Raised
bg-raised
Elevated cards, sticky headers, grouped content that should appear to lift off the page. Pairs with emphasis-raised for rim-light + shadow-md.
Floating
emphasis-floating
Popovers, modals, dropdowns — anything that floats above the document flow. Uses the top elevation shadow and strong rim light.
The four levels map to a depth hierarchy: sunken → normal → raised → floating. Reach for the emphasis-* shortcut when you need the full bg + border + shadow treatment; use bg-raised / bg-sunken directly when you just need the background colour.
Intent + emphasis
Set intent-* on a container to choose the color palette. Use emphasis-* shortcuts for combined bg + text + interactive states, or individual utilities for composability.
neutral
brand
brand-secondary
accent
danger
success
warning
info
Dark mode
Automatic. The .dark class on <html> swaps all OKLCH values. No dark: Tailwind variants needed — bg-normal, text-subtle, border-subtle all adapt automatically.
The CSS also sets color-scheme: light on :root and color-scheme: dark on .dark, so native browser UI (scrollbars, form controls) matches your theme.
Setup
Add a blocking script in <head> to prevent flash of wrong theme. The getThemeScript helper generates this for you — import from @oztix/roadie-core/theme (no React dependency).
import { getThemeScript } from '@oztix/roadie-core/theme'
// In your <head>:
<meta name="color-scheme" content="light" />
<script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />Following system preference
By default, the theme is light. Pass followSystem: true to respect the user's OS preference when they haven't explicitly toggled.
getThemeScript({ followSystem: true })React: ThemeProvider
For React apps, wrap your app in ThemeProvider to get the useTheme() hook with isDark and setDark.
import { ThemeProvider, useTheme } from '@oztix/roadie-components'
// In your layout:
<ThemeProvider followSystem>
<App />
</ThemeProvider>
// In a toggle component:
function ThemeToggle() {
const { isDark, setDark } = useTheme()
return <button onClick={() => setDark(!isDark)}>{isDark ? 'Light' : 'Dark'}</button>
}Once a user explicitly toggles, their choice persists in localStorage and overrides the system preference.
Vue / vanilla JS
For non-React apps, use getThemeScript() for initial load and toggle with plain DOM calls.
function setDark(dark) {
document.documentElement.classList.toggle('dark', dark)
document.documentElement.style.colorScheme = dark ? 'dark' : 'light'
localStorage.setItem('theme', dark ? 'dark' : 'light')
}Dynamic accent
The accent scale is CSS-native. The ThemeProvider sets --accent-hue and --accent-chroma — CSS oklch() computes all accent and neutral colors in the browser. No JavaScript color generation needed for modern browsers.