RoadieRoadie
  • Home
  • Brand
  • Foundations
  • Content
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeBrandFoundations
Appearance

Foundations

  • Visual

    • Colors
    • Elevation
    • Iconography
    • Shape
    • Theming
  • Content

    • Date and time
    • Layout
    • Prose styles
    • Typography
  • Behaviour

    • Accessibility
    • Interactions
    • Motion
    • View transitions
  • Building apps

    • Forms
    • Linking
    • Navigation
    • Performance
    • Records modelAlpha
    • Tables

Colors

Docs updated
20 Oct 2026

ReferenceColor scales tokens·Intents tokens·Data visualisation tokens·Emphasis and states tokens

Roadie uses OKLCH colour scales with 14 steps, 0 to 13. Colours are applied through three utility types, bg-*, text-*, and border-*, and each is scoped to its own CSS property.

Colour scales

Each scale has 14 OKLCH steps. Step 0 is the lightest extreme and step 13 the darkest. Dark mode swaps the values, and the step numbers stay the same.

On the docs site, each scale is a row of swatches in the current theme, with each step's number. Color scales lists every step's value in light and dark.

  • Neutral

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Brand

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Brand secondary

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Accent

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Danger

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Success

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Warning

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13
  • Info

    1. 0
    2. 1
    3. 2
    4. 3
    5. 4
    6. 5
    7. 6
    8. 7
    9. 8
    10. 9
    11. 10
    12. 11
    13. 12
    14. 13

Colour utilities

Three utility namespaces apply colour. Each maps a semantic level to a scale step of the current intent.

Levelbg-*text-*border-*
subtlerStep 9 at 6% alpha (dark: 6 to 10%, by intent)Step 10Step 5
subtleStep 9 at 11% alpha (dark: 11 to 18%, by intent)Step 11Step 6 (dark neutral: 7)
normalStep 1 (dark: 2)Step 12Step 7 (dark neutral: 8)
strongStep 9 (neutral: 13, danger: 10; success and brand-secondary mix 10% toward white)Step 13Step 9 (dark neutral: 10)
invertedStep 12Step 0, the page's text flipped. It pairs with bg-inverted.Step 12
on-strongNoneWhite or step 13, chosen per intent. It's the label colour for strong fills.None
raisedStep 1 (dark: 4 for neutral, 2 for the other intents)NoneNone
sunkenStep 2 (dark: 0)NoneNone
markLight step 5, kept in dark mode (neutral: step 13)Light step 13, kept in dark mode (neutral: step 0)None

For example, bg-normal, bg-subtle, bg-raised, text-subtle, text-strong, border-subtle, border-b-subtler, and divide-subtler.

Surface tokens

Four depths 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, and floating for popovers and modals.

<div className='grid gap-4 sm:grid-cols-2 lg:grid-cols-4'>
<Card className='emphasis-sunken'>
<Card.Content>
<Card.Title>Sunken</Card.Title>
<Card.Description>bg-sunken</Card.Description>
</Card.Content>
SurfaceUse for
Sunkenbg-sunken, for recessed areas, inset panels, and empty-state wells. emphasis-sunken adds the inset shadow.
Normalbg-normal, the default page background and the baseline everything else sits on. Use it when nothing should feel elevated or recessed.
Raisedbg-raised, for elevated cards, sticky headers, and grouped content that should lift off the page. emphasis-raised adds the rim light and shadow-md.
Floatingemphasis-floating, for popovers, modals, dropdowns, and anything else that floats over the document flow. It uses the raised background, lifted in dark mode, shadow-xl, and the strong rim light. Add is-translucent to let content show through as it scrolls beneath.

The four levels go from sunken to normal, raised, and floating. Reach for the emphasis-* shortcut when you need the background, text, and shadow together, and use bg-raised or bg-sunken directly when you only need the background colour.

Intent and emphasis

Set intent-* on a container to choose the palette. Use the emphasis-* shortcuts for background, text, and border together, with hover and press states once you add is-interactive, or the individual utilities when you need to compose them.

On the docs site, each intent shows the emphasis-strong, emphasis-normal, emphasis-subtle, and emphasis-subtler presets.

  • intent-neutral
    • strong
    • normal
    • subtle
    • subtler
  • intent-brand
    • strong
    • normal
    • subtle
    • subtler
  • intent-brand-secondary
    • strong
    • normal
    • subtle
    • subtler
  • intent-brand-purple
    • strong
    • normal
    • subtle
    • subtler
  • intent-brand-blue
    • strong
    • normal
    • subtle
    • subtler
  • intent-brand-orange
    • strong
    • normal
    • subtle
    • subtler
  • intent-accent
    • strong
    • normal
    • subtle
    • subtler
  • intent-danger
    • strong
    • normal
    • subtle
    • subtler
  • intent-success
    • strong
    • normal
    • subtle
    • subtler
  • intent-warning
    • strong
    • normal
    • subtle
    • subtler
  • intent-info
    • strong
    • normal
    • subtle
    • subtler

Brand intents

