Colors

Color in Root is a small, locked set. Three seeds define the primitives. Semantic names sit on top of those primitives. Views must use the semantic names. They must not use a primitive step directly.

This site follows the system. It uses the matching aliases for the current system mode: paper canvas and primary ink in light, inverted paper and ink in dark. The ramps below still show both modes.

Seeds

Each family has one seed. The rest of the ramp comes from that seed in OKLCH. Do not pick a new hex for a product screen.

Neutral

#FBFAF9 at 100 light / 1000 dark

Blue Electric

#2A56F7 at 700, both modes

Orange Electric

#F7452A at 700, both modes

Two layers

The primitive layer is the ramp. The semantic layer is the contract with the interface. If a view needs a color, it asks for a role such as text-primary or bg-accent. It does not ask for neutral-900.

  1. Primitives. Neutral, Blue Electric, Orange Electric, and Neutral Alpha. These keep the ramp consistent. They are not for layout code.
  2. Semantics. Backgrounds, text, icons, and borders. These names stay stable when a seed changes.

There is no third layer of component tokens. If a screen needs a one-off role, add a semantic alias. Do not add a new layer.

Step roles

Every family uses the same ten steps. The role of a step does not change from family to family. The numbers are positions on the ramp, not lightness values. Step 1000 is always primary text, in both modes.

StepRole
100Canvas / default background
200Raised surface / hover background
300Pressed surface / grouped background
400Hairline divider (decorative)
500Control border
600Hover border
700Solid brand fill
800Solid fill that can hold text / hover
900Secondary text and icon
1000Primary text and icon

The jump from 400 to 500 is large on purpose. 400 is a decorative hairline. 500 is an accessible control border. If you need a divider that people can see, use 500.

Families

Neutral

The paper and ink of the system. The seed is #FBFAF9. In light mode that hex sits at 100. In dark mode it sits at 1000. The paper becomes the ink.

Light
100 · seed#FBFAF9
200#F3F1F0
300#EAE7E5
400#DAD7D4
500#928E8B
600#817E7A
700#736F6C
800#63605D
900#545250
1000 · seed#262524
Dark
100 · seed#090807
200#1B1918
300#242221
400#32302E
500#656360
600#75726F
700#858380
800#94928F
900#A9A7A5
1000 · seed#FBFAF9

Blue Electric

The interactive color. The seed is #2A56F7 at 700 in both modes. Use Blue only for actions that the user can touch.

Light
100#F4F8FF
200#E8F0FF
300#D8E5FF
400#C1D4FF
500#6490FF
600#4676FF
700 · seed#2A56F7
800#1B3DE3
900#1531C5
1000#091C84
Dark
100#0E1526
200#131E38
300#18264C
400#1E3269
500#2344B0
600#284ED0
700 · seed#2A56F7
800#4D7CFF
900#80A6FF
1000#D1E0FF

Orange Electric

The moment color. The seed is #F7452A at 700 in both modes. Use Orange for emphasis, not for states.

Light
100#FFF6F4
200#FFECE8
300#FFDDD6
400#FFC8BC
500#FFA18F
600#FF735B
700 · seed#F7452A
800#C91E00
900#9F1600
1000#670A00
Dark
100#24110E
200#341711
300#451B14
400#5E2117
500#A8301D
600#CA3923
700 · seed#F7452A
800#FF6C54
900#FF9683
1000#FFD9D1

Neutral Alpha

The same Neutral ink with a transparent channel. Use it for hover, pressed, hairlines, and overlays that must work on any surface.

Light
100#00000008
200#0000000D
300#00000014
400#0000001C
500#00000029
600#00000038
700#0000004C
800#0000006B
900#00000099
1000#000000D9
Dark
100#FFFFFF08
200#FFFFFF0D
300#FFFFFF14
400#FFFFFF1C
500#FFFFFF29
600#FFFFFF38
700#FFFFFF4C
800#FFFFFF6B
900#FFFFFF99
1000#FFFFFFD9

