Colour
The palette is three scales — midnight, orange, and slate — each expressed as a 12-step ramp in a light and a dark variant. The step numbering follows the Radix convention, which assigns every step a role. Using a step outside its role is the single most common way to break the system.
Core colours
These are the named aliases most projects reach for first. They are shortcuts into the scales, not a separate palette.
- Primary#1d3352Text, headings, primary surfaces
- Primary Light#2b4d78Hover states, dark-mode primary
- Primary Dark#132440Dark backgrounds, navbar, code blocks
- Accent#E05232CTAs, highlights, active states
- Accent Light#ea7558Accent hover on dark, syntax strings
- Accent Dark#b84228Accent pressed state
- Accent Solid#cc4528Fill for solid controls with white text (4.72:1)
- Secondary#546a82Supporting text, borders
Step roles
Every scale uses the same twelve roles in the same order:
| Steps | Role | Used for |
|---|---|---|
| 1–2 | App and subtle backgrounds | Page and section surfaces |
| 3–5 | Element backgrounds | Component fills, hover, active |
| 6–8 | Borders | Subtle, default, and strong borders |
| 9–10 | Solid | Solid fills and their hover state |
| 11–12 | Text | Low-emphasis and high-emphasis text |
Steps 8, 9, and 10 are border and solid-surface roles. They are not held to text contrast thresholds and must not be used for body text. If you need dimmer text than step 11, define a dedicated token and verify its contrast — see the code-comment token for a worked example.
Midnight
Midnight is the primary. It carries structure, text, and the calm end of the
system. Step 9 (#1d3352) is the brand primary and is identical in both modes.
Orange
Orange is the accent. It marks the primary action, the active state, and little
else. Step 9 (#E05232) is constant across modes.
Use orange for the single most important action in a view, active navigation state, and focus emphasis.
Use orange for large background fills, body text, or more than one competing call to action on the same screen.
Slate
Slate is the secondary — supporting text, borders, and neutral surfaces. Step 9
(#546a82) is the brand secondary and is constant across modes.
Terminal
The three scales cover every surface that has a light mode. A terminal does not: it has one surface, it is dark, and every colour in it is measured against that one background. It also needs six hues where the interface needs three, because programs have been writing to sixteen ANSI slots since long before this system existed.
So the terminal palette is a fourth member of the system rather than a fifth
scale — a fixed sixteen-slot palette plus its chrome, derived from the ramps and
measured against midnight-dark-1.
| # | Name | Normal | On surface | Bright | On surface | Provenance |
|---|---|---|---|---|---|---|
| 0 | black |
#0e1720 | — |
#2e4b68 | 2.00:1 | midnight-dark-1 / midnight-dark-6 — Box drawing and rules, not text — deliberately below the floor. |
| 1 | red |
#e55b5b | 5.15:1 |
#f08b80 | 7.49:1 | bright = danger-dark — Never the accent — an error and the brand must not look alike. |
| 2 | green |
#3f9d74 | 5.41:1 |
#6cc090 | 8.24:1 | bright = success-dark |
| 3 | yellow |
#c08a1e | 5.93:1 |
#e0a92a | 8.50:1 | bright = warning-dark — Gold, matching the warning role in the interface. |
| 4 | blue |
#6289b3 | 4.95:1 |
#8aacc8 | 7.59:1 | bright = midnight-dark-11 |
| 5 | magenta |
#bd6d96 | 4.98:1 |
#d491b4 | 7.32:1 | terminal-only — The brand defines no magenta; this slot exists only here. |
| 6 | cyan |
#3f97a3 | 5.31:1 |
#74c0c9 | 8.71:1 | terminal-only — The brand defines no cyan; this slot exists only here. |
| 7 | white |
#97a8b8 | 7.41:1 |
#c5daf0 | 12.62:1 | slate-dark-11 / midnight-dark-12 |
Read the provenance column carefully. The bright ramp is the brand: where a hue already exists in the system, the bright slot takes that step verbatim. The normal ramp has no brand equivalent — the scales define one value per semantic role, not a dim and a bright — so each normal slot is its bright counterpart darkened until it reads a step back while still clearing the floor.
Magenta and cyan exist in neither half of the brand. They are here because a terminal requires them, and nowhere else.
#e55b5b is the terminal’s red. It is not $danger, which is #a8261c.
The normal ramp exists to fill ANSI slots and is measured only against the
terminal surface; using one of its values in the interface puts an unmeasured
colour on an unrelated background.
Terminal chrome
The accent gets no ANSI slot, because it is not semantic — it marks where you are. That, and the surfaces around the sixteen, live here.
| Role | Value | Measured |
|---|---|---|
| Background |
#0e1720 | the surface |
| Foreground |
#c5daf0 | 12.62:1 |
| Cursor |
#e05232 | 4.67:1 |
| Cursor text |
#0e1720 | 4.67:1 on the cursor |
| Selection background |
#20354d | — |
| Selection text |
#c5daf0 | 8.74:1 |
| Dim / comment |
#72889d | 4.93:1 |
| Status bar surface |
#131e2b | — |
| Status bar text |
#c5daf0 | 11.74:1 |
| Inactive tab and pane label |
#7b8da3 | 4.95:1 |
| Active tab fill |
#e05232 | 4.67:1 with #0e1720 text |
| Active pane border |
#e05232 | 4.67:1 |
| Inactive pane border |
#7b8da3 | 5.32:1 |
Every non-background value clears 4.5:1 against the surface; the measured floor is 4.95:1. ANSI 0 bright is the one deliberate exception — programs use it for box drawing and rules, not for text.
Configuration for tmux, WezTerm, Kitty, iTerm2, and Zellij is on the Terminal theming page.
Contrast rules
- Never use pure
#000or#fffas text. Use step 12 of the relevant scale:#142438on light,#c5daf0on dark. - Step 9 is constant across modes. The solid accent does not shift when the theme changes.
- Body text targets 4.5:1, large text (≥24px, or ≥18.66px bold) targets 3:1.
- Verify against the actual surface. A step that passes on the app background may fail on an elevated panel.
Where an identity colour cannot carry text
Being the brand colour does not make a value a legible background. White on
orange-9 (#E05232) measures 3.87:1 — fine as a mark or a border, but
below the floor for button labels. Rather than dilute the accent, the system
adds a separate fill for that job:
| Token | Hex | With white text |
|---|---|---|
--color-accent | #E05232 | 3.87:1 — identity only, not for text |
--color-accent-solid | #cc4528 | 4.72:1 — solid controls |
--color-accent-dark | #b84228 | 5.46:1 — hover and pressed |
The same principle produced the
code-comment token: when no existing
step can do the job accessibly, name a new one rather than misuse a step.
Semantic colours are mode-specific
The callout hues are tuned for dark text on tinted light backgrounds. Used as foregrounds on the dark app surface they fall below AA, so dark mode has its own set:
| Role | Light | Dark | On #0e1720 |
|---|---|---|---|
| Success | #2f7d65 | #6cc090 | 3.65:1 → 8.24:1 |
| Warning | #8b6508 | #e0a92a | 3.41:1 → 8.50:1 |
| Danger | #a8261c | #f08b80 | 2.55:1 → 7.49:1 |
| Info | #3a5a82 | #8aacc8 | 2.55:1 → 7.59:1 |
Dark mode
Both modes are equally supported. See Dark mode for the implementation rules — theme switching, persistence, image treatment, and the always-dark code surface.