Brand intents are named by hue. brand-blue is the same as brand, and brand-orange is the same as brand-secondary. brand-purple uses the purple scale that info also uses. Green, yellow, and red stay status colours, with no brand intent.

The scales have hue names too, as variables only. --color-brand-blue-{n} is --color-brand-{n}, --color-brand-purple-{n} is --color-info-{n}, and --color-brand-orange-{n} is --color-brand-secondary-{n}. Steps 0 to 13 follow dark mode with the scale they name, and the fixed light steps, such as --color-brand-orange-light-9, stay light.

Contrast

Roadie measures all contrast with APCA, the contrast method in the WCAG 3 draft, and doesn't use WCAG 2 ratios. APCA scores a pair as Lc, its lightness contrast, and weighs text size and weight in a way ratios don't.

ContentMinimum
Body textLc 75
Labels on strong fills and large textLc 60
Non-text UI and large display textLc 45

Text on strong fills

text-on-strong is the label colour for strong fills, chosen per intent: white or dark text, whichever reads better on the fill. emphasis-strong uses it. Labels need Lc 60 or more at rest, on hover, and when pressed. text-inverted is different: it's the page's text colour flipped, and pairs with bg-inverted.

White on Spotlight blue scores Lc 66, so blue fills keep white labels. Danger, success, and brand-secondary move their fill slightly to reach Lc 60.

A custom accent keeps white text too. ThemeProvider caps its chroma to what sRGB can show at step 9, because a browser clips a more saturated fill and the clip makes it lighter.

IntentLightDarkAPCA Lc, light
neutralWhiteStep 0107
brandWhiteWhite66
brand-secondaryStep 13Step 061
brand-blueWhiteWhite66
brand-orangeStep 13Step 061
brand-purpleWhiteWhite68
accentWhiteWhite66
dangerWhiteWhite64
successStep 13Step 062
warningStep 13Step 062
infoWhiteWhite68

Data visualisation colours

Charts use their own palette, checked for colour-blind separation in CI. Each set has one job. Read Data visualisation before you pick one. On the docs site, each set shows its swatches on a light and a dark chart surface.

Categorical

For telling series apart.

Light

1

2

3

4

5

6

7

8

Dark

1

2

3

4

5

6

7

8

Sequential

For how much.

Light

0

1

2

3

4

5

6

7

8

Dark

0

1

2

3

4

5

6

7

8

Diverging

For ahead of or behind a benchmark.

Light

pos-4

pos-3

pos-2

pos-1

0

neg-1

neg-2

neg-3

neg-4

Dark

pos-4

pos-3

pos-2

pos-1

0

neg-1

neg-2

neg-3

neg-4

Status

For meaning only.

Light

good

warning

serious

critical

Dark

good

warning

serious

critical

Dark mode

Dark mode is automatic. The .dark class on <html> swaps every OKLCH value, so no dark: Tailwind variants are needed. bg-normal, text-subtle, and border-subtle all adapt by themselves.

Dark steps sit closer together and shadows barely show on dark, so dark mode leans on fills and edges instead:

  • Neutral border-subtle, border-normal, and border-strong take one step higher, to keep their contrast against dark surfaces.
  • Subtle and subtler tints mix more of step 9, so a subtle surface stands about as far off the dark page in every intent: 18% for neutral, 15% for brand and accent, 13% for danger and info, 12% for success, and 11% for warning and brand-secondary. The hover and pressed tints step up with them.
  • Raised neutral surfaces use step 4. Floating surfaces, and a raised surface inside another, lift 20% further toward step 9 (--intent-bg-floating), and a raised surface inside a floating one lifts 20% again, so each layer stands off what's under it.
  • On raised and floating surfaces, text-subtle moves 40% toward step 12, so it keeps Lc 75 on the lighter fills. An intent set inside one moves too.
  • An emphasis-subtle track with an emphasis-raised pill in it, as in Tabs, takes the sunken fill, since a tint lifts it toward the pill on a dark page. The pill then stands off it on any surface.
  • emphasis-field stays sunken, as in light mode, with its edge at 50% of step 9 instead of 22%, so the field's outline shows on the page.

The CSS also sets color-scheme: light on :root and color-scheme: dark on .dark, so native browser UI, such as scrollbars and form controls, matches your theme.

Fixed light colours

Brand artwork such as patterns, illustrations, and print keeps its light colours in dark mode. Use --color-{scale}-light-{0|5|9|12|13} for these. .dark doesn't override them, and only neutral has step 0.

Setup

Add a blocking script in <head> to prevent a flash of the wrong theme. The getThemeScript helper generates it for you. Import it from @oztix/roadie-core/theme, which has 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'
import { Button } from '@oztix/roadie-components/button'
// In your layout:
<ThemeProvider followSystem>
<App />

Once a user explicitly toggles, their choice persists in localStorage and overrides the system preference.

Vue and vanilla JavaScript

For apps without React, use getThemeScript() for the 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. ThemeProvider sets --accent-hue and --accent-chroma, and CSS oklch() computes every accent and neutral colour in the browser. Modern browsers need no JavaScript colour generation.

Previous page← OverviewNext pageElevation →