Semantic aliases

These names are the public API of color in Root. Use them in views. Do not invent a new alias for a one-off screen.

Backgrounds

Paper, chrome, fills, and tinted surfaces. Dark mode inverts Neutral. Blue and Orange fills keep the 700 seed.

bg-canvasLight neutral / 100 · Dark neutral / 100
bg-elevatedLight neutral / 200 · Dark neutral / 200
bg-sunkenLight neutral / 300 · Dark neutral / 100
bg-disabledLight neutral / 300 · Dark neutral / 300
bg-fillLight neutral / 1000 · Dark neutral / 1000
bg-fill-hoverLight neutral / 900 · Dark neutral / 900
bg-accentLight blue-electric / 700 · Dark blue-electric / 700
bg-accent-hoverLight blue-electric / 800 · Dark blue-electric / 800
bg-accent-subtleLight blue-electric / 200 · Dark blue-electric / 200
bg-expressiveLight orange-electric / 700 · Dark orange-electric / 700
bg-expressive-strongLight orange-electric / 800 · Dark orange-electric / 800
bg-expressive-subtleLight orange-electric / 200 · Dark orange-electric / 200

Text and icons

Primary, secondary, and tertiary ink. Group titles use Neutral 500. Disabled controls use Neutral 400. Inverse ink on filled controls, and the two chromatic marks.

text-primaryLight neutral / 1000 · Dark neutral / 1000
text-secondaryLight neutral / 900 · Dark neutral / 900
text-tertiaryLight neutral / 800 · Dark neutral / 800
text-disabledLight neutral / 700 · Dark neutral / 700
text-linkLight blue-electric / 900 · Dark blue-electric / 900
text-expressiveLight orange-electric / 900 · Dark orange-electric / 900
text-on-fillLight neutral / 100 · Dark neutral / 100
text-on-accentLight neutral / 100 · Dark neutral / 1000
text-on-expressiveLight neutral / 100 · Dark neutral / 100
icon-accentLight blue-electric / 700 · Dark blue-electric / 700
icon-expressiveLight orange-electric / 700 · Dark orange-electric / 700

Borders

Hairlines, control borders, and focus rings.

border-hairlineLight neutral / 400 · Dark neutral / 400
border-controlLight neutral / 500 · Dark neutral / 500
border-control-hoverLight neutral / 600 · Dark neutral / 600
border-focusLight blue-electric / 700 · Dark blue-electric / 700
border-on-accent-subtleLight blue-electric / 700 · Dark blue-electric / 800
border-on-expressive-subtleLight orange-electric / 700 · Dark orange-electric / 800

bg-sunken does not sink in dark mode. In light it is Neutral 300. In dark it points at the canvas. Surfaces in dark mode rise by getting lighter. There is nothing below the canvas.

text-on-accent does not invert. bg-accentkeeps the same Blue 700 in both modes, so the text on it stays paper hex #FBFAF9.

Text rank

Ink rank is primary, then secondary, then tertiary, then Neutral 500, then Neutral 400. Group titles use Neutral 500. Body uses Neutral 700. Disabled controls use Neutral 400. Active ink stays text-primary or text-secondary.

  • Group titles recede with Neutral 500. They stay above disabled rows.
  • Disabled controls are inactive UI. Neutral 400 is quieter than the title. Do not use Neutral 400 for live chrome.

Usage rules

  • Blue marks what the user can touch and nothing else. A filled control, a link, a focus ring, and a selected state all resolve to Blue.
  • Orange is for a moment. Use it on a launch mark, a rare highlight, or a product accent. Do not use Orange as success, warning, or error.
  • Orange 700 is the seed. It is not a small-text color. Small text on Orange must use step 800, through bg-expressive-strong.
  • Use Neutral Alpha for hover and pressed. A solid hover on a tinted card looks wrong.
  • Do not mix a primitive step with a semantic alias in the same view. Pick the semantic name.

