opsinjs
FoundationsToken familiesColour

Colour tokens

The generated reference for every colour token. It carries the naming grammar, both themes, both gamuts, and the measured pair each one belongs to.

Overview

This page is the list. Everything on it is generated from tokens/color.json by scripts/build-tokens.mts; nothing on it is typed by a person, and pnpm check:generated fails the build if the committed output and a fresh regeneration disagree.

Use it to answer three questions: what is this token called, what does it control, and who else uses it. The third column is what makes a token table a decision aid instead of an inventory. A token nothing consumes is a token that should not exist, and a token six components consume is one you cannot change casually.

If you want the argument rather than the list, start at Colour. If you want to change a value, you are in the wrong pillar: Theming is where changes live.

How it works

The naming grammar

--opsin-<axis>-<name>-<role>          the two clinical axes
--<chrome-role>                       the neutral application palette
--color-<…>                           the Tailwind utility alias, via @theme inline

The clinical axes are always prefixed --opsin-. The neutral palette (--background, --foreground, --card, --muted, --border, --ring, --primary and their foregrounds) is unprefixed, because it follows the convention a shadcn-based codebase already has and there is nothing to be gained by renaming it. Colour roles explains the split.

What is emitted, and in what order

app/tokens.generated.css contains, in this order:

  1. a :root block holding the light theme,
  2. a .dark block holding the dark theme,
  3. @media (color-gamut: p3) blocks for each of the above, escalating chroma only (Gamut and Display-P3),
  4. the preference-query overrides for prefers-reduced-transparency and prefers-reduced-motion, which belong to other foundations.

app/globals.css imports that file at a fixed position between the vendor theme blocks and @layer base, and the product theme in app/product.css imports the same file. One source of truth, two shells.

Reading a resolved value

--opsin-status-urgent-surfacereading…

A swatch shows the token's resolved value in both themes and the pair it was measured in. A single contrast number attached to one colour would be meaningless. Contrast is a property of a pair, which is why every figure on this site names both halves.

Using it

In Tailwind, use the utility alias: bg-status-attention-surface, text-status-attention-ink, border-category-heart. The aliases are declared with @theme inline, so they resolve the custom property at use time and therefore follow the theme, the gamut escalation and every preference override automatically. @theme without inline would bake the value in at build time and none of that would work; the trap is documented in Tailwind v4.

Outside Tailwind, use var(--opsin-status-attention-surface) directly. Nothing in the token layer depends on Tailwind. The layer is plain custom properties in a plain stylesheet, which is the reason a non-Tailwind consumer is a supported case rather than a workaround. See CSS variables.

Never inline a resolved value. A token exists so that a theme change reaches every consumer. A copied oklch() is a value that will be correct until the first time somebody changes the theme, at which point it becomes a bug nobody can grep for. The lint rules described in the Handbook catch this before review does.

Do not compose a new colour from two tokens. Mixing, fading or overlaying tokens produces a colour that is in no measured pair. If you need a lighter variant, the answer is a different role or a material rung, not color-mix().

Tokens

