Skip to content
TrademarkTrademark
Docs
Basics

Color

Updated: October 10, 2026

Once UI's color system is designed to be accessible and consistent, allowing you to create harmonious, functional interfaces. It consists of two layers: the base layer that contains all possible color tokens and the semantic layer that aliases the base tokens to give them meaningful names.

View token source on GitHub

Base tokens

In Once UI, we call the different color spaces schemes. Color tokens are constructed from pairing a scheme with a weight, such as sand-700. Base tokens serve as the foundational layer of the color system and they are not meant to be used directly.

Schemes

Schemes provide a comprehensive and consistent initial color palette. Each scheme has 12 weights (100–1200), giving you fine-grained control over contrast and tone:

100
200
300
400
500
600
700
800
900
1000
1100
1200
Sand
Gray
Slate
Mint
Rose
Dusk
Red
Orange
Yellow
Moss
Green
Emerald
Aqua
Cyan
Blue
Indigo
Violet
Magenta
Pink
Type
Default
scheme-sand-{weight}
CSS color100 - 1200
scheme-gray-{weight}
CSS color100 - 1200
scheme-slate-{weight}
CSS color100 - 1200
scheme-mint-{weight}
CSS color100 - 1200
scheme-rose-{weight}
CSS color100 - 1200
scheme-dusk-{weight}
CSS color100 - 1200
scheme-red-{weight}
CSS color100 - 1200
scheme-orange-{weight}
CSS color100 - 1200
scheme-yellow-{weight}
CSS color100 - 1200
scheme-moss-{weight}
CSS color100 - 1200
scheme-green-{weight}
CSS color100 - 1200
scheme-emerald-{weight}
CSS color100 - 1200
scheme-aqua-{weight}
CSS color100 - 1200
scheme-cyan-{weight}
CSS color100 - 1200
scheme-blue-{weight}
CSS color100 - 1200
scheme-indigo-{weight}
CSS color100 - 1200
scheme-violet-{weight}
CSS color100 - 1200
scheme-magenta-{weight}
CSS color100 - 1200
scheme-pink-{weight}
CSS color100 - 1200

Semantic tokens

Semantic tokens are contextual - they adapt based on your theme configuration. Each of the 7 semantic schemes provides tokens for backgrounds, text, solids, borders, and overlays across the strong, medium, and weak weight variants.

1. Neutral

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
neutral-background
strongmediumweak
—
neutral-on-background
strongmediumweak
—
neutral-solid
strongmediumweak
—
neutral-on-solid
strongmediumweak
—
neutral-border
strongmediumweak
—
neutral-alpha
strongmediumweak
—

2. Brand

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
brand-background
strongmediumweak
—
brand-on-background
strongmediumweak
—
brand-solid
strongmediumweak
—
brand-on-solid
strongmediumweak
—
brand-border
strongmediumweak
—
brand-alpha
strongmediumweak
—

3. Accent

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
accent-background
strongmediumweak
—
accent-on-background
strongmediumweak
—
accent-solid
strongmediumweak
—
accent-on-solid
strongmediumweak
—
accent-border
strongmediumweak
—
accent-alpha
strongmediumweak
—

4. Info

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
info-background
strongmediumweak
—
info-on-background
strongmediumweak
—
info-solid
strongmediumweak
—
info-on-solid
strongmediumweak
—
info-border
strongmediumweak
—
info-alpha
strongmediumweak
—

5. Danger

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
danger-background
strongmediumweak
—
danger-on-background
strongmediumweak
—
danger-solid
strongmediumweak
—
danger-on-solid
strongmediumweak
—
danger-border
strongmediumweak
—
danger-alpha
strongmediumweak
—

6. Warning

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
warning-background
strongmediumweak
—
warning-on-background
strongmediumweak
—
warning-solid
strongmediumweak
—
warning-on-solid
strongmediumweak
—
warning-border
strongmediumweak
—
warning-alpha
strongmediumweak
—

7. Success

background-strong
background-medium
background-weak
on-background-strong
on-background-medium
on-background-weak
solid-strong
solid-medium
solid-weak
on-solid-strong
on-solid-medium
on-solid-weak
border-strong
border-medium
border-weak
alpha-strong
alpha-medium
alpha-weak
Type
Default
success-background
strongmediumweak
—
success-on-background
strongmediumweak
—
success-solid
strongmediumweak
—
success-on-solid
strongmediumweak
—
success-border
strongmediumweak
—
success-alpha
strongmediumweak
—