Contrast pairs

The pairs below are the intended combinations. A pair that fails its bar is marked. Orange 700 with small text fails. Use Orange 800 instead.

PairSampleRatioBarResult
Light / text-primary on bg-canvasAg14.68Small text 4.5:1Pass
Light / text-secondary on bg-canvasAg7.46Small text 4.5:1Pass
Light / text-tertiary on bg-canvasAg5.99Small text 4.5:1Pass
Light / text-disabled on bg-canvasAg4.77Small text 4.5:1Pass
Light / text-secondary on bg-elevatedAg6.91Small text 4.5:1Pass
Light / border-hairline (decorative)Ag1.37Decorative—
Light / border-controlAg3.12UI 3:1Pass
Light / border-control-hoverAg3.87UI 3:1Pass
Light / fill-neutral with paper textAg5.99Small text 4.5:1Pass
Light / text-link (blue-900)Ag8.84Small text 4.5:1Pass
Light / fill-accent + text (blue-700)Ag5.57Small text 4.5:1Pass
Light / icon-accent (blue-700) on canvasAg5.34UI 3:1Pass
Light / focus-ring (blue-700) on canvasAg5.34UI 3:1Pass
Light / note: text-link on blue-200Ag12.14Small text 4.5:1Pass
Light / note: border blue-700 on blue-200Ag4.86UI 3:1Pass
Light / text-expressive (orange-900)Ag7.78Small text 4.5:1Pass
Light / fill-expressive-700 + small textAg3.60Small text 4.5:1Fail
Light / fill-expressive-700 + large textAg3.60Large text 3:1Pass
Light / fill-expressive-800 + small textAg5.72Small text 4.5:1Pass
Light / icon-expressive (orange-700) on canvasAg3.45UI 3:1Pass
Light / note: border orange-700 on orange-200Ag3.15UI 3:1Pass
Light / note: text orange-1000 on orange-200Ag11.37Small text 4.5:1Pass
Dark / text-primary on bg-canvasAg19.20Small text 4.5:1Pass
Dark / text-secondary on bg-canvasAg8.35Small text 4.5:1Pass
Dark / text-tertiary on bg-canvasAg6.45Small text 4.5:1Pass
Dark / text-disabled on bg-canvasAg5.29Small text 4.5:1Pass
Dark / text-secondary on bg-elevatedAg7.31Small text 4.5:1Pass
Dark / border-hairline (decorative)Ag1.52Decorative—
Dark / border-controlAg3.34UI 3:1Pass
Dark / border-control-hoverAg4.19UI 3:1Pass
Dark / fill-neutral with paper textAg2.98Small text 4.5:1Fail
Dark / text-link (blue-900)Ag8.40Small text 4.5:1Pass
Dark / fill-accent + text (blue-700)Ag5.57Small text 4.5:1Pass
Dark / icon-accent (blue-700) on canvasAg3.59UI 3:1Pass
Dark / focus-ring (blue-700) on canvasAg3.59UI 3:1Pass
Dark / note: text-link on blue-200Ag12.45Small text 4.5:1Pass
Dark / note: border blue-700 on blue-200Ag2.97UI 3:1Fail
Dark / text-expressive (orange-900)Ag9.47Small text 4.5:1Pass
Dark / fill-expressive-700 + small textAg5.56Small text 4.5:1Pass
Dark / fill-expressive-700 + large textAg5.56Large text 3:1Pass
Dark / fill-expressive-800 + small textAg7.17Small text 4.5:1Pass
Dark / icon-expressive (orange-700) on canvasAg5.56UI 3:1Pass
Dark / note: border orange-700 on orange-200Ag4.57UI 3:1Pass
Dark / note: text orange-1000 on orange-200Ag12.61Small text 4.5:1Pass

Chart family

