Nexus DSdocs

Color

Engineered, not picked. Every color is stored as hex, converted to OKLCH at build time, with its lightness pinned to a perceptual grid. The five neutral bases share one grid — the same shade step is equally light in every base — while each chromatic hue follows its own lightness curve, centred where that hue is most vivid, and takes its chroma at the edge of the Display-P3 gamut. Contrast is then gated by APCA before it can ship.

The palettes

Start with the five neutral bases — eleven shades each. Read down any column: every base’s 500 sits at the same perceptual lightness, only hue and chroma differ. Below are the chromatic hues brand and status are built from — these each follow their own lightness curve, so a vivid yellow can stay light while red runs deep — then the full reference. One color-vision filter applies to them all.

50
100
200
300
400
500
600
700
800
900
950
slate
neutral
gray
stone
zinc

Brand & status hues

Brand modes are blue, purple, pink, teal, orange, and black. The chromatic brand modes share the OKLCH pipeline; black is a monochrome semantic recipe built from black.base, white.base, and neutral support. Switch to a color-vision filter and watch the red/green pair (error/success) converge: that’s why status never relies on hue alone.

50
100
200
300
400
500
600
700
800
900
950
bluebrand · information
purplebrand
pinkbrand
tealbrand
orangebrand
greensuccess
yellowwarning
rederror
All color scales (17)

The full chromatic set. Brand uses blue, purple, pink, teal, and orange; status uses red, yellow, green, and blue. The rest are raw primitives available for data viz and one-off use.

50
100
200
300
400
500
600
700
800
900
950
red
orange
amber
yellow
lime
green
emerald
teal
cyan
sky
blue
indigo
violet
purple
fuchsia
pink
rose

Live tokens

Semantic surface/foreground pairs — the real tokens components use. Open the theme picker (bottom-right) and swap the base or dark mode: every pair re-resolves live, and each clears the APCA gate by construction.

Foreground describes its relationship to a surface; text describes one application. The relationship comes first when naming a token. Text and icons share that color; a foreground can also paint a control part or indicator on the same surface. For example, primary-foreground colors both a button’s label and a checked switch’s knob on primary-background. Likewise, error-subtle-foreground colors the message and icon on error-subtle.

A foreground has a default background partner, not necessarily an exclusive one. Use it on other surfaces only with contrast coverage for the intended content tier. A token name alone does not guarantee readability on every background or at every text size.

Aabackground
Aamuted
Aacontainer
Aapopover
Aaprimary

What each shade is for

The shade number is a luminance coordinate, and each step maps to specific semantic roles. The mapping is not a simple light/dark flip — a shade lands at a different step in dark mode to hold the same perceptual tier. Text and hairline borders are the exception: foreground, muted-foreground and border.default are alpha tokens — transparent black in light, white in dark — so they composite onto any surface instead of binding to a neutral shade.

ShadeRoleLight-mode useDark-mode use
50Near-whitemuted, disabled, background/container/popover-hovernav-foreground
100Very lightbackground-active, container-active, control-background, nav-background—
200Lightcontrol-background-hover, nav-item-hover/active, nav-border—
300Light-medium—disabled-foreground, nav-muted-foreground
400Medium-lightborder-focus, disabled-foregroundborder-focus
500Mid— (perceptual anchor)— (anchor)
600Medium-darknav-muted-foreground, brand -background—
700Darkbrand -background-hovercontrol-background-hover, popover-hover
800Very darkbrand -background-activecontrol-background, popover, container-hover, nav-border
900Darkercontainer-foreground, popover-foreground, nav-foregroundcontainer, muted, background-hover/active, nav-item-hover/active
950Near-black— (rarely surfaced)background — the canvas, nav-background, disabled

Accessibility

Contrast is gated by APCA (not WCAG 2 ratios), with thresholds set per intended use. A failing pair blocks the build — thresholds are not negotiable per finding. The palette is also validated against Viénot-simulated dichromacy in CI; the toggle above is the visual preview of that test.

PairMin APCACovers
foreground ↔ backgroundLc ≥ 75Body text
{brand,status}-foreground ↔ -backgroundLc ≥ 60UI labels — buttons, badges
*-subtle-foreground ↔ -subtleLc ≥ 60Labels on tinted fills
nav-foreground ↔ nav surfacesLc ≥ 60Nav label text
chart.categorical ↔ containerLc ≥ 60Chart marks
muted-foreground ↔ mutedLc ≥ 45Incidental / de-emphasised text
disabled-foreground ↔ disabledLc ≥ 45Disabled-state text
focus ring ↔ every surfaceLc ≥ 45Focus indicators

How it works

  1. 1
    Store hex

    Tokens live as hex on disk — the only format Figma and Tokens Studio round-trip cleanly.

  2. 2
    Convert to OKLCH

    At build time every hex value is converted to an oklch(…) value emitted into CSS.

  3. 3
    Pin the lightness

    Each shade’s L is overwritten by a perceptual grid — the neutral bases share one ladder, while each chromatic hue follows its own curve (yellow peaks light, red deep). Neutrals keep their source chroma; the re-pitched hues take it at the Display-P3 cusp.

  4. 4
    Gate with APCA

    Every foreground/background pair is scored with APCA before merge. A failing pair blocks the build.