Additional token groups

Beyond the semantic and function token systems, Once UI provides several specialized color token groups for specific UI concerns.

Code syntax tokens

moss
gray
aqua
green
blue
violet
orange
Type
Default
code-moss
CSS color—
code-gray
CSS color—
code-aqua
CSS color—
code-green
CSS color—
code-blue
CSS color—
code-violet
CSS color—
code-orange
CSS color—

Data visualization tokens

blue
aqua
magenta
pink
yellow
orange
red
moss
green
emerald
cyan
violet
indigo
gray
Type
Default
data-blue
CSS color—
data-aqua
CSS color—
data-magenta
CSS color—
data-pink
CSS color—
data-yellow
CSS color—
data-orange
CSS color—
data-red
CSS color—
data-moss
CSS color—
data-green
CSS color—
data-emerald
CSS color—
data-cyan
CSS color—
data-violet
CSS color—
data-indigo
CSS color—
data-gray
CSS color—

Static tokens

transparent
white
white-medium
black
black-medium
Type
Default
static-transparent
CSS color#00000000
static-white
CSS color#ffffff
static-white-medium
CSS color#ffffff4D
static-black
CSS color#000000
static-black-medium
CSS color#0000004D

Applying colour

Colour arrives in pairs. background sets a surface and onBackground sets the text that sits on it; solid sets a filled control and onSolid the text on that. Setting the text colour on the container means every label inside inherits it, so it is written once rather than on each child.

Which to reach for:

  • background for a panel, a section, a page area.
  • solid for something interactive that has to read as pressable — a button, a checkbox, a filled badge.
  • an alpha weight when the colour should tint whatever is behind it rather than replace it. This is what keeps a layer working over both an image and a plain surface.
  • surface for the neutral raised panel, which follows the theme without naming a scheme at all.

The brand colour

solid="contrast", the default solid style, draws filled controls in neutral: near-white in dark mode, near-black in light. So under it --brand-solid-strong is not the brand colour, by design, and anything that reads it for "the brand colour" loses its hue.

When something needs the brand hue whatever the solid style, such as an effect, an illustration, a gradient or a highlight, use --brand-color-strong, --brand-color-medium or --brand-color-weak. They are the brand solids as data-solid="color" draws them, in both themes, and no solid style changes them.

Keep --brand-solid-* for what it is for: filled, pressable things that should follow the solid style. --brand-on-background-* keeps the hue too, but is tuned for text on a background, so it runs light in dark mode.

Colour format

Scheme tokens are oklch(). The values are the same colours the palette has always used — every one round-trips to the hex it replaced — but expressed in a space where the numbers mean something: oklch(0.6743 0.1670 261.54) reads as "two thirds as light as white, moderately saturated, blue", where #5A93FC reads as nothing.

That needs Chrome 111, Safari 15.4 or Firefox 113, all from 2022–23. There is no fallback: a browser that does not understand oklch() drops the declaration rather than approximating it.

Custom schemes

data-brand="custom" reads --scheme-brand-* (and data-accent and data-neutral the same way), which your app supplies. Rather than hand-picking twelve steps that sit evenly against each other, generate them from one colour. In the config, that is a seed:

ThemeInit generates the scheme on the server and renders it in a <style>, so it is there before the first paint; ThemeProvider applies the same values on the client when the seed changes. See ThemeProvider.

To apply a scheme yourself at runtime, customSchemeProperties returns the properties to write, from the same pure function both of those use:

generateColorScheme and schemeAlphaVariants underneath it return the raw twelve steps and the three alphas, if you need the values for something other than the page's scheme.

The seed can be hex, rgb()/rgba() or oklch() — which covers what ColorInput produces with and without supportAlpha. An unparseable seed returns null rather than something wrong, so you can leave the current scheme in place.

Steps are anchored to a contrast ratio against black, not to fixed lightness values, so a generated ramp holds the same contrast relationships as the built-in schemes whichever hue it starts from. Hue and chroma come from the seed and stay put; only lightness moves.

One thing it does not do is copy the built-in schemes' hand-tuning. Those were drawn by eye, and at the saturated dark end — step 400 especially — several carry more chroma than their own step 600. A ramp built from a single seed cannot know that. Regenerating each built-in scheme from its own step 600 lands within 0.11 OKLab ΔE at the worst step, and inside 0.05 for most.

Edit this page on GitHub