Colors
Six ramps on one lightness scale, a set of translucent tints, and the semantic tokens that map them onto the light and dark themes. Components use the semantic tokens; the ramps are for the few places a role does not exist yet.
Scales
Every hue shares the same lightness steps, so red-600 and green-600 carry the same weight. Click a swatch to copy its var(); Option-click copies the resolved value.
- 50 – 100
- Light surfaces and hover fills; brand-subtle in light.
- 200 – 300
- Light borders and quiet chart ramps.
- 400 – 500
- Graphics: rings, icons, chart series. 500 is the exact brand hue; dark lifts graphics to 400.
- 600 – 700
- Text-safe fills under white text, status and secondary text in light.
- 800 – 975
- Dark borders (800), dark elevated surfaces (900), the dark canvas and light ink (950).
Tints
Neutral 500 (600 in dark) at fixed alphas. They sit on any surface, so a hover or hairline reads the same on the canvas, a card or the sidebar. Reach for a tint before a ramp step whenever the fill is a state, not a surface.
| Example | Token | Usage |
|---|---|---|
| – | Pressed and active rest. | |
| – | Row hover in dense lists. | |
| – | Chip and tint-button rest, table row hover, nav hover. | |
| – | Hairline borders (border-subtle), skeletons. | |
| – | Chip and button hover, active nav item. | |
| – | Loud borders, input outlines, active fills. | |
| – | Hover on inline reference chips in dark. | |
| – | Dividers, glass badges, the shimmer sweep. |
Using colour
Colour is mostly neutral. Tangerine and the status hues are signals, so they only work when they are rare.
Brand
Do. Ink for the primary action. Save brand fills for the one moment that is the product's own.
Avoid. Tangerine on every button. When everything is brand, nothing is.
Status
Do. Tinted badges for passive state, and a neutral badge when nothing is wrong.
Avoid. Solid status fills on labels, or a status hue for something that is not a status.
Text
Do. Three tiers: foreground, muted-foreground, foreground-low. Each step down is less important.
Avoid. Body copy in foreground-low, or brand-accent as a text colour. Both are for graphics and captions.
Surfaces
Backgrounds, each with the foreground it ships with. The ratio is measured live in the current theme.
| Example | Token | Usage |
|---|---|---|
| – | The canvas. Pages, docs blocks, the app shell. White in light, charcoal in dark. | |
| – | Cards. The canvas colour in light; the hairline in shadow-card does the separating. | |
| – | Menus, popovers and select lists. | |
| – | The composer and anything that floats above a card. | |
| – | A whisper above the canvas: the hero backdrop's first stop. | |
| – | Sunken fills: the nav pill, code blocks, DemoFrame footers. Below the canvas in dark. | |
| – | Quiet fills that carry secondary text. | |
| – | Hover and highlighted rows in menus and lists. | |
| – | shadcn's secondary pair, kept for the primitives that read it. | |
| – | Dialog and sheet surface. |
Text
Three tiers on the canvas, plus the inverted ink. All clear AA (4.5:1) in both themes; nothing lighter than foreground-low carries words.
| Example | Token | Usage |
|---|---|---|
| – | Body text, headings, icons at rest. | |
| – | Secondary text: leads, table cells, inactive navigation. | |
| – | The floor. Captions, placeholders, token names; never a paragraph. | |
| – | Text on ink fills, such as the primary button. |
Lines and focus
Borders are graphics, so the bar is 3:1 against the canvas where a line has to be seen, and none for decorative hairlines.
| Example | Token | Usage |
|---|---|---|
| – | Outlines that must be seen: inputs at rest, DemoFrame, pane dividers. | |
| – | The composer and focused-looking inputs (tint-20). | |
| – | Input borders. Same value as border-loud. | |
| – | Hairlines: table rows, block rules, card edges (tint-12). Decorative, so no 3:1 claim. | |
| – | Focus ring. Tangerine 500, lifted to 400 in dark. | |
| – | Text selection: a tangerine wash with tangerine text. |
Brand
Two tangerines: brand-accent is the exact brand hue for graphics, brand is one step deeper so it can carry white text.
| Example | Token | Usage |
|---|---|---|
| – | The default button. Ink, not orange. | |
| – | The text-bearing brand fill: one CTA per view, the user's chat bubble. | |
| – | Rings, icons, selection, chart series 1. Graphic only, never text. | |
| – | Tinted brand surface: the brand badge, highlighted mentions. | |
| – | Category chips and tabs at rest; hover moves to accent. |
Status
Solid fills for actions and alerts, and the same hue at 10% behind its own text for badges.
| Example | Token | Usage |
|---|---|---|
| – | Delete, revoke, failed runs. Darker fill + white in light, lighter fill + ink in dark. | |
| – | Completed runs, connected integrations. | |
| – | Needs attention but nothing has broken: quota, expiring tokens. | |
| – | Neutral notices and tips. |
Chat
The two bubbles of a conversation. Only the visitor's carries the brand.
| Example | Token | Usage |
|---|---|---|
| – | The visitor's message. Tangerine 600 so white text clears AA. | |
| – | The agent's message bubble. |
Summarise this week's support tickets.
42 tickets, mostly billing. Three are still open.
Sidebar
The app sidebar has its own set so it can diverge from the canvas without touching it.
| Example | Token | Usage |
|---|---|---|
| – | The app sidebar's own surface. | |
| – | The selected workspace or primary action in the sidebar. | |
| – | Hovered and active sidebar rows. | |
| – | The sidebar's edge and its dividers. | |
| – | Focus inside the sidebar. |
Charts
Six categorical series in a fixed order, a tangerine sequence for ordered data, and the furniture around them. Every series lifts to its 400 step in dark.
| Example | Token | Usage |
|---|---|---|
| – | Series 1: brand. Always the series the view is about. | |
| – | Series 2: neutral. | |
| – | Series 3: blue. | |
| – | Series 4: green. | |
| – | Series 5: amber. | |
| – | Series 6: red. |
| Example | Token | Usage |
|---|---|---|
| Ring and bar tracks behind a value. | ||
| Gridlines. Aliases border-subtle. | ||
| The dashed goal or target line. | ||
| The optimal-range band: green at 12%. |
Mind tiers
The seven tiers of the Mind score badge, re-mapped lighter for dark. Graphic only: in light, legendary sits under 3:1 on white and novice barely clears it, so a tier colour never carries text.
| Example | Token | Usage |
|---|---|---|
| – | Tier 1 of 7, the lowest. | |
| – | Tier 2 of 7. | |
| – | Tier 3 of 7. | |
| – | Tier 4 of 7. | |
| – | Tier 5 of 7. | |
| – | Tier 6 of 7. | |
| – | Tier 7 of 7, the highest. |
Glyph avatar
The one agent-glyph colourway that follows the theme: a 28px tile that inverts in dark, with figures re-picked against it.
| Example | Token | Usage |
|---|---|---|
| – | The working agent's figure. The tile is --glyph-avatar; the eyes are painted in it. | |
| – | A finished agent. | |
| – | An agent waiting on the visitor. | |
| – | A failed agent. |
Overlays
Surfaces that cover the page rather than sit in it.
| Example | Token | Usage |
|---|---|---|
| Dialog backdrop. Black at 30% in both themes. | ||
| Top-edge highlight layers inside the shadow tokens (also -soft and -strong). Not for fills. | ||
| The 1px light edge on dark glass. Transparent in light. |
Best practices
Choosing a token
- Start from a semantic token (background, muted-foreground, border-subtle). Use a ramp step only when no role fits, then ask whether the role is missing.
- Use a tint for states (hover, pressed, hairlines) and a surface token for places.
- Pair every fill with its own -foreground token; never mix one pair's background with another's text.
- Never write a raw colour in a component.
npm run brand:lint-tokensfails on hex literals and colour functions.
Contrast and themes
- Text clears 4.5:1 and graphics that carry meaning clear 3:1, in both themes. The ratios on this page are live; switch the theme to check the other mapping.
- brand-accent, the chart series and the Mind tiers are graphics. Use brand, or a status pair, when tangerine has to carry words.
- Status is never colour alone: pair the hue with an icon or a word. Change a ramp with
npm run brand:gen-ramps, thennpm run brand:check-contrast.