Four hues, because four is what the paper pays for. Every fill has to clear 3:1 against Neutral 100. That caps relative luminance at 0.286. Split it four ways and neighbouring series sit 0.061 apart, which survives greyscale. Split it six ways and they do not. Four is arithmetic, not taste.

Chroma is about half the electric seeds. A data series must never be mistaken for a control. Ochre is out: below L 0.66 a yellow on warm paper is muddy. These inks are for plots and illustrations. They are not for layout.

Indigo

#4868B7 at 700

Ember

#C46150 at 700

Teal

#1B9CAB at 700

Moss

#0C5E27 at 700

SeriesStep 700On paperLabel on the fillLuminance
Indigo#4868B75.11:1Paper0.147
Ember#C461503.89:1Large text only0.209
Teal#1B9CAB3.15:1Ink0.270
Moss#0C5E277.61:1Paper0.082

Series order is Indigo, Ember, Teal, Moss. Two-series charts get the most separated pair first. Ember 900 can hold small paper text if the fill must carry a caption.

Indigo

Series 1. Paper labels sit on the fill. Contrast on paper is 5.11:1. Luminance is 0.147.

Paper plate
100#F5F8FE
200#EAF0FC
300#DBE5F9
400#C6D4F3
500#7895D5
600#6180C9
700 · seed#4868B7
800#3655A4
900#2D488E
1000#1A2D5E

Ember

Series 2. No small text on Ember 700. If a label must sit on the fill, set it at 20px or larger, or move it outside. Contrast on paper is 3.89:1. Luminance is 0.209.

Paper plate
100#FEF6F4
200#FDEDE9
300#FBE1DC
400#F7D0C8
500#DE8E7F
600#D47868
700 · seed#C46150
800#A84636
900#8E3729
1000#551910

Teal

Series 3. Labels on the fill use ink, not paper. Contrast on paper is 3.15:1. Luminance is 0.270.

Paper plate
100#F2F9FA
200#E7F4F6
300#D8EEF1
400#C1E4E9
500#6ABBC6
600#49ADBA
700 · seed#1B9CAB
800#007E8B
900#006772
1000#00383E

Moss

Series 4. Paper labels. Contrast on paper is 7.61:1. Luminance is 0.082.

Paper plate
100#F4F9F5
200#E6F1E8
300#D5E5D6
400#BAD3BD
500#598F62
600#387846
700 · seed#0C5E27
800#00541F
900#004D1C
1000#093B17

These names are the public API of Pulse color. Views that draw a plate use them. Views that draw chrome do not. Categories start at series-1. Pictures start at illust-indigo.

Plot

What a chart asks for. Subject is still Blue. Moment is still Orange. Context is Neutral.

plot-paper#FBFAF9
plot-grid-minor#EAE7E5
plot-grid-major#DAD7D4
plot-axis#928E8B
plot-context#DAD7D4
plot-subject#2A56F7
plot-moment#F7452A
plot-label#262524
plot-label-quiet#545250

Series

Unordered categories only. Never a state. Never a control.

series-1#4868B7
series-2#C46150
series-3#1B9CAB
series-4#0C5E27

Illustration

What a picture asks for. Same inks as the series. Wash steps are Chart 400.

illust-indigo#4868B7
illust-ember#C46150
illust-teal#1B9CAB
illust-moss#0C5E27
illust-indigo-wash#C6D4F3
illust-ember-wash#F7D0C8
illust-teal-wash#C1E4E9
illust-moss-wash#BAD3BD

Open questions

  • There is no error red yet. Do not use Orange for destructive actions until that decision is closed.
  • There is no success green and no warning yellow. First apps should use copy and icons for that feedback.
  • High Contrast is not defined. Hairline and tertiary text are the first steps that would need it.

Source of truth: foundations/tokens.json for UI color, and foundations/chart_tokens.json for Chart and Pulse inks. The scripts build_palette.py and emit.py rebuild the UI file. build_chart_palette.py rebuilds the Chart family. Do not edit a hex by hand if you plan to run the scripts.