TokenWhat it controlsUsed by
--opsin-neutral-0oklch(1 0 0)Neutral ramp, step 0.no component
--opsin-neutral-50oklch(0.985 0.002 250)Neutral ramp, step 50.no component
--opsin-neutral-100oklch(0.967 0.003 250)Neutral ramp, step 100.no component
--opsin-neutral-200oklch(0.929 0.004 250)Neutral ramp, step 200.no component
--opsin-neutral-300oklch(0.871 0.005 250)Neutral ramp, step 300.no component
--opsin-neutral-400oklch(0.708 0.008 250)Neutral ramp, step 400.no component
--opsin-neutral-500oklch(0.556 0.009 250)Neutral ramp, step 500.no component
--opsin-neutral-600oklch(0.439 0.009 250)Neutral ramp, step 600.no component
--opsin-neutral-700oklch(0.371 0.008 250)Neutral ramp, step 700.no component
--opsin-neutral-800oklch(0.269 0.007 250)Neutral ramp, step 800.no component
--opsin-neutral-900oklch(0.205 0.006 250)Neutral ramp, step 900.no component
--opsin-neutral-950oklch(0.145 0.005 250)Neutral ramp, step 950.no component
--opsin-neutral-1000oklch(0 0 0)Neutral ramp, step 1000.no component
--opsin-chrome-backgroundoklch(0.985 0.002 250)Chrome role background, resolved to the ladder step it names.no component
--opsin-chrome-foregroundoklch(0.205 0.006 250)Chrome role foreground, resolved to the ladder step it names.no component
--opsin-chrome-cardoklch(1 0 0)Chrome role card, resolved to the ladder step it names.no component
--opsin-chrome-card-foregroundoklch(0.205 0.006 250)Chrome role cardForeground, resolved to the ladder step it names.no component
--opsin-chrome-mutedoklch(0.967 0.003 250)Chrome role muted, resolved to the ladder step it names.no component
--opsin-chrome-muted-foregroundoklch(0.439 0.009 250)Chrome role mutedForeground, resolved to the ladder step it names.no component
--opsin-chrome-borderoklch(0.556 0.009 250)Chrome role border, resolved to the ladder step it names.no component
--opsin-chrome-inputoklch(0.556 0.009 250)Chrome role input, resolved to the ladder step it names.no component
--opsin-chrome-ringoklch(0.55 0.13 250)Chrome role ring, resolved to the ladder step it names.no component
--opsin-chrome-primaryoklch(0.51 0.135 250)Chrome role primary, resolved to the ladder step it names.no component
--opsin-chrome-primary-foregroundoklch(0.99 0 0)Chrome role primaryForeground, resolved to the ladder step it names.no component
--opsin-category-sleep-50oklch(0.972 0.013 275)Sleep ramp, step 50.no component
--opsin-category-sleep-100oklch(0.941 0.028 275)Sleep ramp, step 100.no component
--opsin-category-sleep-200oklch(0.884 0.057 275)Sleep ramp, step 200.no component
--opsin-category-sleep-300oklch(0.806 0.092 275)Sleep ramp, step 300.no component
--opsin-category-sleep-400oklch(0.714 0.123 275)Sleep ramp, step 400.no component
--opsin-category-sleep-500oklch(0.622 0.14 275)Sleep ramp, step 500.no component
--opsin-category-sleep-600oklch(0.541 0.136 275)Sleep ramp, step 600.no component
--opsin-category-sleep-700oklch(0.452 0.123 275)Sleep ramp, step 700.no component
--opsin-category-sleep-800oklch(0.362 0.104 275)Sleep ramp, step 800.no component
--opsin-category-sleep-900oklch(0.276 0.081 275)Sleep ramp, step 900.no component
--opsin-category-sleep-950oklch(0.208 0.062 275)Sleep ramp, step 950.no component
--opsin-category-sleep-surfaceoklch(0.972 0.013 275)The tinted background a component in this ramp sits on.no component
--opsin-category-sleep-lineoklch(0.541 0.136 275)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-sleep-inkoklch(0.362 0.104 275)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-sleep-accentoklch(0.58 0.14 275)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-category-heart-50oklch(0.972 0.014 15)Heart ramp, step 50.no component
--opsin-category-heart-100oklch(0.941 0.03 15)Heart ramp, step 100.no component
--opsin-category-heart-200oklch(0.884 0.062 15)Heart ramp, step 200.no component
--opsin-category-heart-300oklch(0.806 0.112 15)Heart ramp, step 300.no component
--opsin-category-heart-400oklch(0.714 0.15 15)Heart ramp, step 400.no component
--opsin-category-heart-500oklch(0.622 0.17 15)Heart ramp, step 500.no component
--opsin-category-heart-600oklch(0.541 0.165 15)Heart ramp, step 600.no component
--opsin-category-heart-700oklch(0.452 0.15 15)Heart ramp, step 700.no component
--opsin-category-heart-800oklch(0.362 0.126 15)Heart ramp, step 800.no component
--opsin-category-heart-900oklch(0.276 0.099 15)Heart ramp, step 900.no component
--opsin-category-heart-950oklch(0.208 0.075 15)Heart ramp, step 950.no component
--opsin-category-heart-surfaceoklch(0.972 0.014 15)The tinted background a component in this ramp sits on.no component
--opsin-category-heart-lineoklch(0.541 0.165 15)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-heart-inkoklch(0.362 0.126 15)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-heart-accentoklch(0.62 0.17 15)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-category-activity-50oklch(0.972 0.022 140)Activity ramp, step 50.no component
--opsin-category-activity-100oklch(0.941 0.038 140)Activity ramp, step 100.no component
--opsin-category-activity-200oklch(0.884 0.067 140)Activity ramp, step 200.no component
--opsin-category-activity-300oklch(0.806 0.106 140)Activity ramp, step 300.no component
--opsin-category-activity-400oklch(0.714 0.141 140)Activity ramp, step 400.no component
--opsin-category-activity-500oklch(0.622 0.16 140)Activity ramp, step 500.no component
--opsin-category-activity-600oklch(0.541 0.155 140)Activity ramp, step 600.no component
--opsin-category-activity-700oklch(0.452 0.141 140)Activity ramp, step 700.no component
--opsin-category-activity-800oklch(0.362 0.118 140)Activity ramp, step 800.no component
--opsin-category-activity-900oklch(0.276 0.092 140)Activity ramp, step 900.no component
--opsin-category-activity-950oklch(0.208 0.07 140)Activity ramp, step 950.no component
--opsin-category-activity-surfaceoklch(0.972 0.022 140)The tinted background a component in this ramp sits on.no component
--opsin-category-activity-lineoklch(0.541 0.155 140)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-activity-inkoklch(0.362 0.118 140)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-activity-accentoklch(0.7 0.16 140)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-category-nutrition-50oklch(0.972 0.02 70)Nutrition ramp, step 50.no component
--opsin-category-nutrition-100oklch(0.941 0.034 70)Nutrition ramp, step 100.no component
--opsin-category-nutrition-200oklch(0.884 0.059 70)Nutrition ramp, step 200.no component
--opsin-category-nutrition-300oklch(0.806 0.092 70)Nutrition ramp, step 300.no component
--opsin-category-nutrition-400oklch(0.714 0.123 70)Nutrition ramp, step 400.no component
--opsin-category-nutrition-500oklch(0.622 0.135 70)Nutrition ramp, step 500.no component
--opsin-category-nutrition-600oklch(0.541 0.118 70)Nutrition ramp, step 600.no component
--opsin-category-nutrition-700oklch(0.452 0.099 70)Nutrition ramp, step 700.no component
--opsin-category-nutrition-800oklch(0.362 0.08 70)Nutrition ramp, step 800.no component
--opsin-category-nutrition-900oklch(0.276 0.062 70)Nutrition ramp, step 900.no component
--opsin-category-nutrition-950oklch(0.208 0.049 70)Nutrition ramp, step 950.no component
--opsin-category-nutrition-surfaceoklch(0.972 0.02 70)The tinted background a component in this ramp sits on.no component
--opsin-category-nutrition-lineoklch(0.541 0.118 70)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-nutrition-inkoklch(0.362 0.08 70)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-nutrition-accentoklch(0.75 0.14 70)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-category-mind-50oklch(0.972 0.018 310)Mind ramp, step 50.no component
--opsin-category-mind-100oklch(0.941 0.034 310)Mind ramp, step 100.no component
--opsin-category-mind-200oklch(0.884 0.059 310)Mind ramp, step 200.no component
--opsin-category-mind-300oklch(0.806 0.092 310)Mind ramp, step 300.no component
--opsin-category-mind-400oklch(0.714 0.123 310)Mind ramp, step 400.no component
--opsin-category-mind-500oklch(0.622 0.14 310)Mind ramp, step 500.no component
--opsin-category-mind-600oklch(0.541 0.136 310)Mind ramp, step 600.no component
--opsin-category-mind-700oklch(0.452 0.123 310)Mind ramp, step 700.no component
--opsin-category-mind-800oklch(0.362 0.104 310)Mind ramp, step 800.no component
--opsin-category-mind-900oklch(0.276 0.081 310)Mind ramp, step 900.no component
--opsin-category-mind-950oklch(0.208 0.062 310)Mind ramp, step 950.no component
--opsin-category-mind-surfaceoklch(0.972 0.018 310)The tinted background a component in this ramp sits on.no component
--opsin-category-mind-lineoklch(0.541 0.136 310)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-mind-inkoklch(0.362 0.104 310)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-mind-accentoklch(0.62 0.14 310)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-category-labs-50oklch(0.972 0.015 210)Labs ramp, step 50.no component
--opsin-category-labs-100oklch(0.941 0.026 210)Labs ramp, step 100.no component
--opsin-category-labs-200oklch(0.884 0.046 210)Labs ramp, step 200.no component
--opsin-category-labs-300oklch(0.806 0.073 210)Labs ramp, step 300.no component
--opsin-category-labs-400oklch(0.714 0.097 210)Labs ramp, step 400.no component
--opsin-category-labs-500oklch(0.622 0.108 210)Labs ramp, step 500.no component
--opsin-category-labs-600oklch(0.541 0.094 210)Labs ramp, step 600.no component
--opsin-category-labs-700oklch(0.452 0.079 210)Labs ramp, step 700.no component
--opsin-category-labs-800oklch(0.362 0.063 210)Labs ramp, step 800.no component
--opsin-category-labs-900oklch(0.276 0.049 210)Labs ramp, step 900.no component
--opsin-category-labs-950oklch(0.208 0.038 210)Labs ramp, step 950.no component
--opsin-category-labs-surfaceoklch(0.972 0.015 210)The tinted background a component in this ramp sits on.no component
--opsin-category-labs-lineoklch(0.541 0.094 210)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.trend-sparkline
--opsin-category-labs-inkoklch(0.362 0.063 210)Text and text-sized icons on `surface`. Clears the text floor against `surface`.metric-tile, range-bar, result-card, score-dial
--opsin-category-labs-accentoklch(0.6 0.11 210)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.log-sheet
--opsin-status-steady-50oklch(0.972 0.015 178)Steady ramp, step 50.no component
--opsin-status-steady-100oklch(0.941 0.026 178)Steady ramp, step 100.no component
--opsin-status-steady-200oklch(0.884 0.046 178)Steady ramp, step 200.no component
--opsin-status-steady-300oklch(0.806 0.073 178)Steady ramp, step 300.no component
--opsin-status-steady-400oklch(0.714 0.097 178)Steady ramp, step 400.no component
--opsin-status-steady-500oklch(0.622 0.11 178)Steady ramp, step 500.no component
--opsin-status-steady-600oklch(0.541 0.1 178)Steady ramp, step 600.no component
--opsin-status-steady-700oklch(0.452 0.084 178)Steady ramp, step 700.no component
--opsin-status-steady-800oklch(0.362 0.067 178)Steady ramp, step 800.no component
--opsin-status-steady-900oklch(0.276 0.052 178)Steady ramp, step 900.no component
--opsin-status-steady-950oklch(0.208 0.041 178)Steady ramp, step 950.no component
--opsin-status-steady-surfaceoklch(0.972 0.015 178)The tinted background a component in this ramp sits on.alert-banner, status-pill
--opsin-status-steady-lineoklch(0.541 0.1 178)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.alert-banner, range-bar, range-legend, status-pill
--opsin-status-steady-inkoklch(0.362 0.067 178)Text and text-sized icons on `surface`. Clears the text floor against `surface`.alert-banner, score-dial, status-pill
--opsin-status-steady-accentoklch(0.62 0.11 178)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.score-dial
--opsin-status-watch-50oklch(0.972 0.02 82)Watch ramp, step 50.no component
--opsin-status-watch-100oklch(0.941 0.034 82)Watch ramp, step 100.no component
--opsin-status-watch-200oklch(0.884 0.059 82)Watch ramp, step 200.no component
--opsin-status-watch-300oklch(0.806 0.092 82)Watch ramp, step 300.no component
--opsin-status-watch-400oklch(0.714 0.123 82)Watch ramp, step 400.no component
--opsin-status-watch-500oklch(0.622 0.129 82)Watch ramp, step 500.no component
--opsin-status-watch-600oklch(0.541 0.112 82)Watch ramp, step 600.no component
--opsin-status-watch-700oklch(0.452 0.094 82)Watch ramp, step 700.no component
--opsin-status-watch-800oklch(0.362 0.076 82)Watch ramp, step 800.no component
--opsin-status-watch-900oklch(0.276 0.059 82)Watch ramp, step 900.no component
--opsin-status-watch-950oklch(0.208 0.046 82)Watch ramp, step 950.no component
--opsin-status-watch-surfaceoklch(0.972 0.02 82)The tinted background a component in this ramp sits on.alert-banner, status-pill
--opsin-status-watch-lineoklch(0.541 0.112 82)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.alert-banner, range-bar, range-legend, status-pill
--opsin-status-watch-inkoklch(0.362 0.076 82)Text and text-sized icons on `surface`. Clears the text floor against `surface`.alert-banner, score-dial, status-pill
--opsin-status-watch-accentoklch(0.60 0.123 82)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.score-dial
--opsin-status-attention-50oklch(0.972 0.015 45)Needs attention ramp, step 50.no component
--opsin-status-attention-100oklch(0.941 0.032 45)Needs attention ramp, step 100.no component
--opsin-status-attention-200oklch(0.884 0.063 45)Needs attention ramp, step 200.no component
--opsin-status-attention-300oklch(0.806 0.099 45)Needs attention ramp, step 300.no component
--opsin-status-attention-400oklch(0.714 0.132 45)Needs attention ramp, step 400.no component
--opsin-status-attention-500oklch(0.622 0.15 45)Needs attention ramp, step 500.no component
--opsin-status-attention-600oklch(0.541 0.145 45)Needs attention ramp, step 600.no component
--opsin-status-attention-700oklch(0.452 0.13 45)Needs attention ramp, step 700.no component
--opsin-status-attention-800oklch(0.362 0.105 45)Needs attention ramp, step 800.no component
--opsin-status-attention-900oklch(0.276 0.082 45)Needs attention ramp, step 900.no component
--opsin-status-attention-950oklch(0.208 0.065 45)Needs attention ramp, step 950.no component
--opsin-status-attention-surfaceoklch(0.972 0.015 45)The tinted background a component in this ramp sits on.alert-banner, status-pill
--opsin-status-attention-lineoklch(0.541 0.145 45)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.alert-banner, range-bar, range-legend, status-pill
--opsin-status-attention-inkoklch(0.362 0.105 45)Text and text-sized icons on `surface`. Clears the text floor against `surface`.alert-banner, score-dial, status-pill
--opsin-status-attention-accentoklch(0.58 0.15 45)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.score-dial
--opsin-status-urgent-50oklch(0.972 0.014 27)Urgent ramp, step 50.no component
--opsin-status-urgent-100oklch(0.941 0.03 27)Urgent ramp, step 100.no component
--opsin-status-urgent-200oklch(0.884 0.061 27)Urgent ramp, step 200.no component
--opsin-status-urgent-300oklch(0.806 0.11 27)Urgent ramp, step 300.no component
--opsin-status-urgent-400oklch(0.714 0.176 27)Urgent ramp, step 400.no component
--opsin-status-urgent-500oklch(0.622 0.2 27)Urgent ramp, step 500.no component
--opsin-status-urgent-600oklch(0.541 0.194 27)Urgent ramp, step 600.no component
--opsin-status-urgent-700oklch(0.452 0.176 27)Urgent ramp, step 700.no component
--opsin-status-urgent-800oklch(0.362 0.148 27)Urgent ramp, step 800.no component
--opsin-status-urgent-900oklch(0.276 0.115 27)Urgent ramp, step 900.no component
--opsin-status-urgent-950oklch(0.208 0.088 27)Urgent ramp, step 950.no component
--opsin-status-urgent-surfaceoklch(0.972 0.014 27)The tinted background a component in this ramp sits on.alert-banner, status-pill
--opsin-status-urgent-lineoklch(0.541 0.194 27)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.alert-banner, range-bar, range-legend, status-pill
--opsin-status-urgent-inkoklch(0.362 0.148 27)Text and text-sized icons on `surface`. Clears the text floor against `surface`.alert-banner, score-dial, status-pill
--opsin-status-urgent-accentoklch(0.52 0.2 27)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.score-dial
--opsin-status-unknown-50oklch(0.972 0.003 250)Unknown ramp, step 50.no component
--opsin-status-unknown-100oklch(0.941 0.005 250)Unknown ramp, step 100.no component
--opsin-status-unknown-200oklch(0.884 0.008 250)Unknown ramp, step 200.no component
--opsin-status-unknown-300oklch(0.806 0.013 250)Unknown ramp, step 300.no component
--opsin-status-unknown-400oklch(0.714 0.018 250)Unknown ramp, step 400.no component
--opsin-status-unknown-500oklch(0.622 0.02 250)Unknown ramp, step 500.no component
--opsin-status-unknown-600oklch(0.541 0.019 250)Unknown ramp, step 600.no component
--opsin-status-unknown-700oklch(0.452 0.018 250)Unknown ramp, step 700.no component
--opsin-status-unknown-800oklch(0.362 0.015 250)Unknown ramp, step 800.no component
--opsin-status-unknown-900oklch(0.276 0.012 250)Unknown ramp, step 900.no component
--opsin-status-unknown-950oklch(0.208 0.009 250)Unknown ramp, step 950.no component
--opsin-status-unknown-surfaceoklch(0.972 0.003 250)The tinted background a component in this ramp sits on.no component
--opsin-status-unknown-lineoklch(0.541 0.019 250)The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`.no component
--opsin-status-unknown-inkoklch(0.362 0.015 250)Text and text-sized icons on `surface`. Clears the text floor against `surface`.no component
--opsin-status-unknown-accentoklch(0.72 0.02 250)The identity fill: a bar fill, a dial track, a legend dot. Chosen for recognition, not for contrast. It must be bounded by `line` or labelled in `ink`; it is never the only thing that carries the meaning.no component

Accessibility impact

Every row above belongs to a measured pair, and the measurements are published rather than asserted. scripts/check-contrast.mts groups its output by token scope rather than by source file, so the colour namespace arrives as three tables rather than one.

The status stems, which are the pairs a reader's safety depends on:

PairThemeAPCA LcWCAG 2.2Floor
attention ink on attention surfacelight89.410.35:1Pass
attention ink on attention surfacedark-81.112.54:1Pass
attention line on attention surfacelight70.04.90:1Pass
attention line on attention surfacedark-65.59.54:1Pass
attention accent on attention surfacelight65.14.18:1Pass
attention accent on attention surfacedark-49.46.80:1Pass
attention ink on the attention bandlight22.42.48:1Below floor
attention ink on the attention banddark-31.01.84:1Below floor
attention accent on the pagelight67.84.35:1Pass
attention accent on the pagedark-50.57.47:1Pass
attention ink on the pagelight92.210.77:1Pass
attention ink on the pagedark-82.313.76:1Pass
steady ink on steady surfacelight88.39.62:1Pass
steady ink on steady surfacedark-82.912.56:1Pass
steady line on steady surfacelight67.54.45:1Pass
steady line on steady surfacedark-68.99.91:1Pass
steady accent on steady surfacelight56.43.21:1Pass
steady accent on steady surfacedark-53.27.25:1Pass
steady ink on the steady bandlight30.03.00:1Below floor
steady ink on the steady banddark-28.81.73:1Below floor
steady accent on the pagelight58.63.31:1Pass
steady accent on the pagedark-54.58.19:1Pass
steady ink on the pagelight90.69.94:1Pass
steady ink on the pagedark-84.214.20:1Pass
unknown ink on unknown surfacelight89.59.98:1Pass
unknown ink on unknown surfacedark-82.212.56:1Pass
unknown line on unknown surfacelight69.24.64:1Pass
unknown line on unknown surfacedark-67.29.72:1Pass
unknown accent on unknown surfacelight43.12.29:1Below floor
unknown accent on unknown surfacedark-51.37.05:1Pass
unknown accent on the pagelight45.62.37:1Below floor
unknown accent on the pagedark-52.37.84:1Pass
unknown ink on the pagelight92.010.34:1Pass
unknown ink on the pagedark-83.113.97:1Pass
urgent ink on urgent surfacelight88.210.58:1Pass
urgent ink on urgent surfacedark-80.312.52:1Pass
urgent line on urgent surfacelight70.05.09:1Pass
urgent line on urgent surfacedark-64.89.52:1Pass
urgent accent on urgent surfacelight72.35.58:1Pass
urgent accent on urgent surfacedark-48.06.63:1Pass
urgent ink on the urgent bandlight13.91.90:1Below floor
urgent ink on the urgent banddark-31.51.89:1Below floor
urgent accent on the pagelight75.35.84:1Pass
urgent accent on the pagedark-49.57.24:1Pass
urgent ink on the pagelight91.211.06:1Pass
urgent ink on the pagedark-81.813.66:1Pass
watch ink on watch surfacelight89.410.02:1Pass
watch ink on watch surfacedark-82.112.58:1Pass
watch line on watch surfacelight69.54.72:1Pass
watch line on watch surfacedark-66.89.68:1Pass
watch accent on watch surfacelight61.73.72:1Pass
watch accent on watch surfacedark-50.76.95:1Pass
watch ink on the watch bandlight25.72.70:1Below floor
watch ink on the watch banddark-30.61.81:1Below floor
watch accent on the pagelight64.23.85:1Pass
watch accent on the pagedark-51.77.71:1Pass
watch ink on the pagelight91.910.38:1Pass
watch ink on the pagedark-83.113.95:1Pass
attention surface on the card materiallight0.01.09:1Below floor
attention surface on the card materialdark0.01.00:1Below floor
steady surface on the card materiallight0.01.08:1Below floor
steady surface on the card materialdark0.01.03:1Below floor
urgent surface on the card materiallight0.01.09:1Below floor
urgent surface on the card materialdark0.01.01:1Below floor
watch surface on the card materiallight0.01.08:1Below floor
watch surface on the card materialdark0.01.01:1Below floor
the focus ring on the attention surfacelight67.34.44:1Pass
the focus ring on the attention surfacedark-45.96.30:1Pass
attention ink on the neutral hover filllight88.610.23:1Pass
attention ink on the neutral hover filldark-79.310.53:1Pass
the focus ring on the steady surfacelight67.84.47:1Pass
the focus ring on the steady surfacedark-45.86.12:1Pass
steady ink on the neutral hover filllight87.09.43:1Pass
steady ink on the neutral hover filldark-81.310.86:1Pass
the focus ring on the urgent surfacelight67.04.42:1Pass
the focus ring on the urgent surfacedark-45.56.34:1Pass
urgent ink on the neutral hover filllight87.710.50:1Pass
urgent ink on the neutral hover filldark-78.910.46:1Pass
the focus ring on the watch surfacelight67.64.46:1Pass
the focus ring on the watch surfacedark-46.06.24:1Pass
watch ink on the neutral hover filllight88.39.86:1Pass
watch ink on the neutral hover filldark-80.110.67:1Pass

The six category ramps, whose -line and -ink roles carry the identity marks:

PairThemeAPCA LcWCAG 2.2Floor
activity ink on activity surfacelight88.59.61:1Pass
activity ink on activity surfacedark-83.112.58:1Pass
activity line on activity surfacelight67.64.42:1Pass
activity line on activity surfacedark-69.09.93:1Pass
activity accent on activity surfacelight44.12.34:1Below floor
activity accent on activity surfacedark-53.37.26:1Pass
activity accent on the pagelight46.12.41:1Below floor
activity accent on the pagedark-54.68.22:1Pass
activity ink on the pagelight90.59.90:1Pass
activity ink on the pagedark-84.414.24:1Pass
activity line on the bandlight66.14.32:1Pass
activity line on the banddark-67.48.60:1Pass
heart ink on heart surfacelight89.110.51:1Pass
heart ink on heart surfacedark-80.512.55:1Pass
heart line on heart surfacelight70.45.05:1Pass
heart line on heart surfacedark-64.59.46:1Pass
heart accent on heart surfacelight60.33.64:1Pass
heart accent on heart surfacedark-48.26.68:1Pass
heart accent on the pagelight63.23.81:1Pass
heart accent on the pagedark-49.57.27:1Pass
heart ink on the pagelight92.010.97:1Pass
heart ink on the pagedark-81.813.65:1Pass
heart line on the bandlight69.95.01:1Pass
heart line on the banddark-62.87.88:1Pass
labs ink on labs surfacelight88.89.78:1Pass
labs ink on labs surfacedark-83.112.69:1Pass
labs line on labs surfacelight67.94.51:1Pass
labs line on labs surfacedark-68.49.90:1Pass
labs accent on labs surfacelight59.33.49:1Pass
labs accent on labs surfacedark-52.77.21:1Pass
labs accent on the pagelight61.53.60:1Pass
labs accent on the pagedark-53.98.07:1Pass
labs ink on the pagelight90.910.09:1Pass
labs ink on the pagedark-84.314.22:1Pass
labs line on the bandlight66.54.42:1Pass
labs line on the banddark-66.78.49:1Pass
mind ink on mind surfacelight89.910.36:1Pass
mind ink on mind surfacedark-81.312.56:1Pass
mind line on mind surfacelight70.74.94:1Pass
mind line on mind surfacedark-65.49.54:1Pass
mind accent on mind surfacelight60.03.55:1Pass
mind accent on mind surfacedark-49.16.77:1Pass
mind accent on the pagelight62.83.70:1Pass
mind accent on the pagedark-50.07.42:1Pass
mind ink on the pagelight92.710.80:1Pass
mind ink on the pagedark-82.213.77:1Pass
mind line on the bandlight70.04.89:1Pass
mind line on the banddark-63.48.00:1Pass
nutrition ink on nutrition surfacelight89.310.06:1Pass
nutrition ink on nutrition surfacedark-81.912.55:1Pass
nutrition line on nutrition surfacelight69.74.78:1Pass
nutrition line on nutrition surfacedark-66.79.66:1Pass
nutrition accent on nutrition surfacelight39.22.11:1Below floor
nutrition accent on nutrition surfacedark-50.26.88:1Pass
nutrition accent on the pagelight41.82.19:1Below floor
nutrition accent on the pagedark-51.37.63:1Pass
nutrition ink on the pagelight91.910.44:1Pass
nutrition ink on the pagedark-82.913.91:1Pass
nutrition line on the bandlight68.84.71:1Pass
nutrition line on the banddark-64.88.19:1Pass
sleep ink on sleep surfacelight89.610.19:1Pass
sleep ink on sleep surfacedark-81.912.60:1Pass
sleep line on sleep surfacelight70.04.80:1Pass
sleep line on sleep surfacedark-66.49.65:1Pass
sleep accent on sleep surfacelight64.64.05:1Pass
sleep accent on sleep surfacedark-50.46.94:1Pass
sleep accent on the pagelight67.34.22:1Pass
sleep accent on the pagedark-51.47.67:1Pass
sleep ink on the pagelight92.310.60:1Pass
sleep ink on the pagedark-82.913.92:1Pass
sleep line on the bandlight69.14.75:1Pass
sleep line on the banddark-64.58.16:1Pass
activity ink on the card materiallight93.610.34:1Pass
activity ink on the card materialdark-83.512.91:1Pass
heart ink on the card materiallight95.111.47:1Pass
heart ink on the card materialdark-80.912.38:1Pass
labs ink on the card materiallight94.010.54:1Pass
labs ink on the card materialdark-83.512.90:1Pass
mind ink on the card materiallight95.711.28:1Pass
mind ink on the card materialdark-81.412.49:1Pass
nutrition ink on the card materiallight95.010.91:1Pass
nutrition ink on the card materialdark-82.012.61:1Pass
sleep ink on the card materiallight95.411.08:1Pass
sleep ink on the card materialdark-82.112.62:1Pass

And the neutral ramp, which every other pair is eventually measured against:

PairThemeAPCA LcWCAG 2.2Floor
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below floor

Three properties of those tables are load-bearing for accessibility and worth stating explicitly, because a generated table looks like it could not possibly be wrong:

  • They are regenerated on every commit. A hand-edit to the emitted CSS fails pnpm check:generated, so the published values and the shipped values cannot drift apart.
  • They cover both themes. There is no row that is measured in light and assumed in dark.
  • They do not cover pairs you assemble yourself. The declared pair set is enumerated in Colour roles. A combination outside it is unmeasured, and unmeasured is the honest word for it.
  • Colour roles says what surface, line and ink mean, and why a guarantee attaches to a pair.
  • Generated token reference has the same data for the whole system, not just colour.
  • Theming is the pillar where you change any of this.

On this page