Colour — semantic
The design language. This is what product code should reach for.
34 tokens — 34 house.
| token | light | dark | verdict | resolves through |
|---|---|---|---|---|
--content-alert | #8537ad | #c084fc | house | light: --color-purple-500dark: --color-purple-300 |
--content-default | #203053 | #e5e5e5 | house | light: --color-gray-800dark: --color-neutral-200 |
--content-error | #c8223d | #f87171 | house | light: --color-red-500dark: --color-red-400d |
--content-info | #102984 | #60a5fa | house | light: --color-azure-700dark: --color-azure-350d |
--content-muted | #67789d | #8b8b8b | house | light: --color-gray-600dark: --color-neutral-500 |
--content-secondary | #415377 | #a3a3a3 | house | light: --color-gray-700dark: --color-neutral-400 |
--content-subtle | #7a86a6 | #737373 | house | light: --color-gray-550dark: --color-neutral-550 |
--content-success | #175c41 | #4ade80 | house | light: --color-green-700dark: --color-green-400d |
--content-success-accent | #29a575 | #4ade80 | house | light: --color-green-500dark: --color-green-400d |
--content-warning | #845714 | #fbbf24 | house | light: --color-yellow-700dark: --color-yellow-400d |
--content-warning-accent | #ec9c24 | #fbbf24 | house | light: --color-yellow-500dark: --color-yellow-400d |
--edge-default | #d9dde7 | #383838 | house | light: --color-gray-200dark: --color-neutral-750 |
--edge-error | #edb8c1 | #701322 | house | light: --color-red-200dark: --color-red-700 |
--edge-info | #8ebffb | #102984 | house | light: --color-azure-200dark: --color-azure-700 |
--edge-muted | #f5f7f9 | #252525 | house | light: --color-gray-100dark: --color-neutral-825 |
--edge-success | #bbe2d3 | #175c41 | house | light: --color-green-200dark: --color-green-700 |
--edge-warning | #f9dfb9 | #845714 | house | light: --color-yellow-200dark: --color-yellow-700 |
--link | #4a6ae8 | #60a5fa | house | light: --color-azure-400dark: --color-azure-350d |
--link-hover | #1b44da | #4a6ae8 | house | light: --color-azure-500dark: --color-azure-400 |
--span-background-jobs | #5aba93 | #7ed4b0 | house | light: declared directly (theme-tokens.css:69) dark: declared directly (theme-tokens.css:127) |
--span-cache | #67789e | #97aaf2 | house | light: declared directly (theme-tokens.css:66) dark: declared directly (theme-tokens.css:124) |
--span-database-orm | #eb84a6 | #f5b3cc | house | light: declared directly (theme-tokens.css:65) dark: declared directly (theme-tokens.css:123) |
--span-http | #177373 | #45c9b5 | house | light: declared directly (theme-tokens.css:67) dark: declared directly (theme-tokens.css:125) |
--span-others | #711323 | #e18896 | house | light: declared directly (theme-tokens.css:70) dark: declared directly (theme-tokens.css:128) |
--span-templating | #618ccc | #91b8f0 | house | light: declared directly (theme-tokens.css:68) dark: declared directly (theme-tokens.css:126) |
--span-web-server | #1b44da | #7b9ff5 | house | light: declared directly (theme-tokens.css:64) dark: declared directly (theme-tokens.css:122) |
--surface-canvas | #f5f7f9 | #141414 | house | light: --color-gray-100dark: --color-neutral-950 |
--surface-default | #ffffff | #1e1e1e | house | light: --color-whitedark: --color-neutral-850 |
--surface-error | #fbedef | #440c15 | house | light: --color-red-100dark: --color-red-800 |
--surface-highlight | #eff3fa | #141414 | house | light: --color-blue-100dark: --color-azure-950 |
--surface-info | #e4e9fc | #0b1b56 | house | light: --color-azure-100dark: --color-azure-800 |
--surface-raised | #f5f7f9 | #141414 | house | light: --color-gray-100dark: --color-neutral-950 |
--surface-success | #eef8f4 | #0e3828 | house | light: --color-green-100dark: --color-green-800 |
--surface-warning | #fdf7ed | #50350c | house | light: --color-yellow-100dark: --color-yellow-800 |
Contrast
These tables say which text and edge colours may sit on which surface, measured against WCAG 2.1. Every foreground is paired with every surface, so a row does not mean the product uses that pairing. It means the pairing is allowed.
Read a cell as the contrast ratio followed by the use it is good for:
| verdict | meaning |
|---|---|
| AAA | passes for any text, including at the stricter AAA level |
| AA | passes for text of any size |
| large only | passes only for text at least 24px, or at least 18.66px and bold |
| fails | not allowed for text |
| passes | allowed for a boundary or focus indicator (non-text, 3:1) |
Most text in the product is smaller than the large-text threshold, so treat "large only" as a fail unless the text is a heading at that size. WCAG exempts placeholder text and disabled controls from these floors, so a muted colour that fails here can still be correct for those.
The non-text floor applies only to a boundary a reader needs in order to find a control, such as the border of an input. A divider or a card outline that only decorates is exempt, so an edge that fails here is not wrong by itself. It is wrong when it is the only thing showing where a control is.
The ratios are computed from the resolved token values on every build. They cover the semantic tokens only. A primitive colour used directly, such as a grey border, is not in these tables.
Text on surfaces, light
13 × 8 pairings, light theme. Floor 4.5:1 for normal text, 3:1 for large text. 16 fail even as large text.
| text | surface-canvas | surface-default | surface-error | surface-highlight | surface-info | surface-raised | surface-success | surface-warning |
|---|---|---|---|---|---|---|---|---|
content-alert | 6.21 AA | 6.67 AA | 5.86 AA | 5.99 AA | 5.51 AA | 6.21 AA | 6.15 AA | 6.26 AA |
content-default | 12.15 AAA | 13.05 AAA | 11.47 AAA | 11.72 AAA | 10.78 AAA | 12.15 AAA | 12.03 AAA | 12.24 AAA |
content-error | 5.21 AA | 5.59 AA | 4.92 AA | 5.02 AA | 4.62 AA | 5.21 AA | 5.16 AA | 5.25 AA |
content-info | 11.69 AAA | 12.55 AAA | 11.03 AAA | 11.28 AAA | 10.38 AAA | 11.69 AAA | 11.58 AAA | 11.78 AAA |
content-muted | 4.11 large only | 4.42 large only | 3.88 large only | 3.97 large only | 3.65 large only | 4.11 large only | 4.07 large only | 4.14 large only |
content-secondary | 7.16 AAA | 7.69 AAA | 6.76 AA | 6.91 AA | 6.36 AA | 7.16 AAA | 7.09 AAA | 7.22 AAA |
content-subtle | 3.37 large only | 3.62 large only | 3.18 large only | 3.26 large only | 2.99 fails | 3.37 large only | 3.34 large only | 3.40 large only |
content-success | 7.39 AAA | 7.94 AAA | 6.98 AA | 7.13 AAA | 6.56 AA | 7.39 AAA | 7.32 AAA | 7.45 AAA |
content-success-accent | 2.90 fails | 3.11 large only | 2.74 fails | 2.80 fails | 2.57 fails | 2.90 fails | 2.87 fails | 2.92 fails |
content-warning | 5.82 AA | 6.26 AA | 5.50 AA | 5.62 AA | 5.17 AA | 5.82 AA | 5.77 AA | 5.87 AA |
content-warning-accent | 2.09 fails | 2.24 fails | 1.97 fails | 2.01 fails | 1.85 fails | 2.09 fails | 2.07 fails | 2.10 fails |
link | 4.32 large only | 4.64 AA | 4.08 large only | 4.17 large only | 3.84 large only | 4.32 large only | 4.28 large only | 4.36 large only |
link-hover | 6.77 AA | 7.27 AAA | 6.39 AA | 6.53 AA | 6.01 AA | 6.77 AA | 6.71 AA | 6.82 AA |
Text on surfaces, dark
13 × 8 pairings, dark theme. Floor 4.5:1 for normal text, 3:1 for large text. 4 fail even as large text.
| text | surface-canvas | surface-default | surface-error | surface-highlight | surface-info | surface-raised | surface-success | surface-warning |
|---|---|---|---|---|---|---|---|---|
content-alert | 6.97 AA | 6.30 AA | 6.07 AA | 6.97 AA | 6.08 AA | 6.97 AA | 4.92 AA | 4.28 large only |
content-default | 14.62 AAA | 13.23 AAA | 12.73 AAA | 14.62 AAA | 12.77 AAA | 14.62 AAA | 10.32 AAA | 8.98 AAA |
content-error | 6.66 AA | 6.02 AA | 5.79 AA | 6.66 AA | 5.81 AA | 6.66 AA | 4.70 AA | 4.09 large only |
content-info | 7.24 AAA | 6.55 AA | 6.30 AA | 7.24 AAA | 6.32 AA | 7.24 AAA | 5.11 AA | 4.45 large only |
content-muted | 5.40 AA | 4.89 AA | 4.70 AA | 5.40 AA | 4.72 AA | 5.40 AA | 3.81 large only | 3.32 large only |
content-secondary | 7.30 AAA | 6.60 AA | 6.35 AA | 7.30 AAA | 6.37 AA | 7.30 AAA | 5.15 AA | 4.48 large only |
content-subtle | 3.88 large only | 3.51 large only | 3.38 large only | 3.88 large only | 3.39 large only | 3.88 large only | 2.74 fails | 2.38 fails |
content-success | 10.57 AAA | 9.56 AAA | 9.20 AAA | 10.57 AAA | 9.23 AAA | 10.57 AAA | 7.46 AAA | 6.49 AA |
content-success-accent | 10.57 AAA | 9.56 AAA | 9.20 AAA | 10.57 AAA | 9.23 AAA | 10.57 AAA | 7.46 AAA | 6.49 AA |
content-warning | 11.03 AAA | 9.98 AAA | 9.60 AAA | 11.03 AAA | 9.63 AAA | 11.03 AAA | 7.78 AAA | 6.77 AA |
content-warning-accent | 11.03 AAA | 9.98 AAA | 9.60 AAA | 11.03 AAA | 9.63 AAA | 11.03 AAA | 7.78 AAA | 6.77 AA |
link | 7.24 AAA | 6.55 AA | 6.30 AA | 7.24 AAA | 6.32 AA | 7.24 AAA | 5.11 AA | 4.45 large only |
link-hover | 3.96 large only | 3.58 large only | 3.45 large only | 3.96 large only | 3.46 large only | 3.96 large only | 2.79 fails | 2.43 fails |
Edges on surfaces, light
6 × 8 pairings, light theme. Floor 3:1 for non-text. 48 fail.
| non-text | surface-canvas | surface-default | surface-error | surface-highlight | surface-info | surface-raised | surface-success | surface-warning |
|---|---|---|---|---|---|---|---|---|
edge-default | 1.26 fails | 1.35 fails | 1.19 fails | 1.22 fails | 1.12 fails | 1.26 fails | 1.25 fails | 1.27 fails |
edge-error | 1.59 fails | 1.71 fails | 1.50 fails | 1.54 fails | 1.41 fails | 1.59 fails | 1.58 fails | 1.61 fails |
edge-info | 1.77 fails | 1.90 fails | 1.67 fails | 1.71 fails | 1.57 fails | 1.77 fails | 1.76 fails | 1.79 fails |
edge-muted | 1.00 fails | 1.07 fails | 1.05 fails | 1.03 fails | 1.12 fails | 1.00 fails | 1.00 fails | 1.00 fails |
edge-success | 1.30 fails | 1.40 fails | 1.23 fails | 1.26 fails | 1.16 fails | 1.30 fails | 1.29 fails | 1.31 fails |
edge-warning | 1.20 fails | 1.28 fails | 1.13 fails | 1.15 fails | 1.06 fails | 1.20 fails | 1.18 fails | 1.21 fails |
Edges on surfaces, dark
6 × 8 pairings, dark theme. Floor 3:1 for non-text. 48 fail.
| non-text | surface-canvas | surface-default | surface-error | surface-highlight | surface-info | surface-raised | surface-success | surface-warning |
|---|---|---|---|---|---|---|---|---|
edge-default | 1.57 fails | 1.42 fails | 1.36 fails | 1.57 fails | 1.37 fails | 1.57 fails | 1.10 fails | 1.03 fails |
edge-error | 1.58 fails | 1.43 fails | 1.37 fails | 1.58 fails | 1.38 fails | 1.58 fails | 1.11 fails | 1.02 fails |
edge-info | 1.46 fails | 1.32 fails | 1.27 fails | 1.46 fails | 1.28 fails | 1.46 fails | 1.03 fails | 1.10 fails |
edge-muted | 1.20 fails | 1.08 fails | 1.04 fails | 1.20 fails | 1.04 fails | 1.20 fails | 1.17 fails | 1.35 fails |
edge-success | 2.31 fails | 2.09 fails | 2.01 fails | 2.31 fails | 2.02 fails | 2.31 fails | 1.63 fails | 1.42 fails |
edge-warning | 2.94 fails | 2.66 fails | 2.56 fails | 2.94 fails | 2.56 fails | 2.94 fails | 2.07 fails | 1.80 fails |
For a lint check
The same data is published as JSON at /data/contrast.json, with one entry per pairing and a pass or fail for each floor in both themes. A check in the product repository can fetch it and fail a pull request on a disallowed pairing.