# Generated reference on opsinjs
> Every token, CSS variable, measured contrast pair, defined term and exported type, generated from the same sources the build uses.
Pages: 67.
Sections: Reference (67).
60 opsinjs components are implemented and installable; every other component id is a specification or a name reserved so the URL answers. Each page carries its own `status`, and https://opsinjs.pensievelabs.org/r/index.json carries `implemented` per id. Read one of those two before you generate code against any API, and never describe an unimplemented component as shipping.
Elements written as `` in the pages below are opsinjs documentation components. Their attributes are the content; the tables and figures they render are generated from `tokens/*.json` and `registry/catalogue.ts` and are published separately under the Reference section and at https://opsinjs.pensievelabs.org/r/index.json. `` is the exception: it is a paired element, and the prose between its tags is authored text reproduced here word for word, stating whether that component has been reviewed. Read its children.
This shard is measurements, not doctrine. It says what a token resolves to and never what it is for. The page that says what it is for is in the foundations shard.
Foundations and theming: https://opsinjs.pensievelabs.org/llms-foundations.txt
The same data as JSON, per component: `GET /r/index.json`
---
# Reference
Source: https://opsinjs.pensievelabs.org/reference
Markdown: https://opsinjs.pensievelabs.org/reference.md
Section: Reference · kind: reference · also known as: generated reference, what is generated
Three pillars describe the same tokens from three directions, and the split is
deliberate:
> **Foundations = what a token means. Handbook and Theming = how you change it.
> Reference = the generated list of every one.**
If you are reading to understand a decision, you are in the wrong pillar. This
one exists so that a question with a finite answer can be answered by looking
rather than by reading. *How many status levels are there, what does
`data-stale` mean, which variable controls the ladder's third rung* are
questions of exactly that kind.
## How this is generated [#how-this-is-generated]
Nothing on the eight pages below is typed by a person. Each is written by
`scripts/build-reference.mts` from a source that the application itself consumes,
so a row cannot describe a token the build does not ship. `pnpm run check` runs
`pnpm run generate` and then diffs the working tree; a page that has drifted from
its source fails CI rather than quietly lying.
### What is generated, and from what [#what-is-generated-and-from-what]
| Page | Source of truth | Produced by | Refreshed |
| --------------- | -------------------------------------------------- | ----------------------------------------------------- | ----------------------------- |
| Tokens | `tokens/*.json` | `build-tokens.mts` → `lib/generated/tokens.ts` | every build |
| Data attributes | component sources and their registry items | `build-registry.mts` | every build |
| CSS variables | `tokens/*.json` → `app/tokens.generated.css` | `build-tokens.mts` | every build |
| Keyboard | the `` block on each component page | `build-reference.mts` | every build |
| Types | the published `.d.ts` of the `@opsinjs` packages | `build-reference.mts` (fumadocs-typescript) | every build |
| Contrast | `tokens/color.json`, `tokens/material.json` | `check-contrast.mts` → `lib/generated/contrast.json` | nightly, and before a release |
| Glossary | `tokens/glossary.json` | `build-reference.mts` | every build |
| Catalogue | `registry/catalogue.ts` | `build-registry.mts` → `lib/generated/catalogue.json` | every build |
| API, per symbol | the published `.d.ts` of the `@opsinjs` packages | `build-reference.mts` | every build |
### The page contract [#the-page-contract]
Every generated page has the same shape. Above the marker comment
`opsinjs:generated:begin` sits a short hand-written block that names the source
and the script. You are reading it now. Everything below it is replaced
wholesale. Editing below the marker is not a mistake the tooling tolerates: the
next `pnpm run generate` erases it, and `check:generated` fails the pull request
that contained it.
To change a row, change the source. There is no exception to this, and the
absence of an exception is the point: a table that can be hand-edited is a table
you cannot trust when it matters.
### Two pages here are hand-written, on purpose [#two-pages-here-are-hand-written-on-purpose]
[System glossary](./system-glossary.mdx) defines the words this project uses about
itself: rung, axis, role, base, style, preset. There is no machine-readable
source for a project's own vocabulary, and inventing one to satisfy a rule would
be worse than writing twenty definitions carefully. [The API index](./api/index.mdx)
is likewise hand-written; only the per-symbol pages beneath it are generated.
### Empty tables are honest, not broken [#empty-tables-are-honest-not-broken]
Two of the eight lists are empty: [Data attributes](./generated/data-attributes.mdx)
and [Keyboard](./generated/keyboard.mdx). They say so with a ``
block naming the script that will fill them. The reason is a gap in the
aggregator rather than an empty system: the implemented components do emit data
attributes and every component page carries its own ``, but
`build-reference.mts` does not yet read the component sources for the first or
parse those blocks for the second. Each page states that on itself, because
"empty because nothing exists" and "empty because nobody wrote the extractor" are
different problems and only one of them gets fixed by somebody reading this. What
an empty table never means is that the page failed to load, and it is never
padded with a plausible example row. A fake row in a reference table is
indistinguishable from a real one, which is the exact failure this pillar exists
to prevent.
### Where these lists also appear [#where-these-lists-also-appear]
Everything here is duplicated into machine surfaces on purpose: `/llms.txt` and
its shards, the `.md` twin of every page, and `/r/index.json`. Prefer those if
you are a program. If you are a person and the list is long, the search box
(Ctrl K) indexes generated pages exactly like written ones.
That is the whole reason these are committed MDX rather than a runtime API.
---
# API
Source: https://opsinjs.pensievelabs.org/reference/api
Markdown: https://opsinjs.pensievelabs.org/reference/api.md
Section: Reference · kind: reference · also known as: api index, symbol pages, exported symbols
Every `type` and `interface` this site's `lib/` exports has a page here,
addressed by its exact exported identifier:
```text
opsinjs.pensievelabs.org/docs/reference/api/ClinicalStatus
opsinjs.pensievelabs.org/docs/reference/api/HealthCategory
opsinjs.pensievelabs.org/docs/reference/api/ReferenceRange
```
The identifier is used verbatim, with the same case, no kebab-casing and no
pluralisation. That is the whole design: an inline `` in prose, a link
from a props table, and a program guessing a URL from a type name it read in an
error message all arrive at the same page without a lookup table.
## How this is generated [#how-this-is-generated]
**Source:** the `export` declarations under `apps/www/lib/`, which are this
documentation site's own TypeScript rather than a published package. No `@opsinjs` package has ever
been released and, under
[decision 0002](../../project/decisions/0002-shadcn-registry-distribution.mdx),
none is going to be: opsinjs is distributed by copying source into your
repository. So read these pages as the definitions behind the site and the
copied components, never as the API of a module you can install.
**Script:** `scripts/build-reference.mts`, which reads declaration lines
textually rather than through `fumadocs-typescript`, because a reference
generator has to run before anything in the repository compiles.
**Command:** `pnpm run generate`. This index page is hand-written; every page
beneath it is generated, and `assert-ia.mts` fails the build if an ``
anywhere in the corpus names a symbol with no page.
### Why per-symbol pages and not one long page [#why-per-symbol-pages-and-not-one-long-page]
A single API document is the format every generated documentation tool reaches
for and it fails in the same three ways each time. Anchors churn, so a link that
worked last release lands halfway down the wrong section. The page grows past the
point where a browser's find-in-page is useful. And a program that fetches it
gets forty thousand tokens of unrelated types in order to answer one question
about one union. A page per symbol costs one file each and fixes all three, and
the `.md` twin of a symbol page is small enough to paste into a prompt whole.
Only types and interfaces earn one. Exported functions and constants are listed
in [Types](../generated/types.mdx) with the file they are declared in, which is
what you actually want for a function; a page apiece for every `STATUS_META`
would bury the symbols an `` has to resolve to.
### What a symbol page contains [#what-a-symbol-page-contains]
Four things, and no more than a line scanner can honestly produce: the symbol's
name, its doc comment in full, a table giving its kind and the file it is
declared in, and its whole declaration, copied from the source rather than
reconstructed. An interface shows its members and a union shows its cases,
because for a type declared across twenty lines the opening line is the one part
of it the page title has already told you. Field comments are stripped from that
block, because a paragraph inside a signature hides the shape the block is there
to show; the prose is in the source, at the path the table names.
Improve a page by improving its doc comment. The first sentence of it is also
the page's `description`, which is the search snippet and the card subtitle, so
the generator writes that line rather than preserving it and a page's summary
cannot drift from the symbol's.
For the health types the page is only ever half the answer. The declaration says
there are four status levels; only
[Clinical status semantics](../../health/clinical-status-semantics.mdx) says what
`attention` obliges you to do, and that is the half a type reference cannot
carry. The two pages here that keep hand-written prose above their generated
block, below, both do that linking by hand; for the rest, follow the symbol back
to the doctrine pillar yourself.
Two things a symbol page still does not contain, and both are the part such a
page is usually judged on. Defaults are not listed, because a default lives in
the function that applies the type rather than in the type. Nor is the set of
components and functions that consume the symbol, which is the part a type
reference usually omits and the part you usually need. Both need a type checker
rather than a textual scan, and until the generator grows one, the declaration
and its file are what these pages promise.
List defaults and consumers on the per-symbol pages.
It needs
`build-reference.mts`
to read the declarations through a TypeScript
program instead of a line scan. Members and unions are expanded already, by
capturing the declaration to its closing brace or its last case.
### What exists today [#what-exists-today]
A page for every `type` and `interface` under `lib/`, each one written by
`pnpm run generate` and replaced wholesale on the next run. That covers the
colour model, the status and category vocabulary, the catalogue, preset and
registry shapes, and the error types. The full list, with the functions and constants that do not get
a page, is [Types](../generated/types.mdx).
Two of them predate the generator and are still half hand-written above their
generated block:
* [ClinicalStatus](./ClinicalStatus.mdx), the four clinical status levels
* [HealthCategory](./HealthCategory.mdx), the subject-identity categories
Their prose was written as a specification, before the types were declared, and
it is still marked as one. Where that prose and the generated block below it
disagree, the generated block is what the code says and the prose is what was
intended. No other symbol page here has two halves like that; every other one is
generated end to end.
---
# AliasProblem
Source: https://opsinjs.pensievelabs.org/reference/api/AliasProblem
Markdown: https://opsinjs.pensievelabs.org/reference/api/AliasProblem.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/catalogue.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## AliasProblem [#aliasproblem]
One search synonym that cannot be used, either because two components claim it or because it collides with a component id.
| Kind | Declared in |
| --------- | ------------------ |
| interface | `lib/catalogue.ts` |
```ts
export interface AliasProblem {
alias: string
reason: "duplicate" | "collides-with-id"
owners: string[]
}
```
{/* opsinjs:generated:end */}
---
# ApcaVerdict
Source: https://opsinjs.pensievelabs.org/reference/api/ApcaVerdict
Markdown: https://opsinjs.pensievelabs.org/reference/api/ApcaVerdict.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/apca.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ApcaVerdict [#apcaverdict]
The result of one APCA measurement, with the signed Lc, its polarity, the floor for that use, and how far above or below the floor it landed.
| Kind | Declared in |
| --------- | ------------------- |
| interface | `lib/color/apca.ts` |
```ts
export interface ApcaVerdict {
lc: number
magnitude: number
polarity: "dark-on-light" | "light-on-dark" | "indistinguishable"
use: ContrastUse
floor: number
passes: boolean
headroom: number
}
```
{/* opsinjs:generated:end */}
---
# AxisConflict
Source: https://opsinjs.pensievelabs.org/reference/api/AxisConflict
Markdown: https://opsinjs.pensievelabs.org/reference/api/AxisConflict.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## AxisConflict [#axisconflict]
A refusal. It is what the system answers when a measurement category and a clinical status are asked to share one surface.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/status.ts` |
```ts
export interface AxisConflict {
code: "OPSIN-0001"
category: HealthCategory
status: ClinicalStatus
message: string
docs: string
}
```
{/* opsinjs:generated:end */}
---
# CatalogueIndexRow
Source: https://opsinjs.pensievelabs.org/reference/api/CatalogueIndexRow
Markdown: https://opsinjs.pensievelabs.org/reference/api/CatalogueIndexRow.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/catalogue.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## CatalogueIndexRow [#catalogueindexrow]
One component as the light registry index publishes it, flattened to the fields a decision to install actually turns on.
| Kind | Declared in |
| --------- | ------------------ |
| interface | `lib/catalogue.ts` |
```ts
export interface CatalogueIndexRow {
name: string
title: string
description: string
type: "registry:ui"
category: CatalogueCategory
status: Status
since: string
aliases: string[]
implemented: boolean
docs: string | null
dependencies?: string[]
registryDependencies?: string[]
}
```
{/* opsinjs:generated:end */}
---
# ClinicalStatus
Source: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatus
Markdown: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatus.md
Section: Reference · kind: reference · also known as: status type, status union
`ClinicalStatus` is the type of the status axis. Anything that renders urgency
takes a value of this type and nothing else, and that means a pill, a banner, a
range or a result card. It is a closed union with four members, and it is closed on purpose: a
system that lets a product add a fifth level has no ordinal scale, and an
alarm-fatigue budget you can extend is not a budget.
## How this is generated [#how-this-is-generated]
**Source:** the published declaration of the `@opsinjs/react` package.
**Script:** `scripts/build-reference.mts`. **Command:** `pnpm run generate`.
The package does not exist yet, so the block below is a **specification**, written
by hand and marked as one. It is here because links point at it and a broken link
is a worse answer than an honest stub. It is not an API, it has not been
implemented, and generating code against it will produce an import that does not
resolve.
```ts
/**
* SPECIFICATION. Not implemented, not published.
* The four ordinal clinical status levels, defined in
* health/clinical-status-semantics.
*/
export type ClinicalStatus = "steady" | "watch" | "attention" | "urgent"
```
The values are ordinal and ordered as written; comparison helpers, if any ship,
will be documented on their own pages rather than added to this union. The
meanings, the tone each level requires, the sentence patterns and the list of
things each level must never be read as are doctrine and live on
[Clinical status semantics](../../health/clinical-status-semantics.mdx). The
reason this type may never be combined with a category on one element is
[The two colour axes](../../health/two-colour-axes.mdx).
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ClinicalStatus [#clinicalstatus]
What a reading means and what, if anything, to do about it.
Four levels, ordered. This is the ONLY status vocabulary in the system: the documentation chrome's callouts, the product's status pills and the dev warnings all use these four words, so a developer reading a Callout in these docs is reading the same vocabulary they will ship.
A level is assigned by the consuming product from a reference range or a threshold that the product owns. opsinjs never assigns one, because opsinjs does not know the reader.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type ClinicalStatus = (typeof CLINICAL_STATUSES)[number]
```
{/* opsinjs:generated:end */}
---
# ClinicalStatusMeta
Source: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatusMeta
Markdown: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatusMeta.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ClinicalStatusMeta [#clinicalstatusmeta]
Everything a clinical status carries besides its colour. The word, the example sentence, who may assign it, its icon and its place in the order.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/status.ts` |
```ts
export interface ClinicalStatusMeta {
word: string
sentence: string
assignedBy: string
icon: string
level: number
}
```
{/* opsinjs:generated:end */}
---
# ClinicalStatusOrUnknown
Source: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatusOrUnknown
Markdown: https://opsinjs.pensievelabs.org/reference/api/ClinicalStatusOrUnknown.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ClinicalStatusOrUnknown [#clinicalstatusorunknown]
The four clinical statuses plus `unknown`. Unknown is the absence of an assertion rather than a fifth level of urgency.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type ClinicalStatusOrUnknown = ClinicalStatus | UnknownStatus
```
{/* opsinjs:generated:end */}
---
# ColorInput
Source: https://opsinjs.pensievelabs.org/reference/api/ColorInput
Markdown: https://opsinjs.pensievelabs.org/reference/api/ColorInput.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/apca.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ColorInput [#colorinput]
Anything the contrast functions accept: an sRGB triple, an OKLCH object, or a CSS colour string (`#0b6bcb`, `oklch(0.62 0.17 15)`, `rgb(11 107 203)`).
The string form is here because the two callers that matter most are a route handler receiving JSON and a playground receiving a paste, and neither has a triple to hand. A string that cannot be parsed yields NaN rather than an exception or a silent black: NaN propagates visibly into the response and into any comparison, which is the behaviour you want from a number nobody managed to measure.
| Kind | Declared in |
| ---- | ------------------- |
| type | `lib/color/apca.ts` |
```ts
export type ColorInput = Rgb255 | Oklch | string
```
{/* opsinjs:generated:end */}
---
# ContrastCheck
Source: https://opsinjs.pensievelabs.org/reference/api/ContrastCheck
Markdown: https://opsinjs.pensievelabs.org/reference/api/ContrastCheck.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ContrastCheck [#contrastcheck]
One measured pair from a derived theme, carrying both the APCA Lc and the WCAG ratio, the use that fixes the floor, and whether it passed.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface ContrastCheck {
pair: string
use: ContrastUse
apcaLc: number
wcagRatio: number
passes: boolean
advisory?: boolean
}
```
{/* opsinjs:generated:end */}
---
# ContrastUse
Source: https://opsinjs.pensievelabs.org/reference/api/ContrastUse
Markdown: https://opsinjs.pensievelabs.org/reference/api/ContrastUse.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/apca.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ContrastUse [#contrastuse]
What a pair is being used for. Determines which floor applies.
| Kind | Declared in |
| ---- | ------------------- |
| type | `lib/color/apca.ts` |
```ts
export type ContrastUse = "body" | "large" | "nonText"
```
{/* opsinjs:generated:end */}
---
# CvdCollision
Source: https://opsinjs.pensievelabs.org/reference/api/CvdCollision
Markdown: https://opsinjs.pensievelabs.org/reference/api/CvdCollision.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/cvd.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## CvdCollision [#cvdcollision]
Two colours that stop being distinguishable under one kind of colour vision deficiency, and the Lc left between them once simulated.
| Kind | Declared in |
| --------- | ------------------ |
| interface | `lib/color/cvd.ts` |
```ts
export interface CvdCollision {
type: CvdType
a: string
b: string
lc: number
}
```
{/* opsinjs:generated:end */}
---
# CvdType
Source: https://opsinjs.pensievelabs.org/reference/api/CvdType
Markdown: https://opsinjs.pensievelabs.org/reference/api/CvdType.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/cvd.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## CvdType [#cvdtype]
The three dichromacies, plus the achromatic check every specimen must also survive.
| Kind | Declared in |
| ---- | ------------------ |
| type | `lib/color/cvd.ts` |
```ts
export type CvdType = "protanopia" | "deuteranopia" | "tritanopia" | "grayscale"
```
{/* opsinjs:generated:end */}
---
# DataState
Source: https://opsinjs.pensievelabs.org/reference/api/DataState
Markdown: https://opsinjs.pensievelabs.org/reference/api/DataState.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DataState [#datastate]
The state a data surface is in, named once so the surfaces converge on one vocabulary, though no component accepts it yet.
These are the five states the Data states foundation names, which are loading, error, empty, partial and stale, plus the resolved case where the value is real, current and simply rendered. The members are listed with the five doctrine states first and resolved last; the order a surface actually moves through them is the resolution order the foundation's flow diagram fixes, which is error first, then loading, empty, partial, stale and finally resolved.
No component accepts this type yet. It exists so that when the data surfaces do converge on a single state input they converge on one set of names rather than six, and because lib/opsinjs.ts is the substrate `shadcn add` copies into a consumer's project, a type declared here already travels with every installed component and costs nothing at runtime. Turning this alias into a running state machine, with a prop, a guard and a default, is a larger change this name does not stand in for.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type DataState =
| "loading"
| "error"
| "empty"
| "partial"
| "stale"
| "resolved"
```
{/* opsinjs:generated:end */}
---
# DecodeError
Source: https://opsinjs.pensievelabs.org/reference/api/DecodeError
Markdown: https://opsinjs.pensievelabs.org/reference/api/DecodeError.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/preset.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DecodeError [#decodeerror]
Why a preset code could not be read. The five reasons are kept distinct so that the sentence shown to a person can be a specific one.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/preset.ts` |
```ts
export type DecodeError =
| "missing-prefix"
| "wrong-length"
| "unknown-version"
| "bad-characters"
| "bad-checksum"
```
{/* opsinjs:generated:end */}
---
# DecodeResult
Source: https://opsinjs.pensievelabs.org/reference/api/DecodeResult
Markdown: https://opsinjs.pensievelabs.org/reference/api/DecodeResult.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/preset.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DecodeResult [#decoderesult]
The outcome of reading a preset code. Either the preset and its version, or an error and a sentence a person can act on.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/preset.ts` |
```ts
export interface DecodeResult {
preset: Preset | null
version: number | null
error: DecodeError | null
message: string | null
}
```
{/* opsinjs:generated:end */}
---
# DerivedTheme
Source: https://opsinjs.pensievelabs.org/reference/api/DerivedTheme
Markdown: https://opsinjs.pensievelabs.org/reference/api/DerivedTheme.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DerivedTheme [#derivedtheme]
Everything derivation produces from one brand colour. Both ramps, every role in light and dark, the contrast checks, the warnings, and whether it is sound.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface DerivedTheme {
brand: Oklch
seed: Oklch
brandRamp: Ramp
neutralRamp: Ramp
roles: Record
checks: ContrastCheck[]
warnings: DeriveWarning[]
sound: boolean
}
```
{/* opsinjs:generated:end */}
---
# DeriveOptions
Source: https://opsinjs.pensievelabs.org/reference/api/DeriveOptions
Markdown: https://opsinjs.pensievelabs.org/reference/api/DeriveOptions.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DeriveOptions [#deriveoptions]
The knobs on theme derivation. Every one of them has a default, so a brand colour on its own is already a complete input.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface DeriveOptions {
neutralChroma?: number
gamut?: Gamut
maxSeedChroma?: number
}
```
{/* opsinjs:generated:end */}
---
# DeriveWarning
Source: https://opsinjs.pensievelabs.org/reference/api/DeriveWarning
Markdown: https://opsinjs.pensievelabs.org/reference/api/DeriveWarning.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## DeriveWarning [#derivewarning]
One thing the derivation has to say about a theme, as a level, a stable code and a message.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface DeriveWarning {
level: WarningLevel
code: string
message: string
}
```
{/* opsinjs:generated:end */}
---
# Detent
Source: https://opsinjs.pensievelabs.org/reference/api/Detent
Markdown: https://opsinjs.pensievelabs.org/reference/api/Detent.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Detent [#detent]
How far a sheet is open.
Three stops rather than a continuous height, because a sheet a reader can leave at an arbitrary position is a sheet that will be left half over the value it is explaining. `content` is as tall as what is inside it, `half` is the resting stop that keeps the screen behind it readable, and `full` is the whole surface.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type Detent = "content" | "half" | "full"
```
{/* opsinjs:generated:end */}
---
# Gamut
Source: https://opsinjs.pensievelabs.org/reference/api/Gamut
Markdown: https://opsinjs.pensievelabs.org/reference/api/Gamut.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/oklch.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Gamut [#gamut]
The colour spaces this module can express a colour in.
| Kind | Declared in |
| ---- | -------------------- |
| type | `lib/color/oklch.ts` |
```ts
export type Gamut = "srgb" | "display-p3"
```
{/* opsinjs:generated:end */}
---
# GeneratedBannedWord
Source: https://opsinjs.pensievelabs.org/reference/api/GeneratedBannedWord
Markdown: https://opsinjs.pensievelabs.org/reference/api/GeneratedBannedWord.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## GeneratedBannedWord [#generatedbannedword]
One banned word: the word a component's copy must not use, what to write in its place, and why the word is barred.
The shape lives here, in the shipped substrate, rather than in lib/generated, because a component that reads this list has to compile inside a consumer's project. build-registry ships exactly two library files, this one and lib/status.ts, so a list a component imports can live in one of those two and nowhere else, and lib/generated is a documentation-site artefact that never leaves this repository.
The rows themselves are generated rather than typed by hand. The space below is reserved and not yet filled. Once scripts/build-tokens.mts emits its banned-word region, the rows will arrive there from tokens/glossary.json, the single source for the banned list and the plain-language glossary alike, and `node scripts/build-tokens.mts --check` will then hold the generated copy and that source in step, failing the build the moment one drifts. Only the shape is stated here, so the generated region can carry data alone.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface GeneratedBannedWord {
word: string
instead: string
reason: string
}
```
{/* opsinjs:generated:end */}
---
# HealthCategory
Source: https://opsinjs.pensievelabs.org/reference/api/HealthCategory
Markdown: https://opsinjs.pensievelabs.org/reference/api/HealthCategory.md
Section: Reference · kind: reference · also known as: category type, category union
`HealthCategory` is the type of the identity axis: the subject a value belongs
to, so that a screen full of unrelated numbers can be scanned. It carries no
urgency, no valence and no judgement. A category colour tells a reader which of
their things they are looking at; it must never tell them how to feel about it.
## How this is generated [#how-this-is-generated]
**Source:** the published declaration of the `@opsinjs/react` package.
**Script:** `scripts/build-reference.mts`. **Command:** `pnpm run generate`.
The package does not exist yet. The block below is a **specification** and is
marked as one; the member list in particular is expected to change, because the
category set is the part of the system a consuming product is most likely to need
extended, and
[Category palettes](../../theming/category-palettes.mdx) describes the supported
way to do that without contaminating the status axis.
```ts
/**
* SPECIFICATION. Not implemented, not published.
* Subject-identity categories, defined in health/category-identity.
* Extensible by a consuming theme; the status axis is not.
*/
export type HealthCategory = string & { readonly __brand?: "HealthCategory" }
```
The specification deliberately does not freeze the member list here. What the
system freezes is the *rule*: a category is identity only, categories are
extensible, and no component accepts a category and a status in the same visual
slot. The current members ship in `tokens/color.json` and are listed, generated,
in [Tokens](../generated/tokens.mdx); the meaning of each is
[Category identity](../../health/category-identity.mdx).
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## HealthCategory [#healthcategory]
What a reading is ABOUT. Identity, never verdict.
A heart-red card does not mean something is wrong with a heart reading; it means the reading concerns the heart. This is the axis products most often misuse, because red already means something else everywhere else on the web.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type HealthCategory = (typeof HEALTH_CATEGORIES)[number]
```
{/* opsinjs:generated:end */}
---
# Kind
Source: https://opsinjs.pensievelabs.org/reference/api/Kind
Markdown: https://opsinjs.pensievelabs.org/reference/api/Kind.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Kind [#kind]
A page's kind. This is a contract, not a label: `kind` fully determines the page's headings, and `assert-ia.mts` fails the build on a missing or an unexpected one.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type Kind = (typeof KINDS)[number]
```
{/* opsinjs:generated:end */}
---
# MaterialRung
Source: https://opsinjs.pensievelabs.org/reference/api/MaterialRung
Markdown: https://opsinjs.pensievelabs.org/reference/api/MaterialRung.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## MaterialRung [#materialrung]
A rung of the material ladder, meaning how a surface sits above what is behind it.
These six names are the token names: every rung resolves a matching set of `--opsin-material--*` custom properties for blur, tint, shadow and border. Where a specification page names a different set, the tokens win, because the tokens are what actually renders.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type MaterialRung =
| "canvas"
| "card"
| "raised"
| "sheet"
| "overlay"
| "scrim"
```
{/* opsinjs:generated:end */}
---
# Oklab
Source: https://opsinjs.pensievelabs.org/reference/api/Oklab
Markdown: https://opsinjs.pensievelabs.org/reference/api/Oklab.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/oklch.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Oklab [#oklab]
A colour in OKLab.
| Kind | Declared in |
| --------- | -------------------- |
| interface | `lib/color/oklch.ts` |
```ts
export interface Oklab {
l: number
a: number
b: number
alpha?: number
}
```
{/* opsinjs:generated:end */}
---
# Oklch
Source: https://opsinjs.pensievelabs.org/reference/api/Oklch
Markdown: https://opsinjs.pensievelabs.org/reference/api/Oklch.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/oklch.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Oklch [#oklch]
A colour in OKLCH. `l` 0 to 1, `c` unbounded, `h` in degrees.
| Kind | Declared in |
| --------- | -------------------- |
| interface | `lib/color/oklch.ts` |
```ts
export interface Oklch {
l: number
c: number
h: number
alpha?: number
}
```
{/* opsinjs:generated:end */}
---
# OpsinError
Source: https://opsinjs.pensievelabs.org/reference/api/OpsinError
Markdown: https://opsinjs.pensievelabs.org/reference/api/OpsinError.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## OpsinError [#opsinerror]
One warning code: what it is called, how severe it is, and the page that prevents it.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface OpsinError {
code: OpsinErrorCode
severity: OpsinErrorSeverity
title: string
message: string
docs: string
params: string[]
}
```
{/* opsinjs:generated:end */}
---
# OpsinErrorCode
Source: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorCode
Markdown: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorCode.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## OpsinErrorCode [#opsinerrorcode]
Every warning code opsinjs can emit.
Flat, allocated in sequence, and permanent: a code is never reused, never renumbered and never removed. The union is what makes `warnOnce("OPSIN-0004")` a compile error when the code does not exist, which is the difference between a stable code and a string somebody typed.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type OpsinErrorCode =
| "OPSIN-0001"
| "OPSIN-0002"
| "OPSIN-0003"
| "OPSIN-0004"
| "OPSIN-0005"
| "OPSIN-0006"
| "OPSIN-0007"
| "OPSIN-0008"
| "OPSIN-0009"
| "OPSIN-0010"
| "OPSIN-0011"
| "OPSIN-0012"
| "OPSIN-0013"
| "OPSIN-0014"
| "OPSIN-0015"
| "OPSIN-0016"
| "OPSIN-0017"
| "OPSIN-0018"
| "OPSIN-0019"
| "OPSIN-0020"
| "OPSIN-0021"
```
{/* opsinjs:generated:end */}
---
# OpsinErrorParams
Source: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorParams
Markdown: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorParams.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## OpsinErrorParams [#opsinerrorparams]
The values a message template asks for, keyed by placeholder name.
Values are stringified as they are: a number arrives as a number so the message does not have to decide how to format it, and no value is ever rounded, unit-converted or localised on its way into a warning. A warning that reformats what it was given is a warning that can disagree with the screen it is complaining about.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type OpsinErrorParams = Record
```
{/* opsinjs:generated:end */}
---
# OpsinErrorSeverity
Source: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorSeverity
Markdown: https://opsinjs.pensievelabs.org/reference/api/OpsinErrorSeverity.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## OpsinErrorSeverity [#opsinerrorseverity]
How severe a warning is.
The three classes are declared in `tokens/errors.json`'s `policy.severity` block, which also carries the sentence that says what each one means:
* safety - A defect that can mislead a reader about their own health. Treat as a bug, not as a lint warning.
* correctness - The component will render something, but not what the author meant.
* hygiene - Works today, will not survive an upgrade.
| Kind | Declared in |
| ---- | ---------------- |
| type | `lib/opsinjs.ts` |
```ts
export type OpsinErrorSeverity = "safety" | "correctness" | "hygiene"
```
{/* opsinjs:generated:end */}
---
# Preset
Source: https://opsinjs.pensievelabs.org/reference/api/Preset
Markdown: https://opsinjs.pensievelabs.org/reference/api/Preset.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/preset.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Preset [#preset]
A theme as a person describes it. What `encodePreset` takes and `decodePreset` returns.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/preset.ts` |
```ts
export interface Preset {
hue: number
chroma: number
lightness: number
neutralChroma: number
radius: number
density: (typeof DENSITY_VALUES)[number]
gamut: (typeof GAMUT_VALUES)[number]
floor: (typeof FLOOR_VALUES)[number]
cornerShape: (typeof CORNER_SHAPE_VALUES)[number]
mode: (typeof MODE_VALUES)[number]
}
```
{/* opsinjs:generated:end */}
---
# Ramp
Source: https://opsinjs.pensievelabs.org/reference/api/Ramp
Markdown: https://opsinjs.pensievelabs.org/reference/api/Ramp.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Ramp [#ramp]
One complete lightness ramp, as a value per step. The chromatic ramp and the neutral ramp of a derived theme are both this shape.
| Kind | Declared in |
| ---- | --------------------- |
| type | `lib/color/derive.ts` |
```ts
export type Ramp = Record
```
{/* opsinjs:generated:end */}
---
# RampStep
Source: https://opsinjs.pensievelabs.org/reference/api/RampStep
Markdown: https://opsinjs.pensievelabs.org/reference/api/RampStep.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RampStep [#rampstep]
One rung of the lightness ladder, from 50 at the lightest to 950 at the darkest. The rungs are fixed, so two themes are comparable step by step.
| Kind | Declared in |
| ---- | --------------------- |
| type | `lib/color/derive.ts` |
```ts
export type RampStep = (typeof RAMP_STEPS)[number]
```
{/* opsinjs:generated:end */}
---
# RampStepValue
Source: https://opsinjs.pensievelabs.org/reference/api/RampStepValue
Markdown: https://opsinjs.pensievelabs.org/reference/api/RampStepValue.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RampStepValue [#rampstepvalue]
One rung of a ramp, as the sRGB value that ships, the wide-gamut value where P3 has headroom, and whether sRGB had to clamp the chroma away.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface RampStepValue {
step: RampStep
srgb: Oklch
p3?: Oklch
clamped: boolean
css: { srgb: string; p3?: string }
}
```
{/* opsinjs:generated:end */}
---
# ReferenceRange
Source: https://opsinjs.pensievelabs.org/reference/api/ReferenceRange
Markdown: https://opsinjs.pensievelabs.org/reference/api/ReferenceRange.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ReferenceRange [#referencerange]
The interval a reading is being compared against, and who says so.
Both bounds are optional because a one-sided range is a real range: some measurements have a floor and no ceiling, and rendering a bound the product did not supply would invent a threshold. An omitted bound renders as an open end, never as zero and never as an assumed limit.
`source` is required, and it is required because opsinjs does not own a single clinical number. A range arriving with no attribution is the consuming product asking this system to vouch for a threshold it has never seen, which is the one thing it must not do; a component handed a range with no `source` reports OPSIN-0004 rather than drawing it.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface ReferenceRange {
low?: number
high?: number
source: string
asOf?: string
}
```
{/* opsinjs:generated:end */}
---
# RefusedConversion
Source: https://opsinjs.pensievelabs.org/reference/api/RefusedConversion
Markdown: https://opsinjs.pensievelabs.org/reference/api/RefusedConversion.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RefusedConversion [#refusedconversion]
A pair people expect to be arithmetic and is not, with the reason.
This list is the load-bearing half of the unit table. An omitted conversion has to render as an explicit "we do not have this", never as a substituted default, and a component that wants to explain WHY reads its reason here.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface RefusedConversion {
between: [string, string]
reason: string
docs: string
}
```
{/* opsinjs:generated:end */}
---
# RegistryLookup
Source: https://opsinjs.pensievelabs.org/reference/api/RegistryLookup
Markdown: https://opsinjs.pensievelabs.org/reference/api/RegistryLookup.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/registry.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RegistryLookup [#registrylookup]
Both call shapes are supported on purpose.
`getRegistryEntry("range-bar", base, style)` is what the preview surface and the machine routes call, and it reads naturally at a call site that already has three variables. The object form exists for the one caller that needs to pass a `kind`, and because a fourth positional argument is where a signature stops being readable.
| Kind | Declared in |
| ---- | ----------------- |
| type | `lib/registry.ts` |
```ts
export type RegistryLookup = string | RegistryQuery
```
{/* opsinjs:generated:end */}
---
# RegistryQuery
Source: https://opsinjs.pensievelabs.org/reference/api/RegistryQuery
Markdown: https://opsinjs.pensievelabs.org/reference/api/RegistryQuery.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/registry.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RegistryQuery [#registryquery]
A request for one registry item, by id and optionally by base, style and kind. Anything left out falls back to the default.
| Kind | Declared in |
| --------- | ----------------- |
| interface | `lib/registry.ts` |
```ts
export interface RegistryQuery {
name: string
base?: string
style?: string
kind?: RegistryKind
}
```
{/* opsinjs:generated:end */}
---
# Rgb
Source: https://opsinjs.pensievelabs.org/reference/api/Rgb
Markdown: https://opsinjs.pensievelabs.org/reference/api/Rgb.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/oklch.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Rgb [#rgb]
Red, green and blue, each 0 to 1, in whichever space the function says.
| Kind | Declared in |
| --------- | -------------------- |
| interface | `lib/color/oklch.ts` |
```ts
export interface Rgb {
r: number
g: number
b: number
alpha?: number
}
```
{/* opsinjs:generated:end */}
---
# Rgb255
Source: https://opsinjs.pensievelabs.org/reference/api/Rgb255
Markdown: https://opsinjs.pensievelabs.org/reference/api/Rgb255.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/apca.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Rgb255 [#rgb255]
A colour as 0 to 255 sRGB integers, which is what APCA is defined over.
| Kind | Declared in |
| ---- | ------------------- |
| type | `lib/color/apca.ts` |
```ts
export type Rgb255 = [number, number, number]
```
{/* opsinjs:generated:end */}
---
# RoleName
Source: https://opsinjs.pensievelabs.org/reference/api/RoleName
Markdown: https://opsinjs.pensievelabs.org/reference/api/RoleName.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RoleName [#rolename]
The roles a derived theme guarantees. Components consume these and never a step.
| Kind | Declared in |
| ---- | --------------------- |
| type | `lib/color/derive.ts` |
```ts
export type RoleName =
| "canvas"
| "surface"
| "ink"
| "muted-ink"
| "line"
| "control-line"
| "primary"
| "on-primary"
| "focus"
```
{/* opsinjs:generated:end */}
---
# RolePair
Source: https://opsinjs.pensievelabs.org/reference/api/RolePair
Markdown: https://opsinjs.pensievelabs.org/reference/api/RolePair.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RolePair [#rolepair]
One semantic role in both colour modes. Roles are derived as a pair, because a role that exists only in light mode is a role that breaks in dark.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface RolePair {
light: RoleValue
dark: RoleValue
}
```
{/* opsinjs:generated:end */}
---
# RoleValue
Source: https://opsinjs.pensievelabs.org/reference/api/RoleValue
Markdown: https://opsinjs.pensievelabs.org/reference/api/RoleValue.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## RoleValue [#rolevalue]
What a semantic role resolves to in one colour mode. The ramp step it came from, the OKLCH value, and the CSS it is written as.
| Kind | Declared in |
| --------- | --------------------- |
| interface | `lib/color/derive.ts` |
```ts
export interface RoleValue {
source: string
value: Oklch
css: string
}
```
{/* opsinjs:generated:end */}
---
# Status
Source: https://opsinjs.pensievelabs.org/reference/api/Status
Markdown: https://opsinjs.pensievelabs.org/reference/api/Status.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Status [#status]
The release phase of a component page or a catalogue entry.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type Status = (typeof STATUSES)[number]
```
{/* opsinjs:generated:end */}
---
# StatusMeta
Source: https://opsinjs.pensievelabs.org/reference/api/StatusMeta
Markdown: https://opsinjs.pensievelabs.org/reference/api/StatusMeta.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## StatusMeta [#statusmeta]
What a release phase shows on a badge. A label and one sentence a reader can act on, which is also the chip's title and its accessible name.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/status.ts` |
```ts
export interface StatusMeta {
label: string
summary: string
}
```
{/* opsinjs:generated:end */}
---
# TokenFormat
Source: https://opsinjs.pensievelabs.org/reference/api/TokenFormat
Markdown: https://opsinjs.pensievelabs.org/reference/api/TokenFormat.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/tokens.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## TokenFormat [#tokenformat]
The formats the `/tokens` and `/colors` browsers can copy a value in.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/tokens.ts` |
```ts
export type TokenFormat = "oklch" | "display-p3" | "hex" | "var" | "class"
```
{/* opsinjs:generated:end */}
---
# TrendPoint
Source: https://opsinjs.pensievelabs.org/reference/api/TrendPoint
Markdown: https://opsinjs.pensievelabs.org/reference/api/TrendPoint.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## TrendPoint [#trendpoint]
One reading in a series, for the components that draw change over time.
`value` is `number | null` rather than `number` because a gap in a series is information: a day with no reading is not a day with a reading of zero, and charting it as zero invents a measurement. A `null` point is drawn as a break in the line and described in words by the chart's text twin.
`status` is optional and is an input, never a derivation. opsinjs does not compare a point to a range and decide what it means, because it does not know the reader; a point carries a verdict only when the consuming product attached one.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface TrendPoint {
at: string
value: number | null
status?: ClinicalStatus
}
```
{/* opsinjs:generated:end */}
---
# Unit
Source: https://opsinjs.pensievelabs.org/reference/api/Unit
Markdown: https://opsinjs.pensievelabs.org/reference/api/Unit.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Unit [#unit]
One unit: what it is called, what a reader sees, and how it is SPOKEN.
The spoken form is the whole reason this table exists. A screen reader handed `mmHg` improvises a pronunciation, and "one twenty over eighty em em aitch gee" is a failure rather than a quirk.
There is no reference range here, no threshold, no plausibility bound and no default precision. A unit table says what a number is measured in; it never says what a number should be. Decimal places belong to the MEASUREMENT and travel with it from the product - health/numbers-units-precision, rule 2.
`joined` is typography, not a claim about a reading: it records that a symbol sits against the number with no space, as everyday English writes 98% and 36.8°C. Which symbols take it is settled by content/docs/content/grammar-and-mechanics.mdx, not by this table.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface Unit {
id: string
symbol: string
spoken: string
plural: string
measures: string
joined?: true
}
```
{/* opsinjs:generated:end */}
---
# UnitConversion
Source: https://opsinjs.pensievelabs.org/reference/api/UnitConversion
Markdown: https://opsinjs.pensievelabs.org/reference/api/UnitConversion.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/opsinjs.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## UnitConversion [#unitconversion]
An exact conversion between two units of the same kind.
Applied as `to = from * numerator / denominator + offsetNumerator / offsetDenominator`. Every field is an integer, and that is deliberate: these are definitions rather than measurements, and a definition stored as a rounded decimal is an approximation wearing a definition's provenance.
`basis` names the definition each factor comes from. A conversion with no basis does not belong in this system: opsinjs does not own a clinical number, and the ones it does carry are the ones that are true by definition rather than by measurement.
| Kind | Declared in |
| --------- | ---------------- |
| interface | `lib/opsinjs.ts` |
```ts
export interface UnitConversion {
from: string
to: string
numerator: number
denominator: number
offsetNumerator: number
offsetDenominator: number
basis: string[]
}
```
{/* opsinjs:generated:end */}
---
# UnknownStatus
Source: https://opsinjs.pensievelabs.org/reference/api/UnknownStatus
Markdown: https://opsinjs.pensievelabs.org/reference/api/UnknownStatus.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/status.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## UnknownStatus [#unknownstatus]
The absence of an assertion, which covers a reading never taken, a stale reading, or a reading whose reference range the product does not own.
Deliberately NOT a fifth clinical status: rendering "we do not know" as a status would claim a verdict the system does not have, and colouring it anywhere near `watch` would let a reader read it as "probably fine".
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/status.ts` |
```ts
export type UnknownStatus = "unknown"
```
{/* opsinjs:generated:end */}
---
# UnresolvedReason
Source: https://opsinjs.pensievelabs.org/reference/api/UnresolvedReason
Markdown: https://opsinjs.pensievelabs.org/reference/api/UnresolvedReason.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/registry.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## UnresolvedReason [#unresolvedreason]
Why a lookup came back empty. The input to ``.
| Kind | Declared in |
| --------- | ----------------- |
| interface | `lib/registry.ts` |
```ts
export interface UnresolvedReason {
name: string
reason: "not-built" | "unknown"
status: Status | null
message: string
}
```
{/* opsinjs:generated:end */}
---
# ViewKind
Source: https://opsinjs.pensievelabs.org/reference/api/ViewKind
Markdown: https://opsinjs.pensievelabs.org/reference/api/ViewKind.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/routes.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ViewKind [#viewkind]
What a `/view` route can render.
| Kind | Declared in |
| ---- | --------------- |
| type | `lib/routes.ts` |
```ts
export type ViewKind = "component" | "example" | "screen"
```
{/* opsinjs:generated:end */}
---
# ViewParams
Source: https://opsinjs.pensievelabs.org/reference/api/ViewParams
Markdown: https://opsinjs.pensievelabs.org/reference/api/ViewParams.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/routes.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## ViewParams [#viewparams]
Everything a chrome-less preview URL can carry. Name and kind become path segments, and the rest become query parameters applied before first paint.
| Kind | Declared in |
| --------- | --------------- |
| interface | `lib/routes.ts` |
```ts
export interface ViewParams {
name: string
kind?: ViewKind
base?: string
style?: string
mode?: "light" | "dark"
density?: "comfortable" | "compact"
text?: 100 | 125 | 150 | 200
theme?: string
}
```
{/* opsinjs:generated:end */}
---
# WarningLevel
Source: https://opsinjs.pensievelabs.org/reference/api/WarningLevel
Markdown: https://opsinjs.pensievelabs.org/reference/api/WarningLevel.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/derive.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## WarningLevel [#warninglevel]
How serious a derivation warning is. An `error` makes the theme unsound and stops it shipping, while a `warning` and a `note` are advisory.
| Kind | Declared in |
| ---- | --------------------- |
| type | `lib/color/derive.ts` |
```ts
export type WarningLevel = "error" | "warning" | "note"
```
{/* opsinjs:generated:end */}
---
# WcagUse
Source: https://opsinjs.pensievelabs.org/reference/api/WcagUse
Markdown: https://opsinjs.pensievelabs.org/reference/api/WcagUse.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/wcag.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## WcagUse [#wcaguse]
Which WCAG 2.2 contrast threshold applies. Body text, large text and non- text content each have a different floor.
| Kind | Declared in |
| ---- | ------------------- |
| type | `lib/color/wcag.ts` |
```ts
export type WcagUse = "body" | "large" | "nonText"
```
{/* opsinjs:generated:end */}
---
# WcagVerdict
Source: https://opsinjs.pensievelabs.org/reference/api/WcagVerdict
Markdown: https://opsinjs.pensievelabs.org/reference/api/WcagVerdict.md
Section: Reference · kind: reference
{/* GENERATED - do not edit. Source: the declaration named in the table below.
Script: scripts/build-reference.mts. Change the doc comment on the symbol. */}
## How this is generated [#how-this-is-generated]
Source: `lib/color/wcag.ts`. Script: `scripts/build-reference.mts`. Command:
`pnpm run generate`.
Everything below is copied from the symbol, so a page is improved by improving
its doc comment. The [API index](./index.mdx) says why there is a page per
symbol and what these pages still do not carry.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## WcagVerdict [#wcagverdict]
The result of one WCAG 2.2 contrast measurement, with the ratio, the floor for that use, and the success criterion it is measured against.
| Kind | Declared in |
| --------- | ------------------- |
| interface | `lib/color/wcag.ts` |
```ts
export interface WcagVerdict {
ratio: number
use: WcagUse
floor: number
passesAA: boolean
passesAAA: boolean
criterion: string
}
```
{/* opsinjs:generated:end */}
---
# Catalogue
Source: https://opsinjs.pensievelabs.org/reference/generated/catalogue
Markdown: https://opsinjs.pensievelabs.org/reference/generated/catalogue.md
Section: Reference · kind: reference · also known as: component list, does opsinjs have
## How this is generated [#how-this-is-generated]
**Source:** `registry/catalogue.ts`, which is the single declaration of the id
namespace.
**Script:** `scripts/build-registry.mts` writes `lib/generated/catalogue.json`;
`scripts/build-reference.mts` renders this page. **Command:** `pnpm run generate`.
This is the answer to "does opsinjs have one of these". It is deliberately the
most boring page on the site and the one most worth trusting: every id the system
will ever resolve appears here, and nothing appears here that is not declared in
the catalogue file. Every id declared today is implemented, installable and
documented, so there is no row on this page without a page behind it. A row for
something specified and not yet built would still appear, with its phase saying
so.
**Five columns.** *Component* is the kebab-case id, which is the same string used
in a URL, in a registry item and in a CLI command, and it is also the docs URL
segment for that component's page. *Category* groups it, and a category beginning
`health-` carries extra obligations, namely a clinical contract section and at
least one governing doctrine page. *Status* is one of the three
[release phases](../../project/release-phases.mdx), and every row reads `shipped`
today. *Since* is the version an id first shipped under, and no version has been
cut yet, so every row reads `unreleased` and the column distinguishes nothing.
*Search synonyms* are the aliases, which is how "lab result" and "blood pressure"
find their way to the right row.
**What is deliberately excluded.** Parts of compound components are not catalogue
entries, so a dialog's panel is documented on the dialog page, not as its own id.
Examples and screens have their own registry types and are listed with the
registry, not here.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Specified [#specified]
| Component | Category | Status | Since | Search synonyms |
| ------------------- | -------------------- | ------- | ---------- | ------------------------------------------------------------------------------------------- |
| `result-card` | health-data-display | shipped | unreleased | results, lab result, test result, report card, blood pressure, a1c |
| `range-bar` | health-data-display | shipped | unreleased | reference range, normal range, in range, range indicator, gauge bar |
| `score-dial` | health-data-display | shipped | unreleased | dial, ring, gauge, score, index, bmi |
| `trend-sparkline` | health-data-display | shipped | unreleased | sparkline, trend, over time, mini chart, line chart, chart, graph |
| `metric-tile` | health-data-display | shipped | unreleased | stat, kpi, vitals, summary tile, steps, resting heart rate, spo2, tile |
| `status-pill` | health-data-display | shipped | unreleased | chip, status chip, status badge, status label, traffic light |
| `timeline-entry` | health-data-display | shipped | unreleased | history item, event list, activity feed |
| `range-legend` | health-data-display | shipped | unreleased | key, chart legend, band legend |
| `alert-banner` | health-communication | shipped | unreleased | warning banner, notification banner, inline alert, warning, notification, alert, banner |
| `care-card` | health-communication | shipped | unreleased | what to do next, action card, advice card, next steps, guidance card, what to do, advice |
| `term` | health-communication | shipped | unreleased | glossary term, jargon, plain word, definition, plain english, tooltip term |
| `consent-sheet` | health-communication | shipped | unreleased | consent, permission, opt in, data sharing, agree |
| `disclaimer-note` | health-communication | shipped | unreleased | disclaimer, not medical advice, legal note, small print, safety note |
| `source-citation` | health-communication | shipped | unreleased | citation, evidence link, reviewed by |
| `log-sheet` | health-input | shipped | unreleased | log, diary, journal, daily entry, capture, quick entry, bottom sheet entry |
| `reading-input` | health-input | shipped | unreleased | enter a reading, measurement input, numeric entry, manual entry, numeric input, unit switch |
| `body-map` | health-input | shipped | unreleased | pain map, anatomy diagram, where does it hurt |
| `value` | health-formatting | shipped | unreleased | number, unit, format a number, numeric display, figure, format, formatted number |
| `relative-time` | health-formatting | shipped | unreleased | time ago, timestamp, last updated, date display, ago, freshness, staleness |
| `table` | data-display | shipped | unreleased | data table, grid, rows and columns |
| `badge` | data-display | shipped | unreleased | counter, label chip |
| `surface` | surfaces | shipped | unreleased | material, glass, blur, elevation, layer, translucency, vibrancy |
| `card` | surfaces | shipped | unreleased | panel, container, content box, box, tile group |
| `sheet` | surfaces | shipped | unreleased | bottom sheet, drawer, side panel, slide over, modal sheet, detent |
| `dialog` | surfaces | shipped | unreleased | modal, alert dialog, confirm, popup |
| `callout` | feedback | shipped | unreleased | admonition, note box, info box, tip, note, aside |
| `empty-state` | feedback | shipped | unreleased | no data, nothing here, zero state, blank slate |
| `skeleton` | feedback | shipped | unreleased | loading placeholder, shimmer, ghost, loading state, placeholder |
| `segmented-control` | navigation | shipped | unreleased | segmented buttons, toggle group |
| `tab-bar` | navigation | shipped | unreleased | bottom navigation, nav bar |
| `stepper` | navigation | shipped | unreleased | wizard, step indicator, multi step |
| `button` | actions-and-forms | shipped | unreleased | cta, action, submit, primary button |
| `link` | actions-and-forms | shipped | unreleased | anchor, hyperlink |
| `field` | actions-and-forms | shipped | unreleased | form field, input wrapper, form control, validation message, label, error message, hint |
| `icon-button` | actions-and-forms | shipped | unreleased | glyph button, round button |
| `switch` | actions-and-forms | shipped | unreleased | toggle |
| `checkbox` | actions-and-forms | shipped | unreleased | tick box, multi select |
| `radio-group` | actions-and-forms | shipped | unreleased | radio buttons, single choice |
| `select` | actions-and-forms | shipped | unreleased | dropdown, picker |
| `number-field` | actions-and-forms | shipped | unreleased | stepper input, quantity input |
| `textarea` | actions-and-forms | shipped | unreleased | long text, notes input |
| `divider` | layout | shipped | unreleased | separator, rule |
| `avatar` | utility | shipped | unreleased | profile picture, initials, user image |
| `visually-hidden` | utility | shipped | unreleased | sr only, screen reader only, clip |
| `goal-ring` | health-data-display | shipped | unreleased | activity ring, close your rings, daily goal |
| `scale-input` | health-input | shipped | unreleased | pain scale, rating scale, likert |
| `dose-tracker` | health-input | shipped | unreleased | medication, pill tracker, dose, adherence tracker |
| `questionnaire` | health-input | shipped | unreleased | survey, assessment, phq, screening form |
| `symptom-picker` | health-input | shipped | unreleased | symptoms, how are you feeling, symptom checker |
| `toast` | feedback | shipped | unreleased | snackbar, transient message, notification toast |
| `progress` | feedback | shipped | unreleased | progress bar, completion |
| `spinner` | feedback | shipped | unreleased | loader, busy indicator, activity indicator |
| `tooltip` | overlay | shipped | unreleased | hover label |
| `popover` | overlay | shipped | unreleased | anchored panel, flyout |
| `menu` | overlay | shipped | unreleased | dropdown menu, context menu, overflow menu |
| `tabs` | navigation | shipped | unreleased | tab list, view switcher |
| `accordion` | navigation | shipped | unreleased | disclosure, expander, collapsible |
| `slider` | actions-and-forms | shipped | unreleased | range input, drag to set |
| `combobox` | actions-and-forms | shipped | unreleased | autocomplete, typeahead, search select |
| `scroll-area` | layout | shipped | unreleased | scroller, overflow container |
{/* opsinjs:generated:end */}
---
# Contrast
Source: https://opsinjs.pensievelabs.org/reference/generated/contrast
Markdown: https://opsinjs.pensievelabs.org/reference/generated/contrast.md
Section: Reference · kind: reference · also known as: contrast measurements, generated contrast, Lc table
## How this is generated [#how-this-is-generated]
**Source:** `tokens/color.json` and `tokens/material.json`.
**Script:** `scripts/check-contrast.mts`, which computes both metrics with the
hand-written maths in `lib/color/apca.ts` and `lib/color/wcag.ts` and writes
`lib/generated/contrast.json`; `scripts/build-reference.mts` renders this page
from it. **Commands:** `pnpm run contrast` to measure, `pnpm run contrast:verify`
to fail on a regression. The nightly job runs both.
Every number here was computed from the shipped token values by a script, in the
run whose date is printed at the top of the table. None of them was typed by a
person, and none of them is a target that someone decided the system had already
met.
**Both metrics, side by side, always.** WCAG 2.2 contrast ratio is what a
conformance claim is written against and what a procurement reviewer will ask
for. APCA Lc models perception better, particularly for light text on dark
surfaces and for the thin weights this system uses at small sizes. They disagree,
and where they disagree we report both and hold the pair to the stricter verdict.
[Contrast and APCA](../../foundations/colour/contrast-and-apca.mdx) explains what
each number means and why we did not pick one.
**Six columns.** *Foreground* and *Background* are the token pair. *Theme* is
light or dark, and every pair is measured in both. *Lc* is the APCA value. *Ratio*
is the WCAG 2.2 value. *Verdict* is measured against the published floor for the
pair's intended use, not against a single global threshold. Body text, large
text and non-text boundaries have different floors, and a pair that passes for one
fails for another.
**Three verdicts, and `advisory` is the one to read carefully.** `pass` means the
pair clears both floors for its role. `FAIL` means a gated pair does not.
`advisory` means the pair is *not gated*, as with an accent used as an identity
fill, a chart mark, or a hairline boundary. It is a statement about how the pair is
used, not about whether it clears anything. Some advisory pairs clear their floor
and some do not, and this table renders both the same way. The ones that do not
are recorded in `lib/generated/contrast.json` with `"passes": false` and the
floors they miss named in `failing`, and `pnpm run contrast` lists the first few
of them separately on stdout. So do not read `advisory` as a quieter `pass`, and
do not quote the headline `n fail` count as a conformance result: it counts gated
failures only.
**What this page does and does not cover.** It covers the presets opsinjs ships.
It says nothing about a theme you derived from your own brand colour: those
numbers depend on your input, which is why the derivation tooling refuses to emit
a theme it cannot verify and why
[Validating your theme](../../theming/validating-your-theme.mdx) exists. Quoting
these figures in a conformance document is only honest if you shipped the preset
they describe.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Measured contrast [#measured-contrast]
Measured on 2026-09-19. 228 pairs: 142 pass, 12 fail, 74 advisory, and 28 of the advisory pairs measure below the floor for their role. Advisory pairs are not gated, so none of those is a build failure; each is an accent that has to be bounded by `line` or labelled in `ink` rather than read as text.
### category [#category]
#### Light theme [#light-theme]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------- | ------- | ------- | -------- | ---------------------- |
| activity ink on activity surface | body | 88.5 | 9.61:1 | pass |
| activity line on activity surface | nonText | 67.6 | 4.42:1 | pass |
| activity accent on activity surface | nonText | 44.1 | 2.34:1 | advisory - below floor |
| activity accent on the page | nonText | 46.1 | 2.41:1 | advisory - below floor |
| activity ink on the page | body | 90.5 | 9.90:1 | pass |
| activity line on the band | nonText | 66.1 | 4.32:1 | pass |
| heart ink on heart surface | body | 89.1 | 10.51:1 | pass |
| heart line on heart surface | nonText | 70.4 | 5.05:1 | pass |
| heart accent on heart surface | nonText | 60.3 | 3.64:1 | advisory |
| heart accent on the page | nonText | 63.2 | 3.81:1 | advisory |
| heart ink on the page | body | 92.0 | 10.97:1 | pass |
| heart line on the band | nonText | 69.9 | 5.01:1 | pass |
| labs ink on labs surface | body | 88.8 | 9.78:1 | pass |
| labs line on labs surface | nonText | 67.9 | 4.51:1 | pass |
| labs accent on labs surface | nonText | 59.3 | 3.49:1 | advisory |
| labs accent on the page | nonText | 61.5 | 3.60:1 | advisory |
| labs ink on the page | body | 90.9 | 10.09:1 | pass |
| labs line on the band | nonText | 66.5 | 4.42:1 | pass |
| mind ink on mind surface | body | 89.9 | 10.36:1 | pass |
| mind line on mind surface | nonText | 70.7 | 4.94:1 | pass |
| mind accent on mind surface | nonText | 60.0 | 3.55:1 | advisory |
| mind accent on the page | nonText | 62.8 | 3.70:1 | advisory |
| mind ink on the page | body | 92.7 | 10.80:1 | pass |
| mind line on the band | nonText | 70.0 | 4.89:1 | pass |
| nutrition ink on nutrition surface | body | 89.3 | 10.06:1 | pass |
| nutrition line on nutrition surface | nonText | 69.7 | 4.78:1 | pass |
| nutrition accent on nutrition surface | nonText | 39.2 | 2.11:1 | advisory - below floor |
| nutrition accent on the page | nonText | 41.8 | 2.19:1 | advisory - below floor |
| nutrition ink on the page | body | 91.9 | 10.44:1 | pass |
| nutrition line on the band | nonText | 68.8 | 4.71:1 | pass |
| sleep ink on sleep surface | body | 89.6 | 10.19:1 | pass |
| sleep line on sleep surface | nonText | 70.0 | 4.80:1 | pass |
| sleep accent on sleep surface | nonText | 64.6 | 4.05:1 | advisory |
| sleep accent on the page | nonText | 67.3 | 4.22:1 | advisory |
| sleep ink on the page | body | 92.3 | 10.60:1 | pass |
| sleep line on the band | nonText | 69.1 | 4.75:1 | pass |
| activity ink on the card material | body | 93.6 | 10.34:1 | pass |
| heart ink on the card material | body | 95.1 | 11.47:1 | pass |
| labs ink on the card material | body | 94.0 | 10.54:1 | pass |
| mind ink on the card material | body | 95.7 | 11.28:1 | pass |
| nutrition ink on the card material | body | 95.0 | 10.91:1 | pass |
| sleep ink on the card material | body | 95.4 | 11.08:1 | pass |
#### Dark theme [#dark-theme]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------- | ------- | ------- | -------- | -------- |
| activity ink on activity surface | body | -83.1 | 12.58:1 | pass |
| activity line on activity surface | nonText | -69.0 | 9.93:1 | pass |
| activity accent on activity surface | nonText | -53.3 | 7.26:1 | advisory |
| activity accent on the page | nonText | -54.6 | 8.22:1 | advisory |
| activity ink on the page | body | -84.4 | 14.24:1 | pass |
| activity line on the band | nonText | -67.4 | 8.60:1 | pass |
| heart ink on heart surface | body | -80.5 | 12.55:1 | pass |
| heart line on heart surface | nonText | -64.5 | 9.46:1 | pass |
| heart accent on heart surface | nonText | -48.2 | 6.68:1 | advisory |
| heart accent on the page | nonText | -49.5 | 7.27:1 | advisory |
| heart ink on the page | body | -81.8 | 13.65:1 | pass |
| heart line on the band | nonText | -62.8 | 7.88:1 | pass |
| labs ink on labs surface | body | -83.1 | 12.69:1 | pass |
| labs line on labs surface | nonText | -68.4 | 9.90:1 | pass |
| labs accent on labs surface | nonText | -52.7 | 7.21:1 | advisory |
| labs accent on the page | nonText | -53.9 | 8.07:1 | advisory |
| labs ink on the page | body | -84.3 | 14.22:1 | pass |
| labs line on the band | nonText | -66.7 | 8.49:1 | pass |
| mind ink on mind surface | body | -81.3 | 12.56:1 | pass |
| mind line on mind surface | nonText | -65.4 | 9.54:1 | pass |
| mind accent on mind surface | nonText | -49.1 | 6.77:1 | advisory |
| mind accent on the page | nonText | -50.0 | 7.42:1 | advisory |
| mind ink on the page | body | -82.2 | 13.77:1 | pass |
| mind line on the band | nonText | -63.4 | 8.00:1 | pass |
| nutrition ink on nutrition surface | body | -81.9 | 12.55:1 | pass |
| nutrition line on nutrition surface | nonText | -66.7 | 9.66:1 | pass |
| nutrition accent on nutrition surface | nonText | -50.2 | 6.88:1 | advisory |
| nutrition accent on the page | nonText | -51.3 | 7.63:1 | advisory |
| nutrition ink on the page | body | -82.9 | 13.91:1 | pass |
| nutrition line on the band | nonText | -64.8 | 8.19:1 | pass |
| sleep ink on sleep surface | body | -81.9 | 12.60:1 | pass |
| sleep line on sleep surface | nonText | -66.4 | 9.65:1 | pass |
| sleep accent on sleep surface | nonText | -50.4 | 6.94:1 | advisory |
| sleep accent on the page | nonText | -51.4 | 7.67:1 | advisory |
| sleep ink on the page | body | -82.9 | 13.92:1 | pass |
| sleep line on the band | nonText | -64.5 | 8.16:1 | pass |
| activity ink on the card material | body | -83.5 | 12.91:1 | pass |
| heart ink on the card material | body | -80.9 | 12.38:1 | pass |
| labs ink on the card material | body | -83.5 | 12.90:1 | pass |
| mind ink on the card material | body | -81.4 | 12.49:1 | pass |
| nutrition ink on the card material | body | -82.0 | 12.61:1 | pass |
| sleep ink on the card material | body | -82.1 | 12.62:1 | pass |
### status [#status]
#### Light theme [#light-theme-1]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| --------------------------------------- | ------- | ------- | -------- | ---------------------- |
| attention ink on attention surface | body | 89.4 | 10.35:1 | pass |
| attention line on attention surface | nonText | 70.0 | 4.90:1 | pass |
| attention accent on attention surface | nonText | 65.1 | 4.18:1 | advisory |
| attention ink on the attention band | nonText | 22.4 | 2.48:1 | FAIL |
| attention accent on the page | nonText | 67.8 | 4.35:1 | advisory |
| attention ink on the page | body | 92.2 | 10.77:1 | pass |
| steady ink on steady surface | body | 88.3 | 9.62:1 | pass |
| steady line on steady surface | nonText | 67.5 | 4.45:1 | pass |
| steady accent on steady surface | nonText | 56.4 | 3.21:1 | advisory |
| steady ink on the steady band | nonText | 30.0 | 3.00:1 | FAIL |
| steady accent on the page | nonText | 58.6 | 3.31:1 | advisory |
| steady ink on the page | body | 90.6 | 9.94:1 | pass |
| unknown ink on unknown surface | body | 89.5 | 9.98:1 | pass |
| unknown line on unknown surface | nonText | 69.2 | 4.64:1 | pass |
| unknown accent on unknown surface | nonText | 43.1 | 2.29:1 | advisory - below floor |
| unknown accent on the page | nonText | 45.6 | 2.37:1 | advisory - below floor |
| unknown ink on the page | body | 92.0 | 10.34:1 | pass |
| urgent ink on urgent surface | body | 88.2 | 10.58:1 | pass |
| urgent line on urgent surface | nonText | 70.0 | 5.09:1 | pass |
| urgent accent on urgent surface | nonText | 72.3 | 5.58:1 | advisory |
| urgent ink on the urgent band | nonText | 13.9 | 1.90:1 | FAIL |
| urgent accent on the page | nonText | 75.3 | 5.84:1 | advisory |
| urgent ink on the page | body | 91.2 | 11.06:1 | pass |
| watch ink on watch surface | body | 89.4 | 10.02:1 | pass |
| watch line on watch surface | nonText | 69.5 | 4.72:1 | pass |
| watch accent on watch surface | nonText | 61.7 | 3.72:1 | advisory |
| watch ink on the watch band | nonText | 25.7 | 2.70:1 | FAIL |
| watch accent on the page | nonText | 64.2 | 3.85:1 | advisory |
| watch ink on the page | body | 91.9 | 10.38:1 | pass |
| attention surface on the card material | nonText | 0.0 | 1.09:1 | advisory - below floor |
| steady surface on the card material | nonText | 0.0 | 1.08:1 | advisory - below floor |
| urgent surface on the card material | nonText | 0.0 | 1.09:1 | advisory - below floor |
| watch surface on the card material | nonText | 0.0 | 1.08:1 | advisory - below floor |
| the focus ring on the attention surface | nonText | 67.3 | 4.44:1 | advisory |
| attention ink on the neutral hover fill | body | 88.6 | 10.23:1 | pass |
| the focus ring on the steady surface | nonText | 67.8 | 4.47:1 | advisory |
| steady ink on the neutral hover fill | body | 87.0 | 9.43:1 | pass |
| the focus ring on the urgent surface | nonText | 67.0 | 4.42:1 | advisory |
| urgent ink on the neutral hover fill | body | 87.7 | 10.50:1 | pass |
| the focus ring on the watch surface | nonText | 67.6 | 4.46:1 | advisory |
| watch ink on the neutral hover fill | body | 88.3 | 9.86:1 | pass |
#### Dark theme [#dark-theme-1]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| --------------------------------------- | ------- | ------- | -------- | ---------------------- |
| attention ink on attention surface | body | -81.1 | 12.54:1 | pass |
| attention line on attention surface | nonText | -65.5 | 9.54:1 | pass |
| attention accent on attention surface | nonText | -49.4 | 6.80:1 | advisory |
| attention ink on the attention band | nonText | -31.0 | 1.84:1 | FAIL |
| attention accent on the page | nonText | -50.5 | 7.47:1 | advisory |
| attention ink on the page | body | -82.3 | 13.76:1 | pass |
| steady ink on steady surface | body | -82.9 | 12.56:1 | pass |
| steady line on steady surface | nonText | -68.9 | 9.91:1 | pass |
| steady accent on steady surface | nonText | -53.2 | 7.25:1 | advisory |
| steady ink on the steady band | nonText | -28.8 | 1.73:1 | FAIL |
| steady accent on the page | nonText | -54.5 | 8.19:1 | advisory |
| steady ink on the page | body | -84.2 | 14.20:1 | pass |
| unknown ink on unknown surface | body | -82.2 | 12.56:1 | pass |
| unknown line on unknown surface | nonText | -67.2 | 9.72:1 | pass |
| unknown accent on unknown surface | nonText | -51.3 | 7.05:1 | advisory |
| unknown accent on the page | nonText | -52.3 | 7.84:1 | advisory |
| unknown ink on the page | body | -83.1 | 13.97:1 | pass |
| urgent ink on urgent surface | body | -80.3 | 12.52:1 | pass |
| urgent line on urgent surface | nonText | -64.8 | 9.52:1 | pass |
| urgent accent on urgent surface | nonText | -48.0 | 6.63:1 | advisory |
| urgent ink on the urgent band | nonText | -31.5 | 1.89:1 | FAIL |
| urgent accent on the page | nonText | -49.5 | 7.24:1 | advisory |
| urgent ink on the page | body | -81.8 | 13.66:1 | pass |
| watch ink on watch surface | body | -82.1 | 12.58:1 | pass |
| watch line on watch surface | nonText | -66.8 | 9.68:1 | pass |
| watch accent on watch surface | nonText | -50.7 | 6.95:1 | advisory |
| watch ink on the watch band | nonText | -30.6 | 1.81:1 | FAIL |
| watch accent on the page | nonText | -51.7 | 7.71:1 | advisory |
| watch ink on the page | body | -83.1 | 13.95:1 | pass |
| attention surface on the card material | nonText | 0.0 | 1.00:1 | advisory - below floor |
| steady surface on the card material | nonText | 0.0 | 1.03:1 | advisory - below floor |
| urgent surface on the card material | nonText | 0.0 | 1.01:1 | advisory - below floor |
| watch surface on the card material | nonText | 0.0 | 1.01:1 | advisory - below floor |
| the focus ring on the attention surface | nonText | -45.9 | 6.30:1 | advisory |
| attention ink on the neutral hover fill | body | -79.3 | 10.53:1 | pass |
| the focus ring on the steady surface | nonText | -45.8 | 6.12:1 | advisory |
| steady ink on the neutral hover fill | body | -81.3 | 10.86:1 | pass |
| the focus ring on the urgent surface | nonText | -45.5 | 6.34:1 | advisory |
| urgent ink on the neutral hover fill | body | -78.9 | 10.46:1 | pass |
| the focus ring on the watch surface | nonText | -46.0 | 6.24:1 | advisory |
| watch ink on the neutral hover fill | body | -80.1 | 10.67:1 | pass |
### surfaces [#surfaces]
#### Light theme [#light-theme-2]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------- | ------- | ------- | -------- | ---------------------- |
| the band fill on the page | nonText | 0.0 | 1.05:1 | advisory - below floor |
| the band edge on the page | nonText | 83.9 | 7.42:1 | pass |
| the band edge on the band | nonText | 80.4 | 7.05:1 | pass |
#### Dark theme [#dark-theme-2]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------- | ------- | ------- | -------- | ---------------------- |
| the band fill on the page | nonText | 0.0 | 1.31:1 | advisory - below floor |
| the band edge on the page | nonText | -80.7 | 13.44:1 | pass |
| the band edge on the band | nonText | -77.8 | 10.28:1 | pass |
### neutral [#neutral]
#### Light theme [#light-theme-3]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------------ | ------- | ------- | -------- | ---------------------- |
| body text on the page | body | 101.6 | 17.18:1 | pass |
| body text on a card | body | 104.7 | 17.96:1 | pass |
| body text on the muted ground | body | 98.1 | 16.32:1 | pass |
| secondary text on the page | body | 83.9 | 7.42:1 | pass |
| secondary text on a card | body | 87.0 | 7.76:1 | pass |
| secondary text on the muted ground | body | 80.4 | 7.05:1 | pass |
| a hairline boundary on the page | nonText | 19.3 | 1.41:1 | advisory - below floor |
| a hairline boundary on a card | nonText | 22.4 | 1.47:1 | advisory - below floor |
| a hairline boundary on the muted ground | nonText | 15.8 | 1.34:1 | advisory - below floor |
| a placeholder boundary on the page | nonText | 69.8 | 4.52:1 | pass |
| a placeholder boundary on a card | nonText | 72.8 | 4.72:1 | pass |
| a placeholder boundary on the muted ground | nonText | 66.2 | 4.29:1 | pass |
| a placeholder fill on the page | nonText | 8.3 | 1.18:1 | advisory - below floor |
| a placeholder fill on a card | nonText | 11.4 | 1.23:1 | advisory - below floor |
| a placeholder fill on the muted ground | nonText | 0.0 | 1.12:1 | advisory - below floor |
| a placeholder boundary on its own fill | nonText | 58.9 | 3.84:1 | pass |
| the card hairline on the page | nonText | 69.8 | 4.52:1 | pass |
| the card hairline on a card | nonText | 72.8 | 4.72:1 | pass |
#### Dark theme [#dark-theme-3]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------------ | ------- | ------- | -------- | ---------------------- |
| body text on the page | body | -100.5 | 18.00:1 | pass |
| body text on a card | body | -99.6 | 16.32:1 | pass |
| body text on the muted ground | body | -97.5 | 13.77:1 | pass |
| secondary text on the page | body | -80.7 | 13.44:1 | pass |
| secondary text on a card | body | -79.8 | 12.19:1 | pass |
| secondary text on the muted ground | body | -77.8 | 10.28:1 | pass |
| a hairline boundary on the page | nonText | -8.2 | 1.90:1 | advisory - below floor |
| a hairline boundary on a card | nonText | -7.3 | 1.72:1 | advisory - below floor |
| a hairline boundary on the muted ground | nonText | 0.0 | 1.45:1 | advisory - below floor |
| a placeholder boundary on the page | nonText | -51.0 | 7.62:1 | pass |
| a placeholder boundary on a card | nonText | -50.1 | 6.91:1 | pass |
| a placeholder boundary on the muted ground | nonText | -48.1 | 5.83:1 | pass |
| a placeholder fill on the page | nonText | -8.2 | 1.90:1 | advisory - below floor |
| a placeholder fill on a card | nonText | -7.3 | 1.72:1 | advisory - below floor |
| a placeholder fill on the muted ground | nonText | 0.0 | 1.45:1 | advisory - below floor |
| a placeholder boundary on its own fill | nonText | -41.5 | 4.01:1 | FAIL |
| the card hairline on the page | nonText | -28.7 | 4.19:1 | FAIL |
| the card hairline on a card | nonText | -27.9 | 3.80:1 | FAIL |
### chrome [#chrome]
#### Light theme [#light-theme-4]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| -------------------------- | ------- | ------- | -------- | ------- |
| the focus ring on the page | nonText | 70.0 | 4.62:1 | pass |
| the focus ring on a card | nonText | 73.1 | 4.83:1 | pass |
#### Dark theme [#dark-theme-4]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| -------------------------- | ------- | ------- | -------- | ------- |
| the focus ring on the page | nonText | -47.0 | 6.91:1 | pass |
| the focus ring on a card | nonText | -46.2 | 6.27:1 | pass |
### materials [#materials]
#### Light theme [#light-theme-5]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------------------------------ | ---- | ------- | -------- | ------- |
| body text on the canvas material | body | 104.7 | 17.96:1 | pass |
| body text on the inset material | body | 98.1 | 16.32:1 | pass |
| body text on the card material | body | 104.7 | 17.96:1 | pass |
| body text on the raised material | body | 104.7 | 17.96:1 | pass |
| body text on the sheet material over the darkest backdrop | body | 99.9 | 16.76:1 | pass |
| body text on the sheet material over the lightest backdrop | body | 104.7 | 17.96:1 | pass |
| body text on the overlay material over the darkest backdrop | body | 85.9 | 13.48:1 | pass |
| body text on the overlay material over the lightest backdrop | body | 104.7 | 17.96:1 | pass |
#### Dark theme [#dark-theme-5]
| Pair | Role | APCA Lc | WCAG 2.2 | Verdict |
| ------------------------------------------------------------ | ---- | ------- | -------- | ------- |
| body text on the canvas material | body | -100.5 | 18.00:1 | pass |
| body text on the inset material | body | -100.1 | 17.18:1 | pass |
| body text on the card material | body | -99.6 | 16.32:1 | pass |
| body text on the raised material | body | -97.5 | 13.77:1 | pass |
| body text on the sheet material over the darkest backdrop | body | -99.7 | 16.47:1 | pass |
| body text on the sheet material over the lightest backdrop | body | -80.8 | 6.07:1 | pass |
| body text on the overlay material over the darkest backdrop | body | -99.9 | 16.77:1 | pass |
| body text on the overlay material over the lightest backdrop | body | -54.2 | 2.69:1 | FAIL |
## Colour-vision audit [#colour-vision-audit]
Pairs that fall below Lc 15 as flat fills - that is, pairs a reader with this form of colour vision cannot tell apart by colour alone.
| Axis | Simulation | Pair | Lc |
| -------- | ------------ | -------------------------------------- | ---- |
| status | protanopia | steady-surface and watch-surface | 0.0 |
| status | protanopia | steady-surface and attention-surface | 0.0 |
| status | protanopia | steady-surface and urgent-surface | 0.0 |
| status | protanopia | steady-surface and unknown-surface | 0.0 |
| status | protanopia | steady-line and steady-accent | 9.6 |
| status | protanopia | steady-line and watch-line | 0.0 |
| status | protanopia | steady-line and watch-accent | 0.0 |
| status | protanopia | steady-line and attention-line | 12.1 |
| status | protanopia | steady-line and attention-accent | 7.8 |
| status | protanopia | steady-line and unknown-line | 0.0 |
| status | protanopia | steady-ink and watch-line | 13.3 |
| status | protanopia | steady-ink and watch-ink | 0.0 |
| status | protanopia | steady-ink and attention-line | 7.5 |
| status | protanopia | steady-ink and attention-ink | 0.0 |
| status | protanopia | steady-ink and attention-accent | 12.1 |
| status | protanopia | steady-ink and urgent-line | 0.0 |
| status | protanopia | steady-ink and urgent-ink | 0.0 |
| status | protanopia | steady-ink and urgent-accent | 0.0 |
| status | protanopia | steady-ink and unknown-ink | 0.0 |
| status | protanopia | steady-accent and watch-accent | 10.9 |
| status | protanopia | steady-accent and unknown-accent | 8.1 |
| status | protanopia | watch-surface and attention-surface | 0.0 |
| status | protanopia | watch-surface and urgent-surface | 0.0 |
| status | protanopia | watch-surface and unknown-surface | 0.0 |
| status | protanopia | watch-line and watch-accent | 0.0 |
| status | protanopia | watch-line and attention-line | 0.0 |
| status | protanopia | watch-line and attention-accent | 0.0 |
| status | protanopia | watch-line and urgent-line | 9.3 |
| status | protanopia | watch-line and urgent-accent | 12.2 |
| status | protanopia | watch-line and unknown-line | 0.0 |
| status | protanopia | watch-ink and attention-line | 11.2 |
| status | protanopia | watch-ink and attention-ink | 0.0 |
| status | protanopia | watch-ink and urgent-line | 0.0 |
| status | protanopia | watch-ink and urgent-ink | 0.0 |
| status | protanopia | watch-ink and urgent-accent | 0.0 |
| status | protanopia | watch-ink and unknown-ink | 0.0 |
| status | protanopia | watch-accent and attention-line | 12.1 |
| status | protanopia | watch-accent and attention-accent | 7.7 |
| status | protanopia | watch-accent and unknown-line | 0.0 |
| status | protanopia | attention-surface and urgent-surface | 0.0 |
| status | protanopia | attention-surface and unknown-surface | 0.0 |
| status | protanopia | attention-line and attention-ink | 13.5 |
| status | protanopia | attention-line and attention-accent | 0.0 |
| status | protanopia | attention-line and urgent-line | 0.0 |
| status | protanopia | attention-line and urgent-accent | 0.0 |
| status | protanopia | attention-line and unknown-line | 0.0 |
| status | protanopia | attention-line and unknown-ink | 9.7 |
| status | protanopia | attention-ink and urgent-line | 9.1 |
| status | protanopia | attention-ink and urgent-ink | 0.0 |
| status | protanopia | attention-ink and urgent-accent | 0.0 |
| status | protanopia | attention-ink and unknown-ink | 0.0 |
| status | protanopia | attention-accent and urgent-line | 8.2 |
| status | protanopia | attention-accent and urgent-accent | 11.0 |
| status | protanopia | attention-accent and unknown-line | 0.0 |
| status | protanopia | attention-accent and unknown-ink | 14.1 |
| status | protanopia | urgent-surface and unknown-surface | 0.0 |
| status | protanopia | urgent-line and urgent-ink | 11.0 |
| status | protanopia | urgent-line and urgent-accent | 0.0 |
| status | protanopia | urgent-line and unknown-line | 11.6 |
| status | protanopia | urgent-line and unknown-ink | 0.0 |
| status | protanopia | urgent-ink and urgent-accent | 8.7 |
| status | protanopia | urgent-ink and unknown-ink | 0.0 |
| status | protanopia | urgent-accent and unknown-line | 14.7 |
| status | protanopia | urgent-accent and unknown-ink | 0.0 |
| status | deuteranopia | steady-surface and watch-surface | 0.0 |
| status | deuteranopia | steady-surface and attention-surface | 0.0 |
| status | deuteranopia | steady-surface and urgent-surface | 0.0 |
| status | deuteranopia | steady-surface and unknown-surface | 0.0 |
| status | deuteranopia | steady-line and steady-accent | 8.7 |
| status | deuteranopia | steady-line and watch-line | 0.0 |
| status | deuteranopia | steady-line and watch-accent | 7.6 |
| status | deuteranopia | steady-line and attention-line | 0.0 |
| status | deuteranopia | steady-line and attention-accent | 0.0 |
| status | deuteranopia | steady-line and urgent-line | 0.0 |
| status | deuteranopia | steady-line and urgent-accent | 0.0 |
| status | deuteranopia | steady-line and unknown-line | 0.0 |
| status | deuteranopia | steady-ink and watch-ink | 0.0 |
| status | deuteranopia | steady-ink and attention-ink | 0.0 |
| status | deuteranopia | steady-ink and urgent-ink | 0.0 |
| status | deuteranopia | steady-ink and unknown-ink | 0.0 |
| status | deuteranopia | steady-accent and watch-line | 8.5 |
| status | deuteranopia | steady-accent and watch-accent | 0.0 |
| status | deuteranopia | steady-accent and attention-line | 8.0 |
| status | deuteranopia | steady-accent and attention-accent | 0.0 |
| status | deuteranopia | steady-accent and urgent-line | 8.0 |
| status | deuteranopia | steady-accent and urgent-accent | 10.2 |
| status | deuteranopia | steady-accent and unknown-line | 10.0 |
| status | deuteranopia | steady-accent and unknown-accent | 13.4 |
| status | deuteranopia | watch-surface and attention-surface | 0.0 |
| status | deuteranopia | watch-surface and urgent-surface | 0.0 |
| status | deuteranopia | watch-surface and unknown-surface | 0.0 |
| status | deuteranopia | watch-line and watch-accent | 0.0 |
| status | deuteranopia | watch-line and attention-line | 0.0 |
| status | deuteranopia | watch-line and attention-accent | 0.0 |
| status | deuteranopia | watch-line and urgent-line | 0.0 |
| status | deuteranopia | watch-line and urgent-accent | 0.0 |
| status | deuteranopia | watch-line and unknown-line | 0.0 |
| status | deuteranopia | watch-ink and attention-ink | 0.0 |
| status | deuteranopia | watch-ink and urgent-ink | 0.0 |
| status | deuteranopia | watch-ink and unknown-ink | 0.0 |
| status | deuteranopia | watch-accent and attention-line | 0.0 |
| status | deuteranopia | watch-accent and attention-accent | 0.0 |
| status | deuteranopia | watch-accent and urgent-line | 0.0 |
| status | deuteranopia | watch-accent and urgent-accent | 9.0 |
| status | deuteranopia | watch-accent and unknown-line | 8.8 |
| status | deuteranopia | watch-accent and unknown-accent | 14.6 |
| status | deuteranopia | attention-surface and urgent-surface | 0.0 |
| status | deuteranopia | attention-surface and unknown-surface | 0.0 |
| status | deuteranopia | attention-line and attention-accent | 0.0 |
| status | deuteranopia | attention-line and urgent-line | 0.0 |
| status | deuteranopia | attention-line and urgent-accent | 0.0 |
| status | deuteranopia | attention-line and unknown-line | 0.0 |
| status | deuteranopia | attention-ink and urgent-ink | 0.0 |
| status | deuteranopia | attention-ink and unknown-ink | 0.0 |
| status | deuteranopia | attention-accent and urgent-line | 0.0 |
| status | deuteranopia | attention-accent and urgent-accent | 0.0 |
| status | deuteranopia | attention-accent and unknown-line | 0.0 |
| status | deuteranopia | urgent-surface and unknown-surface | 0.0 |
| status | deuteranopia | urgent-line and urgent-accent | 0.0 |
| status | deuteranopia | urgent-line and unknown-line | 0.0 |
| status | deuteranopia | urgent-ink and unknown-ink | 0.0 |
| status | deuteranopia | urgent-accent and unknown-line | 0.0 |
| status | tritanopia | steady-surface and watch-surface | 0.0 |
| status | tritanopia | steady-surface and attention-surface | 0.0 |
| status | tritanopia | steady-surface and urgent-surface | 0.0 |
| status | tritanopia | steady-surface and unknown-surface | 0.0 |
| status | tritanopia | steady-line and steady-accent | 0.0 |
| status | tritanopia | steady-line and watch-line | 0.0 |
| status | tritanopia | steady-line and watch-accent | 10.3 |
| status | tritanopia | steady-line and attention-line | 7.4 |
| status | tritanopia | steady-line and attention-ink | 14.1 |
| status | tritanopia | steady-line and attention-accent | 13.6 |
| status | tritanopia | steady-line and urgent-line | 11.7 |
| status | tritanopia | steady-line and urgent-ink | 11.3 |
| status | tritanopia | steady-line and urgent-accent | 9.3 |
| status | tritanopia | steady-line and unknown-line | 0.0 |
| status | tritanopia | steady-ink and watch-ink | 0.0 |
| status | tritanopia | steady-ink and attention-ink | 0.0 |
| status | tritanopia | steady-ink and urgent-ink | 0.0 |
| status | tritanopia | steady-ink and unknown-ink | 0.0 |
| status | tritanopia | steady-accent and watch-line | 0.0 |
| status | tritanopia | steady-accent and watch-accent | 0.0 |
| status | tritanopia | steady-accent and attention-line | 0.0 |
| status | tritanopia | steady-accent and attention-accent | 0.0 |
| status | tritanopia | steady-accent and urgent-line | 0.0 |
| status | tritanopia | steady-accent and urgent-accent | 0.0 |
| status | tritanopia | steady-accent and unknown-line | 9.4 |
| status | tritanopia | steady-accent and unknown-accent | 13.8 |
| status | tritanopia | watch-surface and attention-surface | 0.0 |
| status | tritanopia | watch-surface and urgent-surface | 0.0 |
| status | tritanopia | watch-surface and unknown-surface | 0.0 |
| status | tritanopia | watch-line and watch-accent | 0.0 |
| status | tritanopia | watch-line and attention-line | 0.0 |
| status | tritanopia | watch-line and attention-accent | 10.2 |
| status | tritanopia | watch-line and urgent-line | 8.3 |
| status | tritanopia | watch-line and urgent-ink | 14.7 |
| status | tritanopia | watch-line and urgent-accent | 0.0 |
| status | tritanopia | watch-line and unknown-line | 0.0 |
| status | tritanopia | watch-ink and attention-ink | 0.0 |
| status | tritanopia | watch-ink and urgent-ink | 0.0 |
| status | tritanopia | watch-ink and unknown-ink | 0.0 |
| status | tritanopia | watch-accent and attention-line | 0.0 |
| status | tritanopia | watch-accent and attention-accent | 0.0 |
| status | tritanopia | watch-accent and urgent-line | 0.0 |
| status | tritanopia | watch-accent and urgent-accent | 0.0 |
| status | tritanopia | watch-accent and unknown-line | 12.6 |
| status | tritanopia | watch-accent and unknown-accent | 10.5 |
| status | tritanopia | attention-surface and urgent-surface | 0.0 |
| status | tritanopia | attention-surface and unknown-surface | 0.0 |
| status | tritanopia | attention-line and attention-accent | 0.0 |
| status | tritanopia | attention-line and urgent-line | 0.0 |
| status | tritanopia | attention-line and urgent-accent | 0.0 |
| status | tritanopia | attention-line and unknown-line | 9.8 |
| status | tritanopia | attention-line and unknown-accent | 13.4 |
| status | tritanopia | attention-ink and urgent-ink | 0.0 |
| status | tritanopia | attention-ink and unknown-line | 12.6 |
| status | tritanopia | attention-ink and unknown-ink | 0.0 |
| status | tritanopia | attention-accent and urgent-line | 0.0 |
| status | tritanopia | attention-accent and urgent-accent | 0.0 |
| status | tritanopia | attention-accent and unknown-accent | 0.0 |
| status | tritanopia | urgent-surface and unknown-surface | 0.0 |
| status | tritanopia | urgent-line and urgent-accent | 0.0 |
| status | tritanopia | urgent-line and unknown-line | 14.1 |
| status | tritanopia | urgent-line and unknown-accent | 9.1 |
| status | tritanopia | urgent-ink and unknown-line | 9.5 |
| status | tritanopia | urgent-ink and unknown-ink | 0.0 |
| status | tritanopia | urgent-accent and unknown-line | 11.7 |
| status | tritanopia | urgent-accent and unknown-accent | 11.5 |
| status | grayscale | steady-surface and watch-surface | 0.0 |
| status | grayscale | steady-surface and attention-surface | 0.0 |
| status | grayscale | steady-surface and urgent-surface | 0.0 |
| status | grayscale | steady-surface and unknown-surface | 0.0 |
| status | grayscale | steady-line and steady-accent | 9.4 |
| status | grayscale | steady-line and watch-line | 0.0 |
| status | grayscale | steady-line and watch-accent | 0.0 |
| status | grayscale | steady-line and attention-line | 0.0 |
| status | grayscale | steady-line and attention-accent | 0.0 |
| status | grayscale | steady-line and urgent-line | 0.0 |
| status | grayscale | steady-line and urgent-accent | 0.0 |
| status | grayscale | steady-line and unknown-line | 0.0 |
| status | grayscale | steady-ink and watch-ink | 0.0 |
| status | grayscale | steady-ink and attention-ink | 0.0 |
| status | grayscale | steady-ink and urgent-line | 14.2 |
| status | grayscale | steady-ink and urgent-ink | 0.0 |
| status | grayscale | steady-ink and urgent-accent | 11.6 |
| status | grayscale | steady-ink and unknown-ink | 0.0 |
| status | grayscale | steady-accent and watch-line | 12.3 |
| status | grayscale | steady-accent and watch-accent | 0.0 |
| status | grayscale | steady-accent and attention-line | 13.6 |
| status | grayscale | steady-accent and attention-accent | 8.8 |
| status | grayscale | steady-accent and urgent-line | 14.9 |
| status | grayscale | steady-accent and unknown-line | 11.9 |
| status | grayscale | steady-accent and unknown-accent | 11.6 |
| status | grayscale | watch-surface and attention-surface | 0.0 |
| status | grayscale | watch-surface and urgent-surface | 0.0 |
| status | grayscale | watch-surface and unknown-surface | 0.0 |
| status | grayscale | watch-line and watch-accent | 0.0 |
| status | grayscale | watch-line and attention-line | 0.0 |
| status | grayscale | watch-line and attention-accent | 0.0 |
| status | grayscale | watch-line and urgent-line | 0.0 |
| status | grayscale | watch-line and urgent-accent | 0.0 |
| status | grayscale | watch-line and unknown-line | 0.0 |
| status | grayscale | watch-ink and attention-ink | 0.0 |
| status | grayscale | watch-ink and urgent-ink | 0.0 |
| status | grayscale | watch-ink and urgent-accent | 12.7 |
| status | grayscale | watch-ink and unknown-ink | 0.0 |
| status | grayscale | watch-accent and attention-line | 8.3 |
| status | grayscale | watch-accent and attention-accent | 0.0 |
| status | grayscale | watch-accent and urgent-line | 9.6 |
| status | grayscale | watch-accent and urgent-accent | 12.1 |
| status | grayscale | watch-accent and unknown-line | 0.0 |
| status | grayscale | attention-surface and urgent-surface | 0.0 |
| status | grayscale | attention-surface and unknown-surface | 0.0 |
| status | grayscale | attention-line and attention-accent | 0.0 |
| status | grayscale | attention-line and urgent-line | 0.0 |
| status | grayscale | attention-line and urgent-accent | 0.0 |
| status | grayscale | attention-line and unknown-line | 0.0 |
| status | grayscale | attention-ink and urgent-ink | 0.0 |
| status | grayscale | attention-ink and urgent-accent | 13.4 |
| status | grayscale | attention-ink and unknown-ink | 0.0 |
| status | grayscale | attention-accent and urgent-line | 0.0 |
| status | grayscale | attention-accent and urgent-accent | 7.9 |
| status | grayscale | attention-accent and unknown-line | 0.0 |
| status | grayscale | urgent-surface and unknown-surface | 0.0 |
| status | grayscale | urgent-line and urgent-accent | 0.0 |
| status | grayscale | urgent-line and unknown-line | 0.0 |
| status | grayscale | urgent-line and unknown-ink | 14.8 |
| status | grayscale | urgent-ink and urgent-accent | 14.1 |
| status | grayscale | urgent-ink and unknown-ink | 0.0 |
| status | grayscale | urgent-accent and unknown-line | 0.0 |
| status | grayscale | urgent-accent and unknown-ink | 12.2 |
| category | protanopia | sleep-surface and heart-surface | 0.0 |
| category | protanopia | sleep-surface and activity-surface | 0.0 |
| category | protanopia | sleep-surface and nutrition-surface | 0.0 |
| category | protanopia | sleep-surface and mind-surface | 0.0 |
| category | protanopia | sleep-surface and labs-surface | 0.0 |
| category | protanopia | sleep-line and sleep-accent | 0.0 |
| category | protanopia | sleep-line and heart-line | 9.2 |
| category | protanopia | sleep-line and heart-accent | 0.0 |
| category | protanopia | sleep-line and activity-line | 0.0 |
| category | protanopia | sleep-line and nutrition-line | 0.0 |
| category | protanopia | sleep-line and mind-line | 0.0 |
| category | protanopia | sleep-line and mind-accent | 0.0 |
| category | protanopia | sleep-line and labs-line | 0.0 |
| category | protanopia | sleep-line and labs-accent | 11.7 |
| category | protanopia | sleep-ink and heart-line | 7.5 |
| category | protanopia | sleep-ink and heart-ink | 0.0 |
| category | protanopia | sleep-ink and activity-ink | 0.0 |
| category | protanopia | sleep-ink and nutrition-line | 14.4 |
| category | protanopia | sleep-ink and nutrition-ink | 0.0 |
| category | protanopia | sleep-ink and mind-line | 13.9 |
| category | protanopia | sleep-ink and mind-ink | 0.0 |
| category | protanopia | sleep-ink and labs-ink | 0.0 |
| category | protanopia | sleep-accent and heart-line | 14.3 |
| category | protanopia | sleep-accent and heart-accent | 0.0 |
| category | protanopia | sleep-accent and activity-line | 0.0 |
| category | protanopia | sleep-accent and nutrition-line | 7.9 |
| category | protanopia | sleep-accent and mind-line | 8.4 |
| category | protanopia | sleep-accent and mind-accent | 0.0 |
| category | protanopia | sleep-accent and labs-line | 0.0 |
| category | protanopia | sleep-accent and labs-accent | 0.0 |
| category | protanopia | heart-surface and activity-surface | 0.0 |
| category | protanopia | heart-surface and nutrition-surface | 0.0 |
| category | protanopia | heart-surface and mind-surface | 0.0 |
| category | protanopia | heart-surface and labs-surface | 0.0 |
| category | protanopia | heart-line and heart-ink | 12.3 |
| category | protanopia | heart-line and heart-accent | 7.4 |
| category | protanopia | heart-line and activity-line | 13.7 |
| category | protanopia | heart-line and activity-ink | 0.0 |
| category | protanopia | heart-line and nutrition-line | 0.0 |
| category | protanopia | heart-line and nutrition-ink | 9.0 |
| category | protanopia | heart-line and mind-line | 0.0 |
| category | protanopia | heart-line and mind-ink | 9.4 |
| category | protanopia | heart-line and mind-accent | 14.8 |
| category | protanopia | heart-line and labs-line | 13.5 |
| category | protanopia | heart-line and labs-ink | 0.0 |
| category | protanopia | heart-ink and activity-ink | 0.0 |
| category | protanopia | heart-ink and nutrition-ink | 0.0 |
| category | protanopia | heart-ink and mind-ink | 0.0 |
| category | protanopia | heart-ink and labs-ink | 0.0 |
| category | protanopia | heart-accent and activity-line | 0.0 |
| category | protanopia | heart-accent and activity-ink | 14.1 |
| category | protanopia | heart-accent and nutrition-line | 0.0 |
| category | protanopia | heart-accent and mind-line | 0.0 |
| category | protanopia | heart-accent and mind-accent | 0.0 |
| category | protanopia | heart-accent and labs-line | 0.0 |
| category | protanopia | heart-accent and labs-ink | 14.6 |
| category | protanopia | heart-accent and labs-accent | 12.9 |
| category | protanopia | activity-surface and nutrition-surface | 0.0 |
| category | protanopia | activity-surface and mind-surface | 0.0 |
| category | protanopia | activity-surface and labs-surface | 0.0 |
| category | protanopia | activity-line and nutrition-line | 7.8 |
| category | protanopia | activity-line and mind-line | 8.3 |
| category | protanopia | activity-line and mind-accent | 0.0 |
| category | protanopia | activity-line and labs-line | 0.0 |
| category | protanopia | activity-line and labs-accent | 0.0 |
| category | protanopia | activity-ink and nutrition-line | 11.5 |
| category | protanopia | activity-ink and nutrition-ink | 0.0 |
| category | protanopia | activity-ink and mind-line | 10.9 |
| category | protanopia | activity-ink and mind-ink | 0.0 |
| category | protanopia | activity-ink and labs-ink | 0.0 |
| category | protanopia | activity-accent and nutrition-accent | 0.0 |
| category | protanopia | nutrition-surface and mind-surface | 0.0 |
| category | protanopia | nutrition-surface and labs-surface | 0.0 |
| category | protanopia | nutrition-line and mind-line | 0.0 |
| category | protanopia | nutrition-line and mind-accent | 8.0 |
| category | protanopia | nutrition-line and labs-line | 0.0 |
| category | protanopia | nutrition-line and labs-ink | 12.1 |
| category | protanopia | nutrition-ink and mind-ink | 0.0 |
| category | protanopia | nutrition-ink and labs-ink | 0.0 |
| category | protanopia | nutrition-accent and labs-accent | 11.2 |
| category | protanopia | mind-surface and labs-surface | 0.0 |
| category | protanopia | mind-line and mind-accent | 8.5 |
| category | protanopia | mind-line and labs-line | 0.0 |
| category | protanopia | mind-line and labs-ink | 11.6 |
| category | protanopia | mind-ink and labs-ink | 0.0 |
| category | protanopia | mind-accent and labs-line | 0.0 |
| category | protanopia | mind-accent and labs-accent | 0.0 |
| category | protanopia | labs-line and labs-accent | 0.0 |
| category | deuteranopia | sleep-surface and heart-surface | 0.0 |
| category | deuteranopia | sleep-surface and activity-surface | 0.0 |
| category | deuteranopia | sleep-surface and nutrition-surface | 0.0 |
| category | deuteranopia | sleep-surface and mind-surface | 0.0 |
| category | deuteranopia | sleep-surface and labs-surface | 0.0 |
| category | deuteranopia | sleep-line and sleep-accent | 0.0 |
| category | deuteranopia | sleep-line and heart-line | 0.0 |
| category | deuteranopia | sleep-line and heart-accent | 11.1 |
| category | deuteranopia | sleep-line and activity-line | 0.0 |
| category | deuteranopia | sleep-line and nutrition-line | 0.0 |
| category | deuteranopia | sleep-line and mind-line | 0.0 |
| category | deuteranopia | sleep-line and mind-accent | 9.4 |
| category | deuteranopia | sleep-line and labs-line | 0.0 |
| category | deuteranopia | sleep-line and labs-accent | 0.0 |
| category | deuteranopia | sleep-ink and heart-ink | 0.0 |
| category | deuteranopia | sleep-ink and activity-ink | 0.0 |
| category | deuteranopia | sleep-ink and nutrition-ink | 0.0 |
| category | deuteranopia | sleep-ink and mind-ink | 0.0 |
| category | deuteranopia | sleep-ink and labs-ink | 0.0 |
| category | deuteranopia | sleep-accent and heart-line | 0.0 |
| category | deuteranopia | sleep-accent and heart-accent | 0.0 |
| category | deuteranopia | sleep-accent and activity-line | 0.0 |
| category | deuteranopia | sleep-accent and nutrition-line | 0.0 |
| category | deuteranopia | sleep-accent and mind-line | 0.0 |
| category | deuteranopia | sleep-accent and mind-accent | 0.0 |
| category | deuteranopia | sleep-accent and labs-line | 0.0 |
| category | deuteranopia | sleep-accent and labs-accent | 0.0 |
| category | deuteranopia | heart-surface and activity-surface | 0.0 |
| category | deuteranopia | heart-surface and nutrition-surface | 0.0 |
| category | deuteranopia | heart-surface and mind-surface | 0.0 |
| category | deuteranopia | heart-surface and labs-surface | 0.0 |
| category | deuteranopia | heart-line and heart-accent | 8.8 |
| category | deuteranopia | heart-line and activity-line | 0.0 |
| category | deuteranopia | heart-line and nutrition-line | 0.0 |
| category | deuteranopia | heart-line and mind-line | 0.0 |
| category | deuteranopia | heart-line and mind-accent | 0.0 |
| category | deuteranopia | heart-line and labs-line | 0.0 |
| category | deuteranopia | heart-line and labs-accent | 0.0 |
| category | deuteranopia | heart-ink and activity-ink | 0.0 |
| category | deuteranopia | heart-ink and nutrition-ink | 0.0 |
| category | deuteranopia | heart-ink and mind-ink | 0.0 |
| category | deuteranopia | heart-ink and labs-ink | 0.0 |
| category | deuteranopia | heart-accent and activity-line | 10.8 |
| category | deuteranopia | heart-accent and activity-accent | 9.3 |
| category | deuteranopia | heart-accent and nutrition-line | 10.1 |
| category | deuteranopia | heart-accent and mind-line | 11.7 |
| category | deuteranopia | heart-accent and mind-accent | 0.0 |
| category | deuteranopia | heart-accent and labs-line | 11.9 |
| category | deuteranopia | heart-accent and labs-accent | 0.0 |
| category | deuteranopia | activity-surface and nutrition-surface | 0.0 |
| category | deuteranopia | activity-surface and mind-surface | 0.0 |
| category | deuteranopia | activity-surface and labs-surface | 0.0 |
| category | deuteranopia | activity-line and nutrition-line | 0.0 |
| category | deuteranopia | activity-line and mind-line | 0.0 |
| category | deuteranopia | activity-line and mind-accent | 7.9 |
| category | deuteranopia | activity-line and labs-line | 0.0 |
| category | deuteranopia | activity-line and labs-accent | 0.0 |
| category | deuteranopia | activity-ink and nutrition-ink | 0.0 |
| category | deuteranopia | activity-ink and mind-ink | 0.0 |
| category | deuteranopia | activity-ink and labs-ink | 0.0 |
| category | deuteranopia | activity-accent and nutrition-accent | 0.0 |
| category | deuteranopia | activity-accent and mind-accent | 12.5 |
| category | deuteranopia | nutrition-surface and mind-surface | 0.0 |
| category | deuteranopia | nutrition-surface and labs-surface | 0.0 |
| category | deuteranopia | nutrition-line and mind-line | 0.0 |
| category | deuteranopia | nutrition-line and mind-accent | 0.0 |
| category | deuteranopia | nutrition-line and labs-line | 0.0 |
| category | deuteranopia | nutrition-line and labs-accent | 0.0 |
| category | deuteranopia | nutrition-ink and mind-ink | 0.0 |
| category | deuteranopia | nutrition-ink and labs-ink | 0.0 |
| category | deuteranopia | mind-surface and labs-surface | 0.0 |
| category | deuteranopia | mind-line and mind-accent | 8.8 |
| category | deuteranopia | mind-line and labs-line | 0.0 |
| category | deuteranopia | mind-line and labs-accent | 0.0 |
| category | deuteranopia | mind-ink and labs-ink | 0.0 |
| category | deuteranopia | mind-accent and labs-line | 10.2 |
| category | deuteranopia | mind-accent and labs-accent | 0.0 |
| category | deuteranopia | labs-line and labs-accent | 0.0 |
| category | tritanopia | sleep-surface and heart-surface | 0.0 |
| category | tritanopia | sleep-surface and activity-surface | 0.0 |
| category | tritanopia | sleep-surface and nutrition-surface | 0.0 |
| category | tritanopia | sleep-surface and mind-surface | 0.0 |
| category | tritanopia | sleep-surface and labs-surface | 0.0 |
| category | tritanopia | sleep-line and sleep-accent | 0.0 |
| category | tritanopia | sleep-line and heart-line | 10.9 |
| category | tritanopia | sleep-line and heart-ink | 11.1 |
| category | tritanopia | sleep-line and activity-line | 0.0 |
| category | tritanopia | sleep-line and nutrition-line | 0.0 |
| category | tritanopia | sleep-line and nutrition-ink | 14.2 |
| category | tritanopia | sleep-line and mind-line | 0.0 |
| category | tritanopia | sleep-line and mind-accent | 9.0 |
| category | tritanopia | sleep-line and labs-line | 0.0 |
| category | tritanopia | sleep-line and labs-accent | 0.0 |
| category | tritanopia | sleep-ink and heart-ink | 0.0 |
| category | tritanopia | sleep-ink and activity-ink | 0.0 |
| category | tritanopia | sleep-ink and nutrition-ink | 0.0 |
| category | tritanopia | sleep-ink and mind-ink | 0.0 |
| category | tritanopia | sleep-ink and labs-ink | 0.0 |
| category | tritanopia | sleep-accent and heart-line | 0.0 |
| category | tritanopia | sleep-accent and activity-line | 0.0 |
| category | tritanopia | sleep-accent and nutrition-line | 0.0 |
| category | tritanopia | sleep-accent and mind-line | 0.0 |
| category | tritanopia | sleep-accent and mind-accent | 0.0 |
| category | tritanopia | sleep-accent and labs-line | 0.0 |
| category | tritanopia | sleep-accent and labs-accent | 0.0 |
| category | tritanopia | heart-surface and activity-surface | 0.0 |
| category | tritanopia | heart-surface and nutrition-surface | 0.0 |
| category | tritanopia | heart-surface and mind-surface | 0.0 |
| category | tritanopia | heart-surface and labs-surface | 0.0 |
| category | tritanopia | heart-line and heart-accent | 10.1 |
| category | tritanopia | heart-line and activity-line | 9.9 |
| category | tritanopia | heart-line and activity-accent | 9.3 |
| category | tritanopia | heart-line and nutrition-line | 0.0 |
| category | tritanopia | heart-line and mind-line | 11.9 |
| category | tritanopia | heart-line and mind-accent | 0.0 |
| category | tritanopia | heart-line and labs-line | 10.3 |
| category | tritanopia | heart-line and labs-accent | 0.0 |
| category | tritanopia | heart-ink and activity-line | 12.8 |
| category | tritanopia | heart-ink and activity-ink | 0.0 |
| category | tritanopia | heart-ink and nutrition-ink | 0.0 |
| category | tritanopia | heart-ink and mind-line | 10.8 |
| category | tritanopia | heart-ink and mind-ink | 0.0 |
| category | tritanopia | heart-ink and labs-line | 12.4 |
| category | tritanopia | heart-ink and labs-ink | 0.0 |
| category | tritanopia | heart-accent and activity-accent | 0.0 |
| category | tritanopia | heart-accent and nutrition-accent | 13.9 |
| category | tritanopia | heart-accent and mind-accent | 13.6 |
| category | tritanopia | activity-surface and nutrition-surface | 0.0 |
| category | tritanopia | activity-surface and mind-surface | 0.0 |
| category | tritanopia | activity-surface and labs-surface | 0.0 |
| category | tritanopia | activity-line and nutrition-line | 0.0 |
| category | tritanopia | activity-line and mind-line | 0.0 |
| category | tritanopia | activity-line and mind-accent | 0.0 |
| category | tritanopia | activity-line and labs-line | 0.0 |
| category | tritanopia | activity-line and labs-accent | 0.0 |
| category | tritanopia | activity-ink and nutrition-ink | 0.0 |
| category | tritanopia | activity-ink and mind-ink | 0.0 |
| category | tritanopia | activity-ink and labs-ink | 0.0 |
| category | tritanopia | activity-accent and nutrition-accent | 14.7 |
| category | tritanopia | activity-accent and mind-accent | 12.7 |
| category | tritanopia | nutrition-surface and mind-surface | 0.0 |
| category | tritanopia | nutrition-surface and labs-surface | 0.0 |
| category | tritanopia | nutrition-line and mind-line | 0.0 |
| category | tritanopia | nutrition-line and mind-accent | 0.0 |
| category | tritanopia | nutrition-line and labs-line | 0.0 |
| category | tritanopia | nutrition-line and labs-accent | 0.0 |
| category | tritanopia | nutrition-ink and mind-line | 14.2 |
| category | tritanopia | nutrition-ink and mind-ink | 0.0 |
| category | tritanopia | nutrition-ink and labs-ink | 0.0 |
| category | tritanopia | mind-surface and labs-surface | 0.0 |
| category | tritanopia | mind-line and mind-accent | 8.9 |
| category | tritanopia | mind-line and labs-line | 0.0 |
| category | tritanopia | mind-line and labs-accent | 0.0 |
| category | tritanopia | mind-ink and labs-ink | 0.0 |
| category | tritanopia | mind-accent and labs-line | 8.4 |
| category | tritanopia | mind-accent and labs-accent | 0.0 |
| category | tritanopia | labs-line and labs-accent | 0.0 |
| category | grayscale | sleep-surface and heart-surface | 0.0 |
| category | grayscale | sleep-surface and activity-surface | 0.0 |
| category | grayscale | sleep-surface and nutrition-surface | 0.0 |
| category | grayscale | sleep-surface and mind-surface | 0.0 |
| category | grayscale | sleep-surface and labs-surface | 0.0 |
| category | grayscale | sleep-line and sleep-accent | 0.0 |
| category | grayscale | sleep-line and heart-line | 0.0 |
| category | grayscale | sleep-line and heart-accent | 0.0 |
| category | grayscale | sleep-line and activity-line | 0.0 |
| category | grayscale | sleep-line and nutrition-line | 0.0 |
| category | grayscale | sleep-line and mind-line | 0.0 |
| category | grayscale | sleep-line and mind-accent | 0.0 |
| category | grayscale | sleep-line and labs-line | 0.0 |
| category | grayscale | sleep-line and labs-accent | 8.2 |
| category | grayscale | sleep-ink and heart-ink | 0.0 |
| category | grayscale | sleep-ink and activity-ink | 0.0 |
| category | grayscale | sleep-ink and nutrition-ink | 0.0 |
| category | grayscale | sleep-ink and mind-ink | 0.0 |
| category | grayscale | sleep-ink and labs-ink | 0.0 |
| category | grayscale | sleep-accent and heart-line | 0.0 |
| category | grayscale | sleep-accent and heart-accent | 0.0 |
| category | grayscale | sleep-accent and activity-line | 0.0 |
| category | grayscale | sleep-accent and nutrition-line | 0.0 |
| category | grayscale | sleep-accent and mind-line | 0.0 |
| category | grayscale | sleep-accent and mind-accent | 0.0 |
| category | grayscale | sleep-accent and labs-line | 0.0 |
| category | grayscale | sleep-accent and labs-accent | 0.0 |
| category | grayscale | heart-surface and activity-surface | 0.0 |
| category | grayscale | heart-surface and nutrition-surface | 0.0 |
| category | grayscale | heart-surface and mind-surface | 0.0 |
| category | grayscale | heart-surface and labs-surface | 0.0 |
| category | grayscale | heart-line and heart-accent | 8.6 |
| category | grayscale | heart-line and activity-line | 0.0 |
| category | grayscale | heart-line and activity-ink | 14.6 |
| category | grayscale | heart-line and nutrition-line | 0.0 |
| category | grayscale | heart-line and mind-line | 0.0 |
| category | grayscale | heart-line and mind-accent | 9.1 |
| category | grayscale | heart-line and labs-line | 0.0 |
| category | grayscale | heart-line and labs-ink | 14.9 |
| category | grayscale | heart-line and labs-accent | 10.0 |
| category | grayscale | heart-ink and activity-ink | 0.0 |
| category | grayscale | heart-ink and nutrition-ink | 0.0 |
| category | grayscale | heart-ink and mind-ink | 0.0 |
| category | grayscale | heart-ink and labs-ink | 0.0 |
| category | grayscale | heart-accent and activity-line | 0.0 |
| category | grayscale | heart-accent and nutrition-line | 7.9 |
| category | grayscale | heart-accent and mind-line | 9.2 |
| category | grayscale | heart-accent and mind-accent | 0.0 |
| category | grayscale | heart-accent and labs-line | 0.0 |
| category | grayscale | heart-accent and labs-accent | 0.0 |
| category | grayscale | activity-surface and nutrition-surface | 0.0 |
| category | grayscale | activity-surface and mind-surface | 0.0 |
| category | grayscale | activity-surface and labs-surface | 0.0 |
| category | grayscale | activity-line and nutrition-line | 0.0 |
| category | grayscale | activity-line and mind-line | 0.0 |
| category | grayscale | activity-line and mind-accent | 0.0 |
| category | grayscale | activity-line and labs-line | 0.0 |
| category | grayscale | activity-line and labs-accent | 0.0 |
| category | grayscale | activity-ink and nutrition-ink | 0.0 |
| category | grayscale | activity-ink and mind-ink | 0.0 |
| category | grayscale | activity-ink and labs-ink | 0.0 |
| category | grayscale | activity-accent and nutrition-accent | 0.0 |
| category | grayscale | nutrition-surface and mind-surface | 0.0 |
| category | grayscale | nutrition-surface and labs-surface | 0.0 |
| category | grayscale | nutrition-line and mind-line | 0.0 |
| category | grayscale | nutrition-line and mind-accent | 0.0 |
| category | grayscale | nutrition-line and labs-line | 0.0 |
| category | grayscale | nutrition-line and labs-accent | 8.2 |
| category | grayscale | nutrition-ink and mind-ink | 0.0 |
| category | grayscale | nutrition-ink and labs-ink | 0.0 |
| category | grayscale | mind-surface and labs-surface | 0.0 |
| category | grayscale | mind-line and mind-accent | 8.6 |
| category | grayscale | mind-line and labs-line | 0.0 |
| category | grayscale | mind-line and labs-accent | 9.6 |
| category | grayscale | mind-ink and labs-ink | 0.0 |
| category | grayscale | mind-accent and labs-line | 0.0 |
| category | grayscale | mind-accent and labs-accent | 0.0 |
| category | grayscale | labs-line and labs-accent | 0.0 |
{/* opsinjs:generated:end */}
---
# CSS variables
Source: https://opsinjs.pensievelabs.org/reference/generated/css-variables
Markdown: https://opsinjs.pensievelabs.org/reference/generated/css-variables.md
Section: Reference · kind: reference · also known as: css vars, opsin variables, generated css variables
## How this is generated [#how-this-is-generated]
**Source:** `tokens/*.json`, compiled to `app/tokens.generated.css`.
**Script:** `scripts/build-tokens.mts` emits the stylesheet;
`scripts/build-reference.mts` writes this page from the same data.
**Command:** `pnpm run generate`.
Every custom property the system defines is prefixed `--opsin-`. The prefix is
not decoration: it is how you can tell, in a devtools panel showing four
stylesheets, which variables you are allowed to override and which belong to the
documentation chrome or to Tailwind. If it does not start with `--opsin-`, it is
not ours and it is not covered by the
[versioning policy](../../project/versioning-policy.mdx).
**Five columns.** *Variable* is the property name. *Light* and *Dark* are the
resolved defaults in each theme, printed as the value actually computed rather
than as the expression that produced it. *Controls* is the one thing it decides.
*Read by* names the selectors that consume it. That is what tells you whether
overriding it in one place is safe.
**This page lists variables, not overrides.** Setting a variable is described in
[Theming → CSS variables](../../theming/css-variables.mdx), which is written for
consumers who are not using Tailwind. Adding one of your own is
[Adding your own tokens](../../theming/adding-your-own-tokens.mdx). The rule those pages
share: override at a scope you own, never on `:root` in a library.
**What is deliberately excluded.** shadcn's `--background` / `--foreground`
family, fumadocs' `--fd-*` variables and lyra's chrome variables all appear in
the same cascade on this site and none of them are opsinjs tokens. Also excluded
are internal variables used to pass a computed value between two rules inside one
component; those are unprefixed, undocumented, and may change in a patch.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## CSS variables [#css-variables]
### `:root, .opsin-product` [#root-opsin-product]
| Variable | Value | Controls |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-neutral-0` | `oklch(1 0 0)` | Neutral ramp, step 0. |
| `--opsin-neutral-50` | `oklch(0.985 0.002 250)` | Neutral ramp, step 50. |
| `--opsin-neutral-100` | `oklch(0.967 0.003 250)` | Neutral ramp, step 100. |
| `--opsin-neutral-200` | `oklch(0.929 0.004 250)` | Neutral ramp, step 200. |
| `--opsin-neutral-300` | `oklch(0.871 0.005 250)` | Neutral ramp, step 300. |
| `--opsin-neutral-400` | `oklch(0.708 0.008 250)` | Neutral ramp, step 400. |
| `--opsin-neutral-500` | `oklch(0.556 0.009 250)` | Neutral ramp, step 500. |
| `--opsin-neutral-600` | `oklch(0.439 0.009 250)` | Neutral ramp, step 600. |
| `--opsin-neutral-700` | `oklch(0.371 0.008 250)` | Neutral ramp, step 700. |
| `--opsin-neutral-800` | `oklch(0.269 0.007 250)` | Neutral ramp, step 800. |
| `--opsin-neutral-900` | `oklch(0.205 0.006 250)` | Neutral ramp, step 900. |
| `--opsin-neutral-950` | `oklch(0.145 0.005 250)` | Neutral ramp, step 950. |
| `--opsin-neutral-1000` | `oklch(0 0 0)` | Neutral ramp, step 1000. |
| `--opsin-chrome-background` | `oklch(0.985 0.002 250)` | Chrome role background, resolved to the ladder step it names. |
| `--opsin-chrome-foreground` | `oklch(0.205 0.006 250)` | Chrome role foreground, resolved to the ladder step it names. |
| `--opsin-chrome-card` | `oklch(1 0 0)` | Chrome role card, resolved to the ladder step it names. |
| `--opsin-chrome-card-foreground` | `oklch(0.205 0.006 250)` | Chrome role cardForeground, resolved to the ladder step it names. |
| `--opsin-chrome-muted` | `oklch(0.967 0.003 250)` | Chrome role muted, resolved to the ladder step it names. |
| `--opsin-chrome-muted-foreground` | `oklch(0.439 0.009 250)` | Chrome role mutedForeground, resolved to the ladder step it names. |
| `--opsin-chrome-border` | `oklch(0.556 0.009 250)` | Chrome role border, resolved to the ladder step it names. |
| `--opsin-chrome-input` | `oklch(0.556 0.009 250)` | Chrome role input, resolved to the ladder step it names. |
| `--opsin-chrome-ring` | `oklch(0.55 0.13 250)` | Chrome role ring, resolved to the ladder step it names. |
| `--opsin-chrome-primary` | `oklch(0.51 0.135 250)` | Chrome role primary, resolved to the ladder step it names. |
| `--opsin-chrome-primary-foreground` | `oklch(0.99 0 0)` | Chrome role primaryForeground, resolved to the ladder step it names. |
| `--opsin-category-sleep-50` | `oklch(0.972 0.013 275)` | Sleep ramp, step 50. |
| `--opsin-category-sleep-100` | `oklch(0.941 0.028 275)` | Sleep ramp, step 100. |
| `--opsin-category-sleep-200` | `oklch(0.884 0.057 275)` | Sleep ramp, step 200. |
| `--opsin-category-sleep-300` | `oklch(0.806 0.092 275)` | Sleep ramp, step 300. |
| `--opsin-category-sleep-400` | `oklch(0.714 0.123 275)` | Sleep ramp, step 400. |
| `--opsin-category-sleep-500` | `oklch(0.622 0.14 275)` | Sleep ramp, step 500. |
| `--opsin-category-sleep-600` | `oklch(0.541 0.136 275)` | Sleep ramp, step 600. |
| `--opsin-category-sleep-700` | `oklch(0.452 0.123 275)` | Sleep ramp, step 700. |
| `--opsin-category-sleep-800` | `oklch(0.362 0.104 275)` | Sleep ramp, step 800. |
| `--opsin-category-sleep-900` | `oklch(0.276 0.081 275)` | Sleep ramp, step 900. |
| `--opsin-category-sleep-950` | `oklch(0.208 0.062 275)` | Sleep ramp, step 950. |
| `--opsin-category-sleep-surface` | `oklch(0.972 0.013 275)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-sleep-line` | `oklch(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`. |
| `--opsin-category-sleep-ink` | `oklch(0.362 0.104 275)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-sleep-accent` | `oklch(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. |
| `--opsin-category-heart-50` | `oklch(0.972 0.014 15)` | Heart ramp, step 50. |
| `--opsin-category-heart-100` | `oklch(0.941 0.03 15)` | Heart ramp, step 100. |
| `--opsin-category-heart-200` | `oklch(0.884 0.062 15)` | Heart ramp, step 200. |
| `--opsin-category-heart-300` | `oklch(0.806 0.112 15)` | Heart ramp, step 300. |
| `--opsin-category-heart-400` | `oklch(0.714 0.15 15)` | Heart ramp, step 400. |
| `--opsin-category-heart-500` | `oklch(0.622 0.17 15)` | Heart ramp, step 500. |
| `--opsin-category-heart-600` | `oklch(0.541 0.165 15)` | Heart ramp, step 600. |
| `--opsin-category-heart-700` | `oklch(0.452 0.15 15)` | Heart ramp, step 700. |
| `--opsin-category-heart-800` | `oklch(0.362 0.126 15)` | Heart ramp, step 800. |
| `--opsin-category-heart-900` | `oklch(0.276 0.099 15)` | Heart ramp, step 900. |
| `--opsin-category-heart-950` | `oklch(0.208 0.075 15)` | Heart ramp, step 950. |
| `--opsin-category-heart-surface` | `oklch(0.972 0.014 15)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-heart-line` | `oklch(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`. |
| `--opsin-category-heart-ink` | `oklch(0.362 0.126 15)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-heart-accent` | `oklch(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. |
| `--opsin-category-activity-50` | `oklch(0.972 0.022 140)` | Activity ramp, step 50. |
| `--opsin-category-activity-100` | `oklch(0.941 0.038 140)` | Activity ramp, step 100. |
| `--opsin-category-activity-200` | `oklch(0.884 0.067 140)` | Activity ramp, step 200. |
| `--opsin-category-activity-300` | `oklch(0.806 0.106 140)` | Activity ramp, step 300. |
| `--opsin-category-activity-400` | `oklch(0.714 0.141 140)` | Activity ramp, step 400. |
| `--opsin-category-activity-500` | `oklch(0.622 0.16 140)` | Activity ramp, step 500. |
| `--opsin-category-activity-600` | `oklch(0.541 0.155 140)` | Activity ramp, step 600. |
| `--opsin-category-activity-700` | `oklch(0.452 0.141 140)` | Activity ramp, step 700. |
| `--opsin-category-activity-800` | `oklch(0.362 0.118 140)` | Activity ramp, step 800. |
| `--opsin-category-activity-900` | `oklch(0.276 0.092 140)` | Activity ramp, step 900. |
| `--opsin-category-activity-950` | `oklch(0.208 0.07 140)` | Activity ramp, step 950. |
| `--opsin-category-activity-surface` | `oklch(0.972 0.022 140)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-activity-line` | `oklch(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`. |
| `--opsin-category-activity-ink` | `oklch(0.362 0.118 140)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-activity-accent` | `oklch(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. |
| `--opsin-category-nutrition-50` | `oklch(0.972 0.02 70)` | Nutrition ramp, step 50. |
| `--opsin-category-nutrition-100` | `oklch(0.941 0.034 70)` | Nutrition ramp, step 100. |
| `--opsin-category-nutrition-200` | `oklch(0.884 0.059 70)` | Nutrition ramp, step 200. |
| `--opsin-category-nutrition-300` | `oklch(0.806 0.092 70)` | Nutrition ramp, step 300. |
| `--opsin-category-nutrition-400` | `oklch(0.714 0.123 70)` | Nutrition ramp, step 400. |
| `--opsin-category-nutrition-500` | `oklch(0.622 0.135 70)` | Nutrition ramp, step 500. |
| `--opsin-category-nutrition-600` | `oklch(0.541 0.118 70)` | Nutrition ramp, step 600. |
| `--opsin-category-nutrition-700` | `oklch(0.452 0.099 70)` | Nutrition ramp, step 700. |
| `--opsin-category-nutrition-800` | `oklch(0.362 0.08 70)` | Nutrition ramp, step 800. |
| `--opsin-category-nutrition-900` | `oklch(0.276 0.062 70)` | Nutrition ramp, step 900. |
| `--opsin-category-nutrition-950` | `oklch(0.208 0.049 70)` | Nutrition ramp, step 950. |
| `--opsin-category-nutrition-surface` | `oklch(0.972 0.02 70)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-nutrition-line` | `oklch(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`. |
| `--opsin-category-nutrition-ink` | `oklch(0.362 0.08 70)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-nutrition-accent` | `oklch(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. |
| `--opsin-category-mind-50` | `oklch(0.972 0.018 310)` | Mind ramp, step 50. |
| `--opsin-category-mind-100` | `oklch(0.941 0.034 310)` | Mind ramp, step 100. |
| `--opsin-category-mind-200` | `oklch(0.884 0.059 310)` | Mind ramp, step 200. |
| `--opsin-category-mind-300` | `oklch(0.806 0.092 310)` | Mind ramp, step 300. |
| `--opsin-category-mind-400` | `oklch(0.714 0.123 310)` | Mind ramp, step 400. |
| `--opsin-category-mind-500` | `oklch(0.622 0.14 310)` | Mind ramp, step 500. |
| `--opsin-category-mind-600` | `oklch(0.541 0.136 310)` | Mind ramp, step 600. |
| `--opsin-category-mind-700` | `oklch(0.452 0.123 310)` | Mind ramp, step 700. |
| `--opsin-category-mind-800` | `oklch(0.362 0.104 310)` | Mind ramp, step 800. |
| `--opsin-category-mind-900` | `oklch(0.276 0.081 310)` | Mind ramp, step 900. |
| `--opsin-category-mind-950` | `oklch(0.208 0.062 310)` | Mind ramp, step 950. |
| `--opsin-category-mind-surface` | `oklch(0.972 0.018 310)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-mind-line` | `oklch(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`. |
| `--opsin-category-mind-ink` | `oklch(0.362 0.104 310)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-mind-accent` | `oklch(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. |
| `--opsin-category-labs-50` | `oklch(0.972 0.015 210)` | Labs ramp, step 50. |
| `--opsin-category-labs-100` | `oklch(0.941 0.026 210)` | Labs ramp, step 100. |
| `--opsin-category-labs-200` | `oklch(0.884 0.046 210)` | Labs ramp, step 200. |
| `--opsin-category-labs-300` | `oklch(0.806 0.073 210)` | Labs ramp, step 300. |
| `--opsin-category-labs-400` | `oklch(0.714 0.097 210)` | Labs ramp, step 400. |
| `--opsin-category-labs-500` | `oklch(0.622 0.108 210)` | Labs ramp, step 500. |
| `--opsin-category-labs-600` | `oklch(0.541 0.094 210)` | Labs ramp, step 600. |
| `--opsin-category-labs-700` | `oklch(0.452 0.079 210)` | Labs ramp, step 700. |
| `--opsin-category-labs-800` | `oklch(0.362 0.063 210)` | Labs ramp, step 800. |
| `--opsin-category-labs-900` | `oklch(0.276 0.049 210)` | Labs ramp, step 900. |
| `--opsin-category-labs-950` | `oklch(0.208 0.038 210)` | Labs ramp, step 950. |
| `--opsin-category-labs-surface` | `oklch(0.972 0.015 210)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-labs-line` | `oklch(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`. |
| `--opsin-category-labs-ink` | `oklch(0.362 0.063 210)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-labs-accent` | `oklch(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. |
| `--opsin-status-steady-50` | `oklch(0.972 0.015 178)` | Steady ramp, step 50. |
| `--opsin-status-steady-100` | `oklch(0.941 0.026 178)` | Steady ramp, step 100. |
| `--opsin-status-steady-200` | `oklch(0.884 0.046 178)` | Steady ramp, step 200. |
| `--opsin-status-steady-300` | `oklch(0.806 0.073 178)` | Steady ramp, step 300. |
| `--opsin-status-steady-400` | `oklch(0.714 0.097 178)` | Steady ramp, step 400. |
| `--opsin-status-steady-500` | `oklch(0.622 0.11 178)` | Steady ramp, step 500. |
| `--opsin-status-steady-600` | `oklch(0.541 0.1 178)` | Steady ramp, step 600. |
| `--opsin-status-steady-700` | `oklch(0.452 0.084 178)` | Steady ramp, step 700. |
| `--opsin-status-steady-800` | `oklch(0.362 0.067 178)` | Steady ramp, step 800. |
| `--opsin-status-steady-900` | `oklch(0.276 0.052 178)` | Steady ramp, step 900. |
| `--opsin-status-steady-950` | `oklch(0.208 0.041 178)` | Steady ramp, step 950. |
| `--opsin-status-steady-surface` | `oklch(0.972 0.015 178)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-steady-line` | `oklch(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`. |
| `--opsin-status-steady-ink` | `oklch(0.362 0.067 178)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-steady-accent` | `oklch(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. |
| `--opsin-status-watch-50` | `oklch(0.972 0.02 82)` | Watch ramp, step 50. |
| `--opsin-status-watch-100` | `oklch(0.941 0.034 82)` | Watch ramp, step 100. |
| `--opsin-status-watch-200` | `oklch(0.884 0.059 82)` | Watch ramp, step 200. |
| `--opsin-status-watch-300` | `oklch(0.806 0.092 82)` | Watch ramp, step 300. |
| `--opsin-status-watch-400` | `oklch(0.714 0.123 82)` | Watch ramp, step 400. |
| `--opsin-status-watch-500` | `oklch(0.622 0.129 82)` | Watch ramp, step 500. |
| `--opsin-status-watch-600` | `oklch(0.541 0.112 82)` | Watch ramp, step 600. |
| `--opsin-status-watch-700` | `oklch(0.452 0.094 82)` | Watch ramp, step 700. |
| `--opsin-status-watch-800` | `oklch(0.362 0.076 82)` | Watch ramp, step 800. |
| `--opsin-status-watch-900` | `oklch(0.276 0.059 82)` | Watch ramp, step 900. |
| `--opsin-status-watch-950` | `oklch(0.208 0.046 82)` | Watch ramp, step 950. |
| `--opsin-status-watch-surface` | `oklch(0.972 0.02 82)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-watch-line` | `oklch(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`. |
| `--opsin-status-watch-ink` | `oklch(0.362 0.076 82)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-watch-accent` | `oklch(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. |
| `--opsin-status-attention-50` | `oklch(0.972 0.015 45)` | Needs attention ramp, step 50. |
| `--opsin-status-attention-100` | `oklch(0.941 0.032 45)` | Needs attention ramp, step 100. |
| `--opsin-status-attention-200` | `oklch(0.884 0.063 45)` | Needs attention ramp, step 200. |
| `--opsin-status-attention-300` | `oklch(0.806 0.099 45)` | Needs attention ramp, step 300. |
| `--opsin-status-attention-400` | `oklch(0.714 0.132 45)` | Needs attention ramp, step 400. |
| `--opsin-status-attention-500` | `oklch(0.622 0.15 45)` | Needs attention ramp, step 500. |
| `--opsin-status-attention-600` | `oklch(0.541 0.145 45)` | Needs attention ramp, step 600. |
| `--opsin-status-attention-700` | `oklch(0.452 0.13 45)` | Needs attention ramp, step 700. |
| `--opsin-status-attention-800` | `oklch(0.362 0.105 45)` | Needs attention ramp, step 800. |
| `--opsin-status-attention-900` | `oklch(0.276 0.082 45)` | Needs attention ramp, step 900. |
| `--opsin-status-attention-950` | `oklch(0.208 0.065 45)` | Needs attention ramp, step 950. |
| `--opsin-status-attention-surface` | `oklch(0.972 0.015 45)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-attention-line` | `oklch(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`. |
| `--opsin-status-attention-ink` | `oklch(0.362 0.105 45)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-attention-accent` | `oklch(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. |
| `--opsin-status-urgent-50` | `oklch(0.972 0.014 27)` | Urgent ramp, step 50. |
| `--opsin-status-urgent-100` | `oklch(0.941 0.03 27)` | Urgent ramp, step 100. |
| `--opsin-status-urgent-200` | `oklch(0.884 0.061 27)` | Urgent ramp, step 200. |
| `--opsin-status-urgent-300` | `oklch(0.806 0.11 27)` | Urgent ramp, step 300. |
| `--opsin-status-urgent-400` | `oklch(0.714 0.176 27)` | Urgent ramp, step 400. |
| `--opsin-status-urgent-500` | `oklch(0.622 0.2 27)` | Urgent ramp, step 500. |
| `--opsin-status-urgent-600` | `oklch(0.541 0.194 27)` | Urgent ramp, step 600. |
| `--opsin-status-urgent-700` | `oklch(0.452 0.176 27)` | Urgent ramp, step 700. |
| `--opsin-status-urgent-800` | `oklch(0.362 0.148 27)` | Urgent ramp, step 800. |
| `--opsin-status-urgent-900` | `oklch(0.276 0.115 27)` | Urgent ramp, step 900. |
| `--opsin-status-urgent-950` | `oklch(0.208 0.088 27)` | Urgent ramp, step 950. |
| `--opsin-status-urgent-surface` | `oklch(0.972 0.014 27)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-urgent-line` | `oklch(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`. |
| `--opsin-status-urgent-ink` | `oklch(0.362 0.148 27)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-urgent-accent` | `oklch(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. |
| `--opsin-status-unknown-50` | `oklch(0.972 0.003 250)` | Unknown ramp, step 50. |
| `--opsin-status-unknown-100` | `oklch(0.941 0.005 250)` | Unknown ramp, step 100. |
| `--opsin-status-unknown-200` | `oklch(0.884 0.008 250)` | Unknown ramp, step 200. |
| `--opsin-status-unknown-300` | `oklch(0.806 0.013 250)` | Unknown ramp, step 300. |
| `--opsin-status-unknown-400` | `oklch(0.714 0.018 250)` | Unknown ramp, step 400. |
| `--opsin-status-unknown-500` | `oklch(0.622 0.02 250)` | Unknown ramp, step 500. |
| `--opsin-status-unknown-600` | `oklch(0.541 0.019 250)` | Unknown ramp, step 600. |
| `--opsin-status-unknown-700` | `oklch(0.452 0.018 250)` | Unknown ramp, step 700. |
| `--opsin-status-unknown-800` | `oklch(0.362 0.015 250)` | Unknown ramp, step 800. |
| `--opsin-status-unknown-900` | `oklch(0.276 0.012 250)` | Unknown ramp, step 900. |
| `--opsin-status-unknown-950` | `oklch(0.208 0.009 250)` | Unknown ramp, step 950. |
| `--opsin-status-unknown-surface` | `oklch(0.972 0.003 250)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-unknown-line` | `oklch(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`. |
| `--opsin-status-unknown-ink` | `oklch(0.362 0.015 250)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-unknown-accent` | `oklch(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. |
| `--opsin-material-canvas-tint` | `var(--opsin-neutral-0)` | Rung 0 (canvas): the tint over what is behind it. The application background. Nothing is behind it, so nothing shows through it. |
| `--opsin-material-canvas-tint-alpha` | `1` | Rung 0 (canvas): how opaque that tint is. |
| `--opsin-material-canvas-blur` | `0px` | Rung 0 (canvas): backdrop blur radius. |
| `--opsin-material-canvas-saturation` | `1` | Rung 0 (canvas): backdrop saturation multiplier. |
| `--opsin-material-canvas-border` | `none` | Rung 0 (canvas): the boundary. |
| `--opsin-material-canvas-shadow` | `none` | Rung 0 (canvas): the shadow that separates it from what is behind. |
| `--opsin-material-canvas-scrim` | `0` | Rung 0 (canvas): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-canvas-opaque` | `var(--opsin-neutral-0)` | Rung 0 (canvas): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-inset-tint` | `var(--opsin-neutral-100)` | Rung 0.5 (inset): the tint over what is behind it. The recessed well inside a card: the explanatory note a Callout draws inside the reading it annotates, and the track a RangeBar lays its range along. Its fill sits below the card fill in both themes, so the well reads as carved into the card rather than lifted off it. In light it is neutral-100, one step under the white card, which is where a Callout already sat. In dark it is a value between the page and the card, below the card so the well is a recess and above the page so a Callout used on its own is still visible. |
| `--opsin-material-inset-tint-alpha` | `1` | Rung 0.5 (inset): how opaque that tint is. |
| `--opsin-material-inset-blur` | `0px` | Rung 0.5 (inset): backdrop blur radius. |
| `--opsin-material-inset-saturation` | `1` | Rung 0.5 (inset): backdrop saturation multiplier. |
| `--opsin-material-inset-border` | `none` | Rung 0.5 (inset): the boundary. |
| `--opsin-material-inset-shadow` | `none` | Rung 0.5 (inset): the shadow that separates it from what is behind. |
| `--opsin-material-inset-scrim` | `0` | Rung 0.5 (inset): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-inset-opaque` | `var(--opsin-neutral-100)` | Rung 0.5 (inset): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-card-tint` | `var(--opsin-neutral-0)` | Rung 1 (card): the tint over what is behind it. The default home for a health value. Opaque, bounded by a line rather than a shadow, and the rung every ResultCard, MetricTile and RangeBar sits on. |
| `--opsin-material-card-tint-alpha` | `1` | Rung 1 (card): how opaque that tint is. |
| `--opsin-material-card-blur` | `0px` | Rung 1 (card): backdrop blur radius. |
| `--opsin-material-card-saturation` | `1` | Rung 1 (card): backdrop saturation multiplier. |
| `--opsin-material-card-border` | `var(--opsin-neutral-200)` | Rung 1 (card): the boundary. |
| `--opsin-material-card-shadow` | `none` | Rung 1 (card): the shadow that separates it from what is behind. |
| `--opsin-material-card-scrim` | `0` | Rung 1 (card): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-card-opaque` | `var(--opsin-neutral-0)` | Rung 1 (card): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-raised-tint` | `var(--opsin-neutral-0)` | Rung 2 (raised): the tint over what is behind it. Menus, popovers, tooltips, a dragged card. Still opaque: the shadow, not translucency, is what says 'above'. |
| `--opsin-material-raised-tint-alpha` | `1` | Rung 2 (raised): how opaque that tint is. |
| `--opsin-material-raised-blur` | `0px` | Rung 2 (raised): backdrop blur radius. |
| `--opsin-material-raised-saturation` | `1` | Rung 2 (raised): backdrop saturation multiplier. |
| `--opsin-material-raised-border` | `var(--opsin-neutral-200)` | Rung 2 (raised): the boundary. |
| `--opsin-material-raised-shadow` | `0 1px 2px oklch(0 0 0 / 0.06), 0 4px 12px oklch(0 0 0 / 0.06)` | Rung 2 (raised): the shadow that separates it from what is behind. |
| `--opsin-material-raised-scrim` | `0` | Rung 2 (raised): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-raised-opaque` | `var(--opsin-neutral-0)` | Rung 2 (raised): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-sheet-tint` | `var(--opsin-neutral-0)` | Rung 3 (sheet): the tint over what is behind it. A bottom sheet or side panel the reader can dismiss by dragging. Its tint is now near opaque, so the panel lands close to the card colour and a white control inside it no longer floats above a greyer surface. What keeps the covered page recognisable is the scrim around the panel rather than the page reading through the panel, because at this opacity very little of the page shows through the tint. |
| `--opsin-material-sheet-tint-alpha` | `0.93` | Rung 3 (sheet): how opaque that tint is. |
| `--opsin-material-sheet-blur` | `20px` | Rung 3 (sheet): backdrop blur radius. |
| `--opsin-material-sheet-saturation` | `1.6` | Rung 3 (sheet): backdrop saturation multiplier. |
| `--opsin-material-sheet-border` | `oklch(0 0 0 / 0.08)` | Rung 3 (sheet): the boundary. |
| `--opsin-material-sheet-shadow` | `0 -1px 2px oklch(0 0 0 / 0.05)` | Rung 3 (sheet): the shadow that separates it from what is behind. |
| `--opsin-material-sheet-scrim` | `0.93` | Rung 3 (sheet): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-sheet-opaque` | `var(--opsin-neutral-0)` | Rung 3 (sheet): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-overlay-tint` | `var(--opsin-neutral-0)` | Rung 4 (overlay): the tint over what is behind it. A pinned toolbar, a tab bar and a floating action bar are all chrome that content scrolls beneath. |
| `--opsin-material-overlay-tint-alpha` | `0.74` | Rung 4 (overlay): how opaque that tint is. |
| `--opsin-material-overlay-blur` | `28px` | Rung 4 (overlay): backdrop blur radius. |
| `--opsin-material-overlay-saturation` | `1.8` | Rung 4 (overlay): backdrop saturation multiplier. |
| `--opsin-material-overlay-border` | `oklch(0 0 0 / 0.10)` | Rung 4 (overlay): the boundary. |
| `--opsin-material-overlay-shadow` | `0 8px 32px oklch(0 0 0 / 0.12)` | Rung 4 (overlay): the shadow that separates it from what is behind. |
| `--opsin-material-overlay-scrim` | `0.74` | Rung 4 (overlay): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-overlay-opaque` | `var(--opsin-neutral-0)` | Rung 4 (overlay): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-scrim-tint` | `var(--opsin-neutral-950)` | Rung 5 (scrim): the tint over what is behind it. The dimming layer behind a modal dialog or a consent sheet. Its job is to remove the page from consideration, not to look like glass. |
| `--opsin-material-scrim-tint-alpha` | `0.44` | Rung 5 (scrim): how opaque that tint is. |
| `--opsin-material-scrim-blur` | `2px` | Rung 5 (scrim): backdrop blur radius. |
| `--opsin-material-scrim-saturation` | `1` | Rung 5 (scrim): backdrop saturation multiplier. |
| `--opsin-material-scrim-border` | `none` | Rung 5 (scrim): the boundary. |
| `--opsin-material-scrim-shadow` | `none` | Rung 5 (scrim): the shadow that separates it from what is behind. |
| `--opsin-material-scrim-scrim` | `0.44` | Rung 5 (scrim): the minimum scrim opacity needed for text on this rung to clear the contrast floor. |
| `--opsin-material-scrim-opaque` | `oklch(0.205 0.006 250 / 0.72)` | Rung 5 (scrim): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-ease-spring-snap` | `linear(0, 0.0715, 0.2271, 0.4053, 0.5722, 0.7119, 0.8198, 0.8978, 0.9505, 0.9836, 1.0025, 1.0118, 1.015, 1.0147, 1.0126, 1.01, 1.0073, 1.0051, 1.0033, 1.0019, 1)` | Direct manipulation only: a switch the reader just flipped, a segmented control, a pressed button settling. It overshoots by 1.5%. That is enough to feel physical but not enough to look playful. The 1.5% is the largest overshoot in the system but not the only one: `spring-settle` overshoots by 0.88%. Only `spring-calm` and `spring-sheet` reach their target without passing it. |
| `--opsin-duration-spring-snap` | `283ms` | Settle time for the spring-snap spring, measured from its own parameters. |
| `--opsin-ease-spring-settle` | `linear(0, 0.0742, 0.2328, 0.4113, 0.5758, 0.7116, 0.8157, 0.8905, 0.9412, 0.9736, 0.9928, 1.003, 1.0076, 1.0088, 1.0082, 1.0068, 1.0053, 1.0038, 1.0026, 1.0017, 1)` | The workhorse for chrome: popovers, tooltips, menus, chips appearing and disappearing. |
| `--opsin-duration-spring-settle` | `382ms` | Settle time for the spring-settle spring, measured from its own parameters. |
| `--opsin-ease-spring-calm` | `linear(0, 0.0829, 0.2457, 0.4157, 0.5642, 0.6832, 0.774, 0.841, 0.8893, 0.9236, 0.9476, 0.9643, 0.9757, 0.9836, 0.9889, 0.9926, 0.995, 0.9967, 0.9978, 0.9985, 1)` | A health value that changes while it is already on screen: a bar re-filling from one reading to the next, a dial travelling between two values the reader has already been shown. Never a first paint and never a first reveal. A value arrives at its final figure, with no count-up, no dial sweep and no line drawing itself in (health/motion-in-health-ui rule 2). Slightly overdamped (zeta just over 1) so it never overshoots and never bounces. |
| `--opsin-duration-spring-calm` | `550ms` | Settle time for the spring-calm spring, measured from its own parameters. |
| `--opsin-ease-spring-sheet` | `linear(0, 0.0881, 0.2576, 0.431, 0.5798, 0.6971, 0.7854, 0.8497, 0.8958, 0.9282, 0.9508, 0.9664, 0.9771, 0.9845, 0.9895, 0.9929, 0.9952, 0.9967, 0.9978, 0.9985, 1)` | Large surfaces travelling a long distance: sheets, dialogs, full-screen pushes. Overdamped, because a sheet that bounces at the top of its travel reads as a dropped object. |
| `--opsin-duration-spring-sheet` | `483ms` | Settle time for the spring-sheet spring, measured from its own parameters. |
| `--opsin-ease-standard` | `cubic-bezier(0.2, 0, 0, 1)` | Non-spring transitions where a spring would be overkill: colour, opacity, border. Fast out, slow in. |
| `--opsin-ease-enter` | `cubic-bezier(0.05, 0.7, 0.1, 1)` | Something arriving from off-screen or from nothing. Decelerating, because an arrival should feel like it is coming to rest. |
| `--opsin-ease-exit` | `cubic-bezier(0.3, 0, 0.8, 0.15)` | Something leaving. Accelerating and shorter than its enter, because a reader does not need to watch a dismissal finish. |
| `--opsin-duration-instant` | `80ms` | State change with no travel: hover tint, focus ring, checkbox tick. |
| `--opsin-duration-fast` | `140ms` | Small elements moving a small distance. |
| `--opsin-duration-base` | `220ms` | The default for chrome that is not spring-driven. |
| `--opsin-duration-slow` | `360ms` | Layout change: a list reflowing, a card expanding. |
| `--opsin-duration-deliberate` | `560ms` | The ceiling on a transition a reader is waiting on, such as a first-run reveal or a consent sheet, where the point is that the reader notices. No such transition may exceed it. A looping placeholder period, like the skeleton shimmer, is bounded instead by its iteration count, so a single sweep of it may run longer. |
| `--opsin-duration-shimmer` | `1600ms` | One sweep of a loading placeholder's sheen. This is a period, the time for the sheen to make one pass across the bar, and not a travel time. At 1600ms the movement reads as calm rather than as urgency, which the first motion rule requires of anything that is not a value the reader controls. Its consumer pairs it with a finite iteration count so the total motion stays under the five seconds at which WCAG 2.2 SC 2.2.2 engages. |
| `--opsin-font-sans` | `system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif` | The sans family stack. |
| `--opsin-font-mono` | `ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace` | The mono family stack. |
| `--opsin-font-numeric` | `system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | The numeric family stack. |
| `--opsin-text-large-title-size` | `2.125rem` | The largest step in the scale, reserved for a surface whose entire subject is one number or one word. No component in the system sets a value at this step yet. |
| `--opsin-text-large-title-leading` | `1.206` | Line height for largeTitle, unitless so it scales with the size. |
| `--opsin-text-large-title-tracking` | `-0.011em` | Letter spacing for largeTitle. |
| `--opsin-text-large-title-weight` | `700` | Font weight for largeTitle. |
| `--opsin-text-title1-size` | `1.75rem` | A screen title, and the hero treatment a value's number takes at size display. |
| `--opsin-text-title1-leading` | `1.214` | Line height for title1, unitless so it scales with the size. |
| `--opsin-text-title1-tracking` | `-0.009em` | Letter spacing for title1. |
| `--opsin-text-title1-weight` | `700` | Font weight for title1. |
| `--opsin-text-title2-size` | `1.375rem` | Section title; the floor for a primary health value inside a card. |
| `--opsin-text-title2-leading` | `1.273` | Line height for title2, unitless so it scales with the size. |
| `--opsin-text-title2-tracking` | `-0.006em` | Letter spacing for title2. |
| `--opsin-text-title2-weight` | `600` | Font weight for title2. |
| `--opsin-text-title3-size` | `1.25rem` | A Dialog or Sheet title, and the unit beside a value's number at size display. |
| `--opsin-text-title3-leading` | `1.25` | Line height for title3, unitless so it scales with the size. |
| `--opsin-text-title3-tracking` | `-0.004em` | Letter spacing for title3. |
| `--opsin-text-title3-weight` | `600` | Font weight for title3. |
| `--opsin-text-headline-size` | `1.0625rem` | An emphasised line of body text, such as a status sentence or a question in a form. |
| `--opsin-text-headline-leading` | `1.294` | Line height for headline, unitless so it scales with the size. |
| `--opsin-text-headline-tracking` | `-0.003em` | Letter spacing for headline. |
| `--opsin-text-headline-weight` | `600` | Font weight for headline. |
| `--opsin-text-body-size` | `1.0625rem` | Everything a reader reads as prose. The anchor of the scale. |
| `--opsin-text-body-leading` | `1.294` | Line height for body, unitless so it scales with the size. |
| `--opsin-text-body-tracking` | `-0.003em` | Letter spacing for body. |
| `--opsin-text-body-weight` | `400` | Font weight for body. |
| `--opsin-text-callout-size` | `1rem` | Secondary prose inside a card or a callout. |
| `--opsin-text-callout-leading` | `1.313` | Line height for callout, unitless so it scales with the size. |
| `--opsin-text-callout-tracking` | `-0.002em` | Letter spacing for callout. |
| `--opsin-text-callout-weight` | `400` | Font weight for callout. |
| `--opsin-text-subheadline-size` | `0.9375rem` | A supporting line under a title. |
| `--opsin-text-subheadline-leading` | `1.333` | Line height for subheadline, unitless so it scales with the size. |
| `--opsin-text-subheadline-tracking` | `-0.001em` | Letter spacing for subheadline. |
| `--opsin-text-subheadline-weight` | `400` | Font weight for subheadline. |
| `--opsin-text-subheadline-emphasis-size` | `0.9375rem` | The emphasised twin of subheadline, for a compact control's label, where the control must read as narrower and never as quieter. |
| `--opsin-text-subheadline-emphasis-leading` | `1.333` | Line height for subheadlineEmphasis, unitless so it scales with the size. |
| `--opsin-text-subheadline-emphasis-tracking` | `-0.001em` | Letter spacing for subheadlineEmphasis. |
| `--opsin-text-subheadline-emphasis-weight` | `600` | Font weight for subheadlineEmphasis. |
| `--opsin-text-footnote-size` | `0.8125rem` | Provenance: who measured this, when, with what. |
| `--opsin-text-footnote-leading` | `1.385` | Line height for footnote, unitless so it scales with the size. |
| `--opsin-text-footnote-tracking` | `0em` | Letter spacing for footnote. |
| `--opsin-text-footnote-weight` | `400` | Font weight for footnote. |
| `--opsin-text-caption1-size` | `0.75rem` | Axis labels, legend text, and legal text. |
| `--opsin-text-caption1-leading` | `1.333` | Line height for caption1, unitless so it scales with the size. |
| `--opsin-text-caption1-tracking` | `0.002em` | Letter spacing for caption1. |
| `--opsin-text-caption1-weight` | `400` | Font weight for caption1. |
| `--opsin-text-caption2-size` | `0.6875rem` | The smallest text the system permits, and only for text that repeats a label already present elsewhere. Never the only place a fact appears. |
| `--opsin-text-caption2-leading` | `1.182` | Line height for caption2, unitless so it scales with the size. |
| `--opsin-text-caption2-tracking` | `0.005em` | Letter spacing for caption2. |
| `--opsin-text-caption2-weight` | `500` | Font weight for caption2. |
| `--opsin-numerals` | `tabular-nums` | Every component that renders a number sets this, so a changing value does not shift its own layout. |
| `--opsin-space-0` | `0rem` | 0px. |
| `--opsin-space-1` | `0.25rem` | Gap between an icon and its label. |
| `--opsin-space-2` | `0.5rem` | Gap between tightly related lines. For the separation between two interactive targets see targets.separation, which is the same number and carries its scope. |
| `--opsin-space-3` | `0.75rem` | Inner padding of a compact control. |
| `--opsin-space-4` | `1rem` | The default gap between elements inside a card. |
| `--opsin-space-5` | `1.25rem` | Card inner padding on a phone. |
| `--opsin-space-6` | `1.5rem` | Card inner padding on a wide screen; gap between cards. |
| `--opsin-space-8` | `2rem` | Gap between sections within a screen. |
| `--opsin-space-10` | `2.5rem` | Space above a section heading. |
| `--opsin-space-12` | `3rem` | Gap between major regions of a screen. |
| `--opsin-space-16` | `4rem` | Top of a screen below the safe area; the space a consent sheet leaves above its first question. |
| `--opsin-space-20` | `5rem` | Empty-state vertical rhythm. |
| `--opsin-space-24` | `6rem` | The largest step. Beyond this, use a layout, not a gap. |
| `--opsin-space-px` | `0.0625rem` | Hairline borders only. |
| `--opsin-space-0-5` | `0.125rem` | Optical nudges. Not a layout step. |
| `--opsin-target-minimum` | `2.75rem` | The opsinjs floor for any interactive control, applied to the hit area rather than to the visible box. |
| `--opsin-target-comfortable` | `3rem` | The default for a primary action in the product theme. |
| `--opsin-target-generous` | `3.5rem` | A single primary action on a consent, escalation or emergency surface, where a mis-tap has a real cost. |
| `--opsin-target-separation` | `0.5rem` | The minimum gap between two adjacent targets whose visible boxes are smaller than 44px. It is a floor for undersized targets only. Two targets that both meet the 44px minimum are outside its scope, and separating those is governed by the differing cost of a mis-tap between them rather than by a fixed gap. |
| `--opsin-gutter-phone` | `16px` | The screen gutter in the phone responsive mode. |
| `--opsin-gutter-tablet` | `24px` | The screen gutter in the tablet responsive mode. |
| `--opsin-gutter-wide` | `32px` | The screen gutter in the wide responsive mode. |
| `--opsin-measure-tight` | `45ch` | A caption or a legend. |
| `--opsin-measure-comfortable` | `66ch` | The maximum line length for prose anywhere in the product, including a disclaimer nobody wants to read. |
| `--opsin-measure-wide` | `80ch` | Code and machine output only. |
| `--opsin-graphic-dial` | `17rem` | The maximum width of the ScoreDial arc. This is 16em against the product body of 1.0625rem, which is 17rem and lands on the 4px grid at 68 steps, and it yields to w-full when the column is narrower. |
| `--opsin-safe-top` | `env(safe-area-inset-top, 0px)` | The top safe-area inset, for installed web apps where browser chrome does not protect the edge. |
| `--opsin-safe-right` | `env(safe-area-inset-right, 0px)` | The right safe-area inset, for installed web apps where browser chrome does not protect the edge. |
| `--opsin-safe-bottom` | `env(safe-area-inset-bottom, 0px)` | The bottom safe-area inset, for installed web apps where browser chrome does not protect the edge. |
| `--opsin-safe-left` | `env(safe-area-inset-left, 0px)` | The left safe-area inset, for installed web apps where browser chrome does not protect the edge. |
| `--opsin-radius-base` | `0.875rem` | The one radius the whole ladder is derived from. |
| `--opsin-radius-none` | `0rem` | Full-bleed media and anything that meets a screen edge. |
| `--opsin-radius-xs` | `0.25rem` | The floor for a nested corner. Tags inside a chip, a swatch inside a legend. |
| `--opsin-radius-sm` | `0.4375rem` | Inputs and small controls inside a card. |
| `--opsin-radius-md` | `0.875rem` | The default. Buttons, chips, cards. |
| `--opsin-radius-lg` | `1.3125rem` | Cards on a phone, where the card is nearly the width of the screen. |
| `--opsin-radius-xl` | `1.75rem` | Sheets and dialogs. Applied to the leading edge only when the surface meets a screen edge on the other side. |
| `--opsin-radius-inner-card` | `max(calc(0.875rem - 5 * var(--spacing)), var(--opsin-radius-xs))` | A box inside a card at the comfortable density (card padding p-5). |
| `--opsin-radius-inner-card-compact` | `max(calc(0.875rem - 4 * var(--spacing)), var(--opsin-radius-xs))` | A box inside a card at the compact density (card padding p-4). |
| `--opsin-corner-shape` | `superellipse(4)` | The squircle curvature. Degrades to `round` where corner-shape is unsupported. |
| `--opsin-border-hairline` | `1px` | Every boundary in the system by default. |
| `--opsin-border-emphasis` | `2px` | The boundary of a surface carrying `attention` or `urgent`, where the boundary is one of the three non-colour carriers of the status. |
| `--opsin-border-focus` | `2px` | The focus ring. Always 2px with a 2px offset, always in a colour measured against both the surface and the page behind it, and never removed. See /docs/accessibility/keyboard-and-focus. |
| `--opsin-border-focus-offset` | `2px` | Offset for the focus boundary. |
### `.dark, .opsin-product.dark` [#dark-opsin-productdark]
| Variable | Value | Controls |
| ------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-chrome-background` | `oklch(0.145 0.005 250)` | Chrome role background, resolved to the ladder step it names. |
| `--opsin-chrome-foreground` | `oklch(0.967 0.003 250)` | Chrome role foreground, resolved to the ladder step it names. |
| `--opsin-chrome-card` | `oklch(0.205 0.006 250)` | Chrome role card, resolved to the ladder step it names. |
| `--opsin-chrome-card-foreground` | `oklch(0.967 0.003 250)` | Chrome role cardForeground, resolved to the ladder step it names. |
| `--opsin-chrome-muted` | `oklch(0.269 0.007 250)` | Chrome role muted, resolved to the ladder step it names. |
| `--opsin-chrome-muted-foreground` | `oklch(0.871 0.005 250)` | Chrome role mutedForeground, resolved to the ladder step it names. |
| `--opsin-chrome-border` | `oklch(0.556 0.009 250)` | Chrome role border, resolved to the ladder step it names. |
| `--opsin-chrome-input` | `oklch(0.556 0.009 250)` | Chrome role input, resolved to the ladder step it names. |
| `--opsin-chrome-ring` | `oklch(0.68 0.115 250)` | Chrome role ring, resolved to the ladder step it names. |
| `--opsin-chrome-primary` | `oklch(0.72 0.12 250)` | Chrome role primary, resolved to the ladder step it names. |
| `--opsin-chrome-primary-foreground` | `oklch(0.16 0.01 260)` | Chrome role primaryForeground, resolved to the ladder step it names. |
| `--opsin-category-sleep-surface` | `oklch(0.208 0.062 275)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-sleep-line` | `oklch(0.806 0.092 275)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-sleep-ink` | `oklch(0.884 0.057 275)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-sleep-accent` | `oklch(0.714 0.123 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. |
| `--opsin-category-heart-surface` | `oklch(0.208 0.075 15)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-heart-line` | `oklch(0.806 0.112 15)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-heart-ink` | `oklch(0.884 0.062 15)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-heart-accent` | `oklch(0.714 0.15 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. |
| `--opsin-category-activity-surface` | `oklch(0.208 0.07 140)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-activity-line` | `oklch(0.806 0.106 140)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-activity-ink` | `oklch(0.884 0.067 140)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-activity-accent` | `oklch(0.714 0.141 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. |
| `--opsin-category-nutrition-surface` | `oklch(0.208 0.049 70)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-nutrition-line` | `oklch(0.806 0.092 70)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-nutrition-ink` | `oklch(0.884 0.059 70)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-nutrition-accent` | `oklch(0.714 0.123 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. |
| `--opsin-category-mind-surface` | `oklch(0.208 0.062 310)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-mind-line` | `oklch(0.806 0.092 310)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-mind-ink` | `oklch(0.884 0.059 310)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-mind-accent` | `oklch(0.714 0.123 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. |
| `--opsin-category-labs-surface` | `oklch(0.208 0.038 210)` | The tinted background a component in this ramp sits on. |
| `--opsin-category-labs-line` | `oklch(0.806 0.073 210)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-category-labs-ink` | `oklch(0.884 0.046 210)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-category-labs-accent` | `oklch(0.714 0.097 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. |
| `--opsin-status-steady-surface` | `oklch(0.208 0.041 178)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-steady-line` | `oklch(0.806 0.073 178)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-status-steady-ink` | `oklch(0.884 0.046 178)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-steady-accent` | `oklch(0.714 0.097 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. |
| `--opsin-status-watch-surface` | `oklch(0.208 0.046 82)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-watch-line` | `oklch(0.806 0.092 82)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-status-watch-ink` | `oklch(0.884 0.059 82)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-watch-accent` | `oklch(0.714 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. |
| `--opsin-status-attention-surface` | `oklch(0.208 0.065 45)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-attention-line` | `oklch(0.806 0.099 45)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-status-attention-ink` | `oklch(0.884 0.063 45)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-attention-accent` | `oklch(0.714 0.132 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. |
| `--opsin-status-urgent-surface` | `oklch(0.208 0.088 27)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-urgent-line` | `oklch(0.806 0.11 27)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-status-urgent-ink` | `oklch(0.884 0.061 27)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-urgent-accent` | `oklch(0.714 0.176 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. |
| `--opsin-status-unknown-surface` | `oklch(0.208 0.009 250)` | The tinted background a component in this ramp sits on. |
| `--opsin-status-unknown-line` | `oklch(0.806 0.013 250)` | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. |
| `--opsin-status-unknown-ink` | `oklch(0.884 0.008 250)` | Text and text-sized icons on `surface`. Clears the text floor against `surface`. |
| `--opsin-status-unknown-accent` | `oklch(0.714 0.018 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. |
| `--opsin-material-canvas-tint` | `var(--opsin-neutral-950)` | Rung 0 (canvas): the tint over what is behind it. The application background. Nothing is behind it, so nothing shows through it. |
| `--opsin-material-canvas-opaque` | `var(--opsin-neutral-950)` | Rung 0 (canvas): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-inset-tint` | `oklch(0.178 0.006 250)` | Rung 0.5 (inset): the tint over what is behind it. The recessed well inside a card: the explanatory note a Callout draws inside the reading it annotates, and the track a RangeBar lays its range along. Its fill sits below the card fill in both themes, so the well reads as carved into the card rather than lifted off it. In light it is neutral-100, one step under the white card, which is where a Callout already sat. In dark it is a value between the page and the card, below the card so the well is a recess and above the page so a Callout used on its own is still visible. |
| `--opsin-material-inset-opaque` | `oklch(0.178 0.006 250)` | Rung 0.5 (inset): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-card-tint` | `var(--opsin-neutral-900)` | Rung 1 (card): the tint over what is behind it. The default home for a health value. Opaque, bounded by a line rather than a shadow, and the rung every ResultCard, MetricTile and RangeBar sits on. |
| `--opsin-material-card-border` | `var(--opsin-neutral-800)` | Rung 1 (card): the boundary. |
| `--opsin-material-card-opaque` | `var(--opsin-neutral-900)` | Rung 1 (card): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-raised-tint` | `var(--opsin-neutral-800)` | Rung 2 (raised): the tint over what is behind it. Menus, popovers, tooltips, a dragged card. Still opaque: the shadow, not translucency, is what says 'above'. |
| `--opsin-material-raised-border` | `var(--opsin-neutral-700)` | Rung 2 (raised): the boundary. |
| `--opsin-material-raised-opaque` | `var(--opsin-neutral-800)` | Rung 2 (raised): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-sheet-tint` | `var(--opsin-neutral-900)` | Rung 3 (sheet): the tint over what is behind it. A bottom sheet or side panel the reader can dismiss by dragging. Its tint is now near opaque, so the panel lands close to the card colour and a white control inside it no longer floats above a greyer surface. What keeps the covered page recognisable is the scrim around the panel rather than the page reading through the panel, because at this opacity very little of the page shows through the tint. |
| `--opsin-material-sheet-tint-alpha` | `0.9` | Rung 3 (sheet): how opaque that tint is. |
| `--opsin-material-sheet-border` | `oklch(1 0 0 / 0.10)` | Rung 3 (sheet): the boundary. |
| `--opsin-material-sheet-opaque` | `var(--opsin-neutral-900)` | Rung 3 (sheet): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-overlay-tint` | `var(--opsin-neutral-900)` | Rung 4 (overlay): the tint over what is behind it. A pinned toolbar, a tab bar and a floating action bar are all chrome that content scrolls beneath. |
| `--opsin-material-overlay-tint-alpha` | `0.7` | Rung 4 (overlay): how opaque that tint is. |
| `--opsin-material-overlay-border` | `oklch(1 0 0 / 0.12)` | Rung 4 (overlay): the boundary. |
| `--opsin-material-overlay-opaque` | `var(--opsin-neutral-900)` | Rung 4 (overlay): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
| `--opsin-material-scrim-tint` | `var(--opsin-neutral-1000)` | Rung 5 (scrim): the tint over what is behind it. The dimming layer behind a modal dialog or a consent sheet. Its job is to remove the page from consideration, not to look like glass. |
| `--opsin-material-scrim-tint-alpha` | `0.6` | Rung 5 (scrim): how opaque that tint is. |
| `--opsin-material-scrim-opaque` | `oklch(0 0 0 / 0.8)` | Rung 5 (scrim): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. |
### `@supports (color-gamut: p3)` [#supports-color-gamut-p3]
| Variable | Value | Controls |
| ------------------------------------ | ------------------------ | ------------------------------------------------------------------------------------ |
| `--opsin-category-sleep-50` | `oklch(0.972 0.015 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-100` | `oklch(0.941 0.031 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-200` | `oklch(0.884 0.062 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-300` | `oklch(0.806 0.107 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-400` | `oklch(0.714 0.145 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-500` | `oklch(0.622 0.165 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-600` | `oklch(0.541 0.16 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-700` | `oklch(0.452 0.145 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-800` | `oklch(0.362 0.122 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-900` | `oklch(0.276 0.096 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-950` | `oklch(0.208 0.073 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-surface` | `oklch(0.972 0.015 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-line` | `oklch(0.541 0.16 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-ink` | `oklch(0.362 0.122 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-sleep-accent` | `oklch(0.714 0.145 275)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-50` | `oklch(0.972 0.018 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-100` | `oklch(0.941 0.039 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-200` | `oklch(0.884 0.081 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-300` | `oklch(0.806 0.132 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-400` | `oklch(0.714 0.177 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-500` | `oklch(0.622 0.201 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-600` | `oklch(0.541 0.195 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-700` | `oklch(0.452 0.177 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-800` | `oklch(0.362 0.148 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-900` | `oklch(0.276 0.116 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-950` | `oklch(0.208 0.088 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-surface` | `oklch(0.972 0.018 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-line` | `oklch(0.541 0.195 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-ink` | `oklch(0.362 0.148 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-heart-accent` | `oklch(0.714 0.177 15)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-50` | `oklch(0.972 0.026 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-100` | `oklch(0.941 0.045 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-200` | `oklch(0.884 0.079 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-300` | `oklch(0.806 0.125 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-400` | `oklch(0.714 0.166 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-500` | `oklch(0.622 0.189 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-600` | `oklch(0.541 0.183 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-700` | `oklch(0.452 0.166 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-800` | `oklch(0.362 0.139 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-900` | `oklch(0.276 0.108 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-950` | `oklch(0.208 0.083 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-surface` | `oklch(0.972 0.026 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-line` | `oklch(0.541 0.183 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-ink` | `oklch(0.362 0.139 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-activity-accent` | `oklch(0.714 0.166 140)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-50` | `oklch(0.972 0.023 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-100` | `oklch(0.941 0.04 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-200` | `oklch(0.884 0.069 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-300` | `oklch(0.806 0.109 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-400` | `oklch(0.714 0.145 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-500` | `oklch(0.622 0.155 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-600` | `oklch(0.541 0.135 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-700` | `oklch(0.452 0.113 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-800` | `oklch(0.362 0.091 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-900` | `oklch(0.276 0.071 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-950` | `oklch(0.208 0.056 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-surface` | `oklch(0.972 0.023 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-line` | `oklch(0.541 0.135 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-ink` | `oklch(0.362 0.091 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-nutrition-accent` | `oklch(0.714 0.145 70)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-50` | `oklch(0.972 0.02 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-100` | `oklch(0.941 0.04 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-200` | `oklch(0.884 0.069 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-300` | `oklch(0.806 0.109 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-400` | `oklch(0.714 0.145 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-500` | `oklch(0.622 0.165 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-600` | `oklch(0.541 0.16 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-700` | `oklch(0.452 0.145 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-800` | `oklch(0.362 0.122 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-900` | `oklch(0.276 0.096 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-950` | `oklch(0.208 0.073 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-surface` | `oklch(0.972 0.02 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-line` | `oklch(0.541 0.16 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-ink` | `oklch(0.362 0.122 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-mind-accent` | `oklch(0.714 0.145 310)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-50` | `oklch(0.972 0.018 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-100` | `oklch(0.941 0.031 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-200` | `oklch(0.884 0.055 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-300` | `oklch(0.806 0.086 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-400` | `oklch(0.714 0.114 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-500` | `oklch(0.622 0.13 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-600` | `oklch(0.541 0.125 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-700` | `oklch(0.452 0.105 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-800` | `oklch(0.362 0.084 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-900` | `oklch(0.276 0.065 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-950` | `oklch(0.208 0.051 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-surface` | `oklch(0.972 0.018 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-line` | `oklch(0.541 0.125 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-ink` | `oklch(0.362 0.084 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-category-labs-accent` | `oklch(0.714 0.114 210)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-50` | `oklch(0.972 0.018 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-100` | `oklch(0.941 0.031 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-200` | `oklch(0.884 0.055 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-300` | `oklch(0.806 0.086 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-400` | `oklch(0.714 0.114 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-500` | `oklch(0.622 0.13 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-600` | `oklch(0.541 0.126 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-700` | `oklch(0.452 0.114 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-800` | `oklch(0.362 0.091 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-900` | `oklch(0.276 0.071 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-950` | `oklch(0.208 0.055 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-surface` | `oklch(0.972 0.018 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-line` | `oklch(0.541 0.126 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-ink` | `oklch(0.362 0.091 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-steady-accent` | `oklch(0.714 0.114 178)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-50` | `oklch(0.972 0.023 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-100` | `oklch(0.941 0.04 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-200` | `oklch(0.884 0.069 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-300` | `oklch(0.806 0.109 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-400` | `oklch(0.714 0.145 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-500` | `oklch(0.622 0.148 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-600` | `oklch(0.541 0.129 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-700` | `oklch(0.452 0.108 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-800` | `oklch(0.362 0.087 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-900` | `oklch(0.276 0.068 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-950` | `oklch(0.208 0.054 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-surface` | `oklch(0.972 0.023 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-line` | `oklch(0.541 0.129 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-ink` | `oklch(0.362 0.087 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-watch-accent` | `oklch(0.60 0.14 82)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-50` | `oklch(0.972 0.019 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-100` | `oklch(0.941 0.041 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-200` | `oklch(0.884 0.074 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-300` | `oklch(0.806 0.117 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-400` | `oklch(0.714 0.156 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-500` | `oklch(0.622 0.177 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-600` | `oklch(0.541 0.172 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-700` | `oklch(0.452 0.148 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-800` | `oklch(0.362 0.119 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-900` | `oklch(0.276 0.093 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-950` | `oklch(0.208 0.074 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-surface` | `oklch(0.972 0.019 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-line` | `oklch(0.541 0.172 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-ink` | `oklch(0.362 0.119 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-attention-accent` | `oklch(0.714 0.156 45)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-50` | `oklch(0.972 0.018 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-100` | `oklch(0.941 0.039 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-200` | `oklch(0.884 0.079 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-300` | `oklch(0.806 0.142 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-400` | `oklch(0.714 0.208 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-500` | `oklch(0.622 0.236 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-600` | `oklch(0.541 0.229 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-700` | `oklch(0.452 0.208 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-800` | `oklch(0.362 0.168 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-900` | `oklch(0.276 0.129 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-950` | `oklch(0.208 0.1 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-surface` | `oklch(0.972 0.018 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-line` | `oklch(0.541 0.229 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-ink` | `oklch(0.362 0.168 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-urgent-accent` | `oklch(0.714 0.208 27)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-100` | `oklch(0.941 0.006 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-200` | `oklch(0.884 0.01 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-300` | `oklch(0.806 0.016 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-400` | `oklch(0.714 0.021 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-500` | `oklch(0.622 0.024 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-600` | `oklch(0.541 0.023 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-700` | `oklch(0.452 0.021 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-800` | `oklch(0.362 0.017 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-900` | `oklch(0.276 0.014 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-950` | `oklch(0.208 0.01 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-surface` | `oklch(0.208 0.01 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-line` | `oklch(0.541 0.023 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-ink` | `oklch(0.362 0.017 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
| `--opsin-status-unknown-accent` | `oklch(0.714 0.021 250)` | Chroma escalation only: same hue, same lightness, so measured contrast is unchanged. |
{/* opsinjs:generated:end */}
---
# Data attributes
Source: https://opsinjs.pensievelabs.org/reference/generated/data-attributes
Markdown: https://opsinjs.pensievelabs.org/reference/generated/data-attributes.md
Section: Reference · kind: reference · also known as: data-attributes, styling hooks, every data attribute
## How this is generated [#how-this-is-generated]
**Source:** the component sources under `registry/bases/base` and the registry
items generated from them. **Script:** `scripts/build-registry.mts` produces the
data; `scripts/build-reference.mts` writes this page. **Command:**
`pnpm run generate`.
A data attribute is a styling contract, not an implementation detail. Everything
listed here is covered by the [versioning policy](../../project/versioning-policy.mdx):
removing an attribute, or changing the condition under which it appears, is a
breaking change and gets a major version, because somebody's stylesheet selects
on it.
**Four columns.** *Attribute* is the literal string you select on. *Emitted by*
is the component and the part. Attributes appear on parts, not on components as
a whole, and `data-open` on a dialog's backdrop is a different contract from
`data-open` on its panel. *Condition* is the state that produces it, written so
you can reproduce it. *Values* is the closed set, or `(none)` for a boolean
attribute whose presence is the signal.
**Two families appear here.** Attributes inherited from Base UI behave exactly as
Base UI documents them, and are listed with a one-line summary and a link rather
than re-documented. That family is `data-open`, `data-closed`,
`data-starting-style`, `data-ending-style`, `data-disabled` and their relatives.
Attributes opsinjs adds are documented in full, and are the ones carrying system
meaning: the clinical status of a value, the category it belongs to, whether the
data behind it is stale or partial.
**What is deliberately excluded.** ARIA attributes are not data attributes and
are documented per component under Accessibility. Attributes emitted by the
documentation site's own chrome are not part of the system.
**The table below is empty, and the components are not.** The implemented
components under `registry/bases/base` carry `data-slot` on every part, plus
`data-status`, `data-category` and `data-opsinjs-value` where
those apply, and they inherit Base UI's `data-open`, `data-starting-style` and
`data-ending-style`. What is missing is the extraction: the generator does not
read the component sources for `data-*` yet, and a table filled by guessing the
condition and the value set would be inventing a styling contract that consumers
then select on. Until it is written, read the attributes off the anatomy block on
each component's own page. Separately from the system,
`data-opsinjs-not-implemented` is emitted
by every not-built-yet marker on this site, so a program can tell a specification
from a component without parsing prose.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
This script does not read the component sources for `data-*` attributes yet, so the table below has no rows, which is not the same as no attribute being emitted. The base layer under `registry/bases/base` emits `data-slot` on every part, plus opsinjs's `data-status`, `data-category` and `data-opsinjs-value`, and inherits Base UI's `data-open`, `data-starting-style` and `data-ending-style`. That is the whole component contract, and the Handbook states it is closed at those four. The squircle is not among them: Card, Callout and Dialog deliver it with an inline `corner-shape` property, `[corner-shape:var(--opsin-corner-shape)]`, rather than stamping a `data-opsin-shape` attribute, so no component emits one. `data-opsinjs-not-implemented` is not a component attribute either. It is a documentation-site marker that this site's not-built-yet chrome stamps so a program can tell a specification from a shipped component. Filling this table needs the attribute, the part, the condition and the value set together; the vocabulary itself is specified in the Handbook under Data attributes.
{/* opsinjs:generated:end */}
---
# Glossary data
Source: https://opsinjs.pensievelabs.org/reference/generated/glossary
Markdown: https://opsinjs.pensievelabs.org/reference/generated/glossary.md
Section: Reference · kind: reference · also known as: term data, glossary table, plain english data
## How this is generated [#how-this-is-generated]
**Source:** `tokens/glossary.json`, which is the same file the `` component
reads at runtime, so a term that renders in a product is a term that appears here.
**Script:** `scripts/build-reference.mts`. **Command:** `pnpm run generate`.
**This is not the page you want if you are reading.**
[Plain-English A to Z](../../content/plain-english-a-z.mdx) is canonical: it is
written to be read, it groups terms, it explains the judgement behind each
replacement, and it is where a change to the guidance is made. This page is its
flat twin, and the twin carries one row per term with every field of the source
exposed. It exists so that search, the `.md` twins and `/r` all cover the glossary
without anybody writing a second copy of it by hand.
**Five columns.** *Term* is the clinical or technical word. *Plain* is the
replacement to use in an interface. *Definition* is the one-sentence explanation
shown when a reader expands a ``. *Aliases* are the other spellings and
abbreviations a search should match. *Notes* carries the caveat, where the plain
word is not an exact synonym and the difference could matter. Those rows are the
ones worth reading twice.
**Provenance.** Every definition here is written for this project. Nothing is
copied from the NHS A to Z or any other Crown-copyright source; where a source
informed a definition it is cited on the canonical page, not pasted here. A
glossary is exactly the kind of asset that gets copied without checking, so the
rule is absolute.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## A-Z [#a-z]
| Term | Say this instead | Show both | Why |
| ------------------ | ------------------------------------------------------------------------------------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| acute | sudden, or short-lasting | first-use | Often misread as 'severe'. It describes how quickly something started, not how bad it is. |
| adherence | taking a medicine the way it was prescribed | plain-only | 'Compliance' frames the reader as obedient or not; 'adherence' is better but still clinical. Write what actually happened instead. |
| adverse effect | an unwanted effect of a medicine | first-use | 'Adverse' is not everyday English. 'Side effect' is understood, but it can make a serious effect sound minor, so say what the effect is. |
| benign | not cancer | always | This word appears on reports and is one of the few whose plain meaning is genuinely reassuring, so the reader should be able to match the two. |
| biomarker | something measurable in your body that says something about your health | first-use | Common in product copy, almost never understood outside research. |
| blood pressure | the pressure of blood pushing against the walls of your arteries, written as two numbers | first-use | The words are familiar but the two-number format is not. Always explain which number is which where both are shown. |
| BMI | a number worked out from your height and weight | always | The abbreviation is widespread and the calculation is not. Anywhere BMI is shown, say that it does not distinguish muscle from fat and is a poor guide for some people. |
| bradycardia | a slower than usual heart rate | first-use | Appears in device output. Also note that a slow resting heart rate is expected in many fit people. |
| cholesterol | a fatty substance carried in your blood | first-use | Widely known as a word, widely misunderstood as a single number. Most reports give several. |
| chronic | long-lasting, or ongoing | first-use | Often heard as 'severe'. It describes duration. |
| contraindication | a reason this treatment is not safe for you | plain-only | A word with no everyday equivalent that readers guess at. Never show it. |
| diastolic | the pressure between heartbeats, which is the lower of the two blood pressure numbers | always | The reader's own record shows the clinical word, so both are needed to match them up. |
| eGFR | an estimate of how well your kidneys are filtering | always | Appears on results with no explanation. The word 'estimated' matters and should never be dropped. |
| false positive | a result that says something was found when it was not there | first-use | Essential for interpreting any screening result honestly, and impossible to explain after the fact if the first screen did not mention it. |
| fasting | not eating or drinking anything except water for a set time before a test | first-use | Readers frequently assume water is included. Say the time and say that water is allowed. |
| HbA1c | your average blood sugar over about the last three months | always | One of the most common results a consumer app displays and one of the least understood. Also flag that it is reported in two different units worldwide. |
| HDL | one of the types of cholesterol measured in a blood test | always | Frequently described to readers as 'good cholesterol'. Avoid that shorthand: it invites the reader to treat one number as a verdict. |
| hypertension | blood pressure that stays higher than the usual range | first-use | Frequently confused with 'tension' in the sense of stress. |
| hypoglycaemia | blood sugar that has dropped below the range you were given | always | Time-critical. The reader may already know it as 'a hypo', and both forms must be recognisable. |
| in range | inside the range this test uses for comparison | plain-only | The replacement for 'normal'. Always name whose range it is: a laboratory's range, a device maker's range, or a range a clinician set for this person. |
| LDL | one of the types of cholesterol measured in a blood test | always | Usually captioned 'bad cholesterol'. Same objection as HDL: it turns one number into a moral judgement. |
| malignant | cancer | always | Never soften this and never show it without a route to a person. See /docs/health/emergency-and-escalation. |
| mg/dL | milligrams per decilitre, which is a unit used for blood test results | always | See mmol/L. Converting between the two is a correctness surface, not a display preference. |
| mmol/L | millimoles per litre, which is a unit used for blood test results | always | The same measurement is reported in mmol/L in some countries and mg/dL in others, and the numbers are not close. Never show a value without its unit. |
| oedema | swelling caused by fluid building up | first-use | Spelled two ways depending on country; the plain wording avoids the problem entirely. |
| out of range | outside the range this test uses for comparison | plain-only | The replacement for 'abnormal'. It states a fact about a comparison and leaves the interpretation to whoever is entitled to make it. |
| prognosis | what is likely to happen next | plain-only | A word no component should ever need. If a product is showing a prognosis, a clinician wrote it. |
| reference range | the range a test result is compared against | always | The most important term in this glossary. A reference range is a comparison, not a target, and it belongs to a laboratory or a device rather than to the reader. Always name whose range it is and never call it a normal range. |
| remission | a period when the signs of an illness have reduced or gone | first-use | Often heard as 'cured'. The plain wording keeps the distinction. |
| resting heart rate | how many times your heart beats a minute when you are at rest | first-use | Understood as words but frequently confused with the heart rate shown during activity. Say when it was measured. |
| screening | a test offered to people with no symptoms, to look for a possible problem early | first-use | Readers commonly assume a screening test is diagnostic. It is not, and the difference determines what the result means. |
| SpO2 | an estimate of how much oxygen your blood is carrying | always | Shown by consumer devices as a bare percentage. The word 'estimate' is not optional: the measurement is affected by movement, cold hands and skin tone, and the product must say so. |
| systolic | the pressure while your heart beats, which is the higher of the two blood pressure numbers | always | See diastolic. |
| tachycardia | a faster than usual heart rate | first-use | Appears in device output with no explanation and reliably alarms people. |
| titration | changing a dose in small steps until it is right for you | plain-only | No everyday equivalent; the plain wording is complete on its own. |
| triglycerides | a type of fat measured in a blood test | always | Appears on the same report as cholesterol and is routinely mistaken for it. |
## Words we do not use [#words-we-do-not-use]
| Never | Instead | Why |
| ----------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| abnormal | outside the usual range | The counterpart of the same problem, and the version that frightens people. |
| bad | the specific finding, stated plainly | Carries a verdict the system is not entitled to give. |
| diagnosis | say what the reading is and who can interpret it | opsinjs components never diagnose. Using the word implies they do. |
| don't worry | state what the reading means and what happens next | Reassurance the system cannot back up, and it reads as a reason to worry. |
| elevated | higher than | Clinical register; means little to a lay reader, and it sounds like a verdict to the readers who do recognise it. |
| failed | outside the range we expected | A reading is not a test the reader sat. |
| good | say the direction - higher, or lower | Moral framing of something largely outside the reader's control. |
| healthy | in the usual range | A verdict on a life, from one number. A reading can sit inside a range while the person is unwell, and outside it while they are fine. |
| just | delete it | 'Just a bit high' minimises a reading the reader may need to act on. |
| negative | the test did not find \ | Same collision, in the other direction. |
| normal | in the usual range, or the expected range for you | Outside a reference range is not abnormal in the everyday sense of the word, and inside one is not a clean bill of health. 'Normal' also carries a judgement about the person rather than about the reading. This word is banned outright across the system, including in code identifiers. |
| optimal | in the usual range | Sets up every future reading as a decline, in the register of a fitness tracker rather than a clinic. |
| perfect | in the usual range | Sets up every future reading as a decline. |
| poor | lower than the range for you | Judges the person, not the measurement. |
| positive | the test found \ | In everyday English 'positive' means good news. In a test result it usually means the opposite, and the collision is dangerous. |
| unhealthy | higher than the usual range | The same verdict in the other direction. Say which way the reading sits and against whose range; the replacement wording assumes the common case, so where the reading is low, say 'lower than the usual range'. |
{/* opsinjs:generated:end */}
---
# Keyboard
Source: https://opsinjs.pensievelabs.org/reference/generated/keyboard
Markdown: https://opsinjs.pensievelabs.org/reference/generated/keyboard.md
Section: Reference · kind: reference · also known as: key bindings, keyboard table, shortcuts
## How this is generated [#how-this-is-generated]
**Source:** the `` block on every component page, which is the same
block the component page renders rather than a separate list.
**Script:** `scripts/build-reference.mts`. **Command:** `pnpm run generate`.
Aggregating rather than re-authoring is the whole point. A separate global
keyboard list is a document that is correct on the day it is written and wrong
within two releases, because the person changing a component's focus order does
not know it exists. Here, a key that is not documented on its component page
cannot appear on this page, and a component page without a keyboard table fails
the build.
**Four columns.** *Component* and *part* say where you must be focused. *Key* is
written the way a keyboard prints it, so Enter, Esc,
Shift Tab. *Action* describes the outcome, not the
implementation. *Notes* carries the exceptions: what changes in a right-to-left
layout, what a screen reader's browse mode intercepts, what differs on iOS where
an external keyboard is attached.
**What is deliberately excluded.** Browser and operating-system defaults are not
listed, because Tab moving focus forward is not a feature of a
component.
The global focus contract, focus-visible behaviour and the skip-link rules live
in [Keyboard and focus](../../accessibility/keyboard-and-focus.mdx), which is
doctrine and belongs there rather than in a table.
This site's own shortcuts are not part of the system either; the search dialog
opens with Ctrl K and that is a property of the
documentation, not of opsinjs.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
This script does not parse the `` blocks yet, so nothing is aggregated here - not because no component documents its keys. Every component page carries its own table, and that is where the contract is readable today. The global contract every component must meet is in Accessibility under Keyboard and focus.
{/* opsinjs:generated:end */}
---
# Tokens
Source: https://opsinjs.pensievelabs.org/reference/generated/tokens
Markdown: https://opsinjs.pensievelabs.org/reference/generated/tokens.md
Section: Reference · kind: reference · also known as: all tokens, token index, complete token table
## How this is generated [#how-this-is-generated]
**Source:** `tokens/color.json`, `tokens/material.json`, `tokens/motion.json`,
`tokens/type.json`, `tokens/space.json`, `tokens/shape.json`.
**Script:** `scripts/build-tokens.mts`, which also writes `lib/generated/tokens.ts`
and `app/tokens.generated.css`. **Command:** `pnpm run generate`.
Three columns, and the third is the one that matters. *Token* is the name you
write. *What it controls* is the single property or decision it owns. If a token
controls two unrelated things, that is a bug in the token, not a longer sentence
in this column. *Used by* lists the components and foundations that read it, which
is how you find out whether changing a value is a local adjustment or a system
event.
**Tier is shown for every row.** Primitive tokens are raw values and are listed
for completeness only; a component that reads one has broken the rule described in
[Token architecture](../../foundations/token-architecture.mdx). Semantic and
component tiers are the ones you are meant to use.
**What is deliberately excluded.** The documentation site's own theme variables
are not opsinjs tokens and are not listed, and that covers everything lyra,
shadcn and fumadocs define for the chrome you are looking at. They are not part of the public
surface, they carry no semver promise, and confusing the two is the most common
way a consumer ends up styling their product with our sidebar's colours.
If you want to change a value rather than read one, you want
[Theming](../../theming/index.mdx). If you want to know what a value *means*, you
want [Foundations](../../foundations/index.mdx). To change a row here, edit the
JSON source; this page cannot be edited.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Tokens [#tokens]
### color [#color]
#### neutral [#neutral]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------- | --------- | ------------------------ | ------------------------ | ---- | ------------ |
| `--opsin-neutral-0` | primitive | Neutral ramp, step 0. | `oklch(1 0 0)` | same | no component |
| `--opsin-neutral-50` | primitive | Neutral ramp, step 50. | `oklch(0.985 0.002 250)` | same | no component |
| `--opsin-neutral-100` | primitive | Neutral ramp, step 100. | `oklch(0.967 0.003 250)` | same | no component |
| `--opsin-neutral-200` | primitive | Neutral ramp, step 200. | `oklch(0.929 0.004 250)` | same | no component |
| `--opsin-neutral-300` | primitive | Neutral ramp, step 300. | `oklch(0.871 0.005 250)` | same | no component |
| `--opsin-neutral-400` | primitive | Neutral ramp, step 400. | `oklch(0.708 0.008 250)` | same | no component |
| `--opsin-neutral-500` | primitive | Neutral ramp, step 500. | `oklch(0.556 0.009 250)` | same | no component |
| `--opsin-neutral-600` | primitive | Neutral ramp, step 600. | `oklch(0.439 0.009 250)` | same | no component |
| `--opsin-neutral-700` | primitive | Neutral ramp, step 700. | `oklch(0.371 0.008 250)` | same | no component |
| `--opsin-neutral-800` | primitive | Neutral ramp, step 800. | `oklch(0.269 0.007 250)` | same | no component |
| `--opsin-neutral-900` | primitive | Neutral ramp, step 900. | `oklch(0.205 0.006 250)` | same | no component |
| `--opsin-neutral-950` | primitive | Neutral ramp, step 950. | `oklch(0.145 0.005 250)` | same | no component |
| `--opsin-neutral-1000` | primitive | Neutral ramp, step 1000. | `oklch(0 0 0)` | same | no component |
#### chrome [#chrome]
| Token | Tier | What it controls | Light | Dark | Used by |
| ----------------------------------- | -------- | -------------------------------------------------------------------- | ------------------------ | ------------------------ | ------------ |
| `--opsin-chrome-background` | semantic | Chrome role background, resolved to the ladder step it names. | `oklch(0.985 0.002 250)` | `oklch(0.145 0.005 250)` | no component |
| `--opsin-chrome-foreground` | semantic | Chrome role foreground, resolved to the ladder step it names. | `oklch(0.205 0.006 250)` | `oklch(0.967 0.003 250)` | no component |
| `--opsin-chrome-card` | semantic | Chrome role card, resolved to the ladder step it names. | `oklch(1 0 0)` | `oklch(0.205 0.006 250)` | no component |
| `--opsin-chrome-card-foreground` | semantic | Chrome role cardForeground, resolved to the ladder step it names. | `oklch(0.205 0.006 250)` | `oklch(0.967 0.003 250)` | no component |
| `--opsin-chrome-muted` | semantic | Chrome role muted, resolved to the ladder step it names. | `oklch(0.967 0.003 250)` | `oklch(0.269 0.007 250)` | no component |
| `--opsin-chrome-muted-foreground` | semantic | Chrome role mutedForeground, resolved to the ladder step it names. | `oklch(0.439 0.009 250)` | `oklch(0.871 0.005 250)` | no component |
| `--opsin-chrome-border` | semantic | Chrome role border, resolved to the ladder step it names. | `oklch(0.556 0.009 250)` | `oklch(0.556 0.009 250)` | no component |
| `--opsin-chrome-input` | semantic | Chrome role input, resolved to the ladder step it names. | `oklch(0.556 0.009 250)` | `oklch(0.556 0.009 250)` | no component |
| `--opsin-chrome-ring` | semantic | Chrome role ring, resolved to the ladder step it names. | `oklch(0.55 0.13 250)` | `oklch(0.68 0.115 250)` | no component |
| `--opsin-chrome-primary` | semantic | Chrome role primary, resolved to the ladder step it names. | `oklch(0.51 0.135 250)` | `oklch(0.72 0.12 250)` | no component |
| `--opsin-chrome-primary-foreground` | semantic | Chrome role primaryForeground, resolved to the ladder step it names. | `oklch(0.99 0 0)` | `oklch(0.16 0.01 260)` | no component |
#### category [#category]
| Token | Tier | What it controls | Light | Dark | Used by |
| ------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | ------------------------ | ----------------------------------------------- |
| `--opsin-category-sleep-50` | primitive | Sleep ramp, step 50. | `oklch(0.972 0.013 275)` | same | no component |
| `--opsin-category-sleep-100` | primitive | Sleep ramp, step 100. | `oklch(0.941 0.028 275)` | same | no component |
| `--opsin-category-sleep-200` | primitive | Sleep ramp, step 200. | `oklch(0.884 0.057 275)` | same | no component |
| `--opsin-category-sleep-300` | primitive | Sleep ramp, step 300. | `oklch(0.806 0.092 275)` | same | no component |
| `--opsin-category-sleep-400` | primitive | Sleep ramp, step 400. | `oklch(0.714 0.123 275)` | same | no component |
| `--opsin-category-sleep-500` | primitive | Sleep ramp, step 500. | `oklch(0.622 0.14 275)` | same | no component |
| `--opsin-category-sleep-600` | primitive | Sleep ramp, step 600. | `oklch(0.541 0.136 275)` | same | no component |
| `--opsin-category-sleep-700` | primitive | Sleep ramp, step 700. | `oklch(0.452 0.123 275)` | same | no component |
| `--opsin-category-sleep-800` | primitive | Sleep ramp, step 800. | `oklch(0.362 0.104 275)` | same | no component |
| `--opsin-category-sleep-900` | primitive | Sleep ramp, step 900. | `oklch(0.276 0.081 275)` | same | no component |
| `--opsin-category-sleep-950` | primitive | Sleep ramp, step 950. | `oklch(0.208 0.062 275)` | same | no component |
| `--opsin-category-sleep-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.013 275)` | `oklch(0.208 0.062 275)` | no component |
| `--opsin-category-sleep-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.136 275)` | `oklch(0.806 0.092 275)` | trend-sparkline |
| `--opsin-category-sleep-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.104 275)` | `oklch(0.884 0.057 275)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-sleep-accent` | semantic | 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. | `oklch(0.58 0.14 275)` | `oklch(0.714 0.123 275)` | log-sheet |
| `--opsin-category-heart-50` | primitive | Heart ramp, step 50. | `oklch(0.972 0.014 15)` | same | no component |
| `--opsin-category-heart-100` | primitive | Heart ramp, step 100. | `oklch(0.941 0.03 15)` | same | no component |
| `--opsin-category-heart-200` | primitive | Heart ramp, step 200. | `oklch(0.884 0.062 15)` | same | no component |
| `--opsin-category-heart-300` | primitive | Heart ramp, step 300. | `oklch(0.806 0.112 15)` | same | no component |
| `--opsin-category-heart-400` | primitive | Heart ramp, step 400. | `oklch(0.714 0.15 15)` | same | no component |
| `--opsin-category-heart-500` | primitive | Heart ramp, step 500. | `oklch(0.622 0.17 15)` | same | no component |
| `--opsin-category-heart-600` | primitive | Heart ramp, step 600. | `oklch(0.541 0.165 15)` | same | no component |
| `--opsin-category-heart-700` | primitive | Heart ramp, step 700. | `oklch(0.452 0.15 15)` | same | no component |
| `--opsin-category-heart-800` | primitive | Heart ramp, step 800. | `oklch(0.362 0.126 15)` | same | no component |
| `--opsin-category-heart-900` | primitive | Heart ramp, step 900. | `oklch(0.276 0.099 15)` | same | no component |
| `--opsin-category-heart-950` | primitive | Heart ramp, step 950. | `oklch(0.208 0.075 15)` | same | no component |
| `--opsin-category-heart-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.014 15)` | `oklch(0.208 0.075 15)` | no component |
| `--opsin-category-heart-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.165 15)` | `oklch(0.806 0.112 15)` | trend-sparkline |
| `--opsin-category-heart-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.126 15)` | `oklch(0.884 0.062 15)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-heart-accent` | semantic | 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. | `oklch(0.62 0.17 15)` | `oklch(0.714 0.15 15)` | log-sheet |
| `--opsin-category-activity-50` | primitive | Activity ramp, step 50. | `oklch(0.972 0.022 140)` | same | no component |
| `--opsin-category-activity-100` | primitive | Activity ramp, step 100. | `oklch(0.941 0.038 140)` | same | no component |
| `--opsin-category-activity-200` | primitive | Activity ramp, step 200. | `oklch(0.884 0.067 140)` | same | no component |
| `--opsin-category-activity-300` | primitive | Activity ramp, step 300. | `oklch(0.806 0.106 140)` | same | no component |
| `--opsin-category-activity-400` | primitive | Activity ramp, step 400. | `oklch(0.714 0.141 140)` | same | no component |
| `--opsin-category-activity-500` | primitive | Activity ramp, step 500. | `oklch(0.622 0.16 140)` | same | no component |
| `--opsin-category-activity-600` | primitive | Activity ramp, step 600. | `oklch(0.541 0.155 140)` | same | no component |
| `--opsin-category-activity-700` | primitive | Activity ramp, step 700. | `oklch(0.452 0.141 140)` | same | no component |
| `--opsin-category-activity-800` | primitive | Activity ramp, step 800. | `oklch(0.362 0.118 140)` | same | no component |
| `--opsin-category-activity-900` | primitive | Activity ramp, step 900. | `oklch(0.276 0.092 140)` | same | no component |
| `--opsin-category-activity-950` | primitive | Activity ramp, step 950. | `oklch(0.208 0.07 140)` | same | no component |
| `--opsin-category-activity-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.022 140)` | `oklch(0.208 0.07 140)` | no component |
| `--opsin-category-activity-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.155 140)` | `oklch(0.806 0.106 140)` | trend-sparkline |
| `--opsin-category-activity-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.118 140)` | `oklch(0.884 0.067 140)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-activity-accent` | semantic | 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. | `oklch(0.7 0.16 140)` | `oklch(0.714 0.141 140)` | log-sheet |
| `--opsin-category-nutrition-50` | primitive | Nutrition ramp, step 50. | `oklch(0.972 0.02 70)` | same | no component |
| `--opsin-category-nutrition-100` | primitive | Nutrition ramp, step 100. | `oklch(0.941 0.034 70)` | same | no component |
| `--opsin-category-nutrition-200` | primitive | Nutrition ramp, step 200. | `oklch(0.884 0.059 70)` | same | no component |
| `--opsin-category-nutrition-300` | primitive | Nutrition ramp, step 300. | `oklch(0.806 0.092 70)` | same | no component |
| `--opsin-category-nutrition-400` | primitive | Nutrition ramp, step 400. | `oklch(0.714 0.123 70)` | same | no component |
| `--opsin-category-nutrition-500` | primitive | Nutrition ramp, step 500. | `oklch(0.622 0.135 70)` | same | no component |
| `--opsin-category-nutrition-600` | primitive | Nutrition ramp, step 600. | `oklch(0.541 0.118 70)` | same | no component |
| `--opsin-category-nutrition-700` | primitive | Nutrition ramp, step 700. | `oklch(0.452 0.099 70)` | same | no component |
| `--opsin-category-nutrition-800` | primitive | Nutrition ramp, step 800. | `oklch(0.362 0.08 70)` | same | no component |
| `--opsin-category-nutrition-900` | primitive | Nutrition ramp, step 900. | `oklch(0.276 0.062 70)` | same | no component |
| `--opsin-category-nutrition-950` | primitive | Nutrition ramp, step 950. | `oklch(0.208 0.049 70)` | same | no component |
| `--opsin-category-nutrition-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.02 70)` | `oklch(0.208 0.049 70)` | no component |
| `--opsin-category-nutrition-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.118 70)` | `oklch(0.806 0.092 70)` | trend-sparkline |
| `--opsin-category-nutrition-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.08 70)` | `oklch(0.884 0.059 70)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-nutrition-accent` | semantic | 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. | `oklch(0.75 0.14 70)` | `oklch(0.714 0.123 70)` | log-sheet |
| `--opsin-category-mind-50` | primitive | Mind ramp, step 50. | `oklch(0.972 0.018 310)` | same | no component |
| `--opsin-category-mind-100` | primitive | Mind ramp, step 100. | `oklch(0.941 0.034 310)` | same | no component |
| `--opsin-category-mind-200` | primitive | Mind ramp, step 200. | `oklch(0.884 0.059 310)` | same | no component |
| `--opsin-category-mind-300` | primitive | Mind ramp, step 300. | `oklch(0.806 0.092 310)` | same | no component |
| `--opsin-category-mind-400` | primitive | Mind ramp, step 400. | `oklch(0.714 0.123 310)` | same | no component |
| `--opsin-category-mind-500` | primitive | Mind ramp, step 500. | `oklch(0.622 0.14 310)` | same | no component |
| `--opsin-category-mind-600` | primitive | Mind ramp, step 600. | `oklch(0.541 0.136 310)` | same | no component |
| `--opsin-category-mind-700` | primitive | Mind ramp, step 700. | `oklch(0.452 0.123 310)` | same | no component |
| `--opsin-category-mind-800` | primitive | Mind ramp, step 800. | `oklch(0.362 0.104 310)` | same | no component |
| `--opsin-category-mind-900` | primitive | Mind ramp, step 900. | `oklch(0.276 0.081 310)` | same | no component |
| `--opsin-category-mind-950` | primitive | Mind ramp, step 950. | `oklch(0.208 0.062 310)` | same | no component |
| `--opsin-category-mind-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.018 310)` | `oklch(0.208 0.062 310)` | no component |
| `--opsin-category-mind-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.136 310)` | `oklch(0.806 0.092 310)` | trend-sparkline |
| `--opsin-category-mind-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.104 310)` | `oklch(0.884 0.059 310)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-mind-accent` | semantic | 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. | `oklch(0.62 0.14 310)` | `oklch(0.714 0.123 310)` | log-sheet |
| `--opsin-category-labs-50` | primitive | Labs ramp, step 50. | `oklch(0.972 0.015 210)` | same | no component |
| `--opsin-category-labs-100` | primitive | Labs ramp, step 100. | `oklch(0.941 0.026 210)` | same | no component |
| `--opsin-category-labs-200` | primitive | Labs ramp, step 200. | `oklch(0.884 0.046 210)` | same | no component |
| `--opsin-category-labs-300` | primitive | Labs ramp, step 300. | `oklch(0.806 0.073 210)` | same | no component |
| `--opsin-category-labs-400` | primitive | Labs ramp, step 400. | `oklch(0.714 0.097 210)` | same | no component |
| `--opsin-category-labs-500` | primitive | Labs ramp, step 500. | `oklch(0.622 0.108 210)` | same | no component |
| `--opsin-category-labs-600` | primitive | Labs ramp, step 600. | `oklch(0.541 0.094 210)` | same | no component |
| `--opsin-category-labs-700` | primitive | Labs ramp, step 700. | `oklch(0.452 0.079 210)` | same | no component |
| `--opsin-category-labs-800` | primitive | Labs ramp, step 800. | `oklch(0.362 0.063 210)` | same | no component |
| `--opsin-category-labs-900` | primitive | Labs ramp, step 900. | `oklch(0.276 0.049 210)` | same | no component |
| `--opsin-category-labs-950` | primitive | Labs ramp, step 950. | `oklch(0.208 0.038 210)` | same | no component |
| `--opsin-category-labs-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.015 210)` | `oklch(0.208 0.038 210)` | no component |
| `--opsin-category-labs-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.094 210)` | `oklch(0.806 0.073 210)` | trend-sparkline |
| `--opsin-category-labs-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.063 210)` | `oklch(0.884 0.046 210)` | metric-tile, range-bar, result-card, score-dial |
| `--opsin-category-labs-accent` | semantic | 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. | `oklch(0.6 0.11 210)` | `oklch(0.714 0.097 210)` | log-sheet |
#### status [#status]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | ------------------------ | -------------------------------------------------- |
| `--opsin-status-steady-50` | primitive | Steady ramp, step 50. | `oklch(0.972 0.015 178)` | same | no component |
| `--opsin-status-steady-100` | primitive | Steady ramp, step 100. | `oklch(0.941 0.026 178)` | same | no component |
| `--opsin-status-steady-200` | primitive | Steady ramp, step 200. | `oklch(0.884 0.046 178)` | same | no component |
| `--opsin-status-steady-300` | primitive | Steady ramp, step 300. | `oklch(0.806 0.073 178)` | same | no component |
| `--opsin-status-steady-400` | primitive | Steady ramp, step 400. | `oklch(0.714 0.097 178)` | same | no component |
| `--opsin-status-steady-500` | primitive | Steady ramp, step 500. | `oklch(0.622 0.11 178)` | same | no component |
| `--opsin-status-steady-600` | primitive | Steady ramp, step 600. | `oklch(0.541 0.1 178)` | same | no component |
| `--opsin-status-steady-700` | primitive | Steady ramp, step 700. | `oklch(0.452 0.084 178)` | same | no component |
| `--opsin-status-steady-800` | primitive | Steady ramp, step 800. | `oklch(0.362 0.067 178)` | same | no component |
| `--opsin-status-steady-900` | primitive | Steady ramp, step 900. | `oklch(0.276 0.052 178)` | same | no component |
| `--opsin-status-steady-950` | primitive | Steady ramp, step 950. | `oklch(0.208 0.041 178)` | same | no component |
| `--opsin-status-steady-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.015 178)` | `oklch(0.208 0.041 178)` | alert-banner, status-pill |
| `--opsin-status-steady-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.1 178)` | `oklch(0.806 0.073 178)` | alert-banner, range-bar, range-legend, status-pill |
| `--opsin-status-steady-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.067 178)` | `oklch(0.884 0.046 178)` | alert-banner, score-dial, status-pill |
| `--opsin-status-steady-accent` | semantic | 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. | `oklch(0.62 0.11 178)` | `oklch(0.714 0.097 178)` | score-dial |
| `--opsin-status-watch-50` | primitive | Watch ramp, step 50. | `oklch(0.972 0.02 82)` | same | no component |
| `--opsin-status-watch-100` | primitive | Watch ramp, step 100. | `oklch(0.941 0.034 82)` | same | no component |
| `--opsin-status-watch-200` | primitive | Watch ramp, step 200. | `oklch(0.884 0.059 82)` | same | no component |
| `--opsin-status-watch-300` | primitive | Watch ramp, step 300. | `oklch(0.806 0.092 82)` | same | no component |
| `--opsin-status-watch-400` | primitive | Watch ramp, step 400. | `oklch(0.714 0.123 82)` | same | no component |
| `--opsin-status-watch-500` | primitive | Watch ramp, step 500. | `oklch(0.622 0.129 82)` | same | no component |
| `--opsin-status-watch-600` | primitive | Watch ramp, step 600. | `oklch(0.541 0.112 82)` | same | no component |
| `--opsin-status-watch-700` | primitive | Watch ramp, step 700. | `oklch(0.452 0.094 82)` | same | no component |
| `--opsin-status-watch-800` | primitive | Watch ramp, step 800. | `oklch(0.362 0.076 82)` | same | no component |
| `--opsin-status-watch-900` | primitive | Watch ramp, step 900. | `oklch(0.276 0.059 82)` | same | no component |
| `--opsin-status-watch-950` | primitive | Watch ramp, step 950. | `oklch(0.208 0.046 82)` | same | no component |
| `--opsin-status-watch-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.02 82)` | `oklch(0.208 0.046 82)` | alert-banner, status-pill |
| `--opsin-status-watch-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.112 82)` | `oklch(0.806 0.092 82)` | alert-banner, range-bar, range-legend, status-pill |
| `--opsin-status-watch-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.076 82)` | `oklch(0.884 0.059 82)` | alert-banner, score-dial, status-pill |
| `--opsin-status-watch-accent` | semantic | 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. | `oklch(0.60 0.123 82)` | `oklch(0.714 0.123 82)` | score-dial |
| `--opsin-status-attention-50` | primitive | Needs attention ramp, step 50. | `oklch(0.972 0.015 45)` | same | no component |
| `--opsin-status-attention-100` | primitive | Needs attention ramp, step 100. | `oklch(0.941 0.032 45)` | same | no component |
| `--opsin-status-attention-200` | primitive | Needs attention ramp, step 200. | `oklch(0.884 0.063 45)` | same | no component |
| `--opsin-status-attention-300` | primitive | Needs attention ramp, step 300. | `oklch(0.806 0.099 45)` | same | no component |
| `--opsin-status-attention-400` | primitive | Needs attention ramp, step 400. | `oklch(0.714 0.132 45)` | same | no component |
| `--opsin-status-attention-500` | primitive | Needs attention ramp, step 500. | `oklch(0.622 0.15 45)` | same | no component |
| `--opsin-status-attention-600` | primitive | Needs attention ramp, step 600. | `oklch(0.541 0.145 45)` | same | no component |
| `--opsin-status-attention-700` | primitive | Needs attention ramp, step 700. | `oklch(0.452 0.13 45)` | same | no component |
| `--opsin-status-attention-800` | primitive | Needs attention ramp, step 800. | `oklch(0.362 0.105 45)` | same | no component |
| `--opsin-status-attention-900` | primitive | Needs attention ramp, step 900. | `oklch(0.276 0.082 45)` | same | no component |
| `--opsin-status-attention-950` | primitive | Needs attention ramp, step 950. | `oklch(0.208 0.065 45)` | same | no component |
| `--opsin-status-attention-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.015 45)` | `oklch(0.208 0.065 45)` | alert-banner, status-pill |
| `--opsin-status-attention-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.145 45)` | `oklch(0.806 0.099 45)` | alert-banner, range-bar, range-legend, status-pill |
| `--opsin-status-attention-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.105 45)` | `oklch(0.884 0.063 45)` | alert-banner, score-dial, status-pill |
| `--opsin-status-attention-accent` | semantic | 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. | `oklch(0.58 0.15 45)` | `oklch(0.714 0.132 45)` | score-dial |
| `--opsin-status-urgent-50` | primitive | Urgent ramp, step 50. | `oklch(0.972 0.014 27)` | same | no component |
| `--opsin-status-urgent-100` | primitive | Urgent ramp, step 100. | `oklch(0.941 0.03 27)` | same | no component |
| `--opsin-status-urgent-200` | primitive | Urgent ramp, step 200. | `oklch(0.884 0.061 27)` | same | no component |
| `--opsin-status-urgent-300` | primitive | Urgent ramp, step 300. | `oklch(0.806 0.11 27)` | same | no component |
| `--opsin-status-urgent-400` | primitive | Urgent ramp, step 400. | `oklch(0.714 0.176 27)` | same | no component |
| `--opsin-status-urgent-500` | primitive | Urgent ramp, step 500. | `oklch(0.622 0.2 27)` | same | no component |
| `--opsin-status-urgent-600` | primitive | Urgent ramp, step 600. | `oklch(0.541 0.194 27)` | same | no component |
| `--opsin-status-urgent-700` | primitive | Urgent ramp, step 700. | `oklch(0.452 0.176 27)` | same | no component |
| `--opsin-status-urgent-800` | primitive | Urgent ramp, step 800. | `oklch(0.362 0.148 27)` | same | no component |
| `--opsin-status-urgent-900` | primitive | Urgent ramp, step 900. | `oklch(0.276 0.115 27)` | same | no component |
| `--opsin-status-urgent-950` | primitive | Urgent ramp, step 950. | `oklch(0.208 0.088 27)` | same | no component |
| `--opsin-status-urgent-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.014 27)` | `oklch(0.208 0.088 27)` | alert-banner, status-pill |
| `--opsin-status-urgent-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.194 27)` | `oklch(0.806 0.11 27)` | alert-banner, range-bar, range-legend, status-pill |
| `--opsin-status-urgent-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.148 27)` | `oklch(0.884 0.061 27)` | alert-banner, score-dial, status-pill |
| `--opsin-status-urgent-accent` | semantic | 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. | `oklch(0.52 0.2 27)` | `oklch(0.714 0.176 27)` | score-dial |
| `--opsin-status-unknown-50` | primitive | Unknown ramp, step 50. | `oklch(0.972 0.003 250)` | same | no component |
| `--opsin-status-unknown-100` | primitive | Unknown ramp, step 100. | `oklch(0.941 0.005 250)` | same | no component |
| `--opsin-status-unknown-200` | primitive | Unknown ramp, step 200. | `oklch(0.884 0.008 250)` | same | no component |
| `--opsin-status-unknown-300` | primitive | Unknown ramp, step 300. | `oklch(0.806 0.013 250)` | same | no component |
| `--opsin-status-unknown-400` | primitive | Unknown ramp, step 400. | `oklch(0.714 0.018 250)` | same | no component |
| `--opsin-status-unknown-500` | primitive | Unknown ramp, step 500. | `oklch(0.622 0.02 250)` | same | no component |
| `--opsin-status-unknown-600` | primitive | Unknown ramp, step 600. | `oklch(0.541 0.019 250)` | same | no component |
| `--opsin-status-unknown-700` | primitive | Unknown ramp, step 700. | `oklch(0.452 0.018 250)` | same | no component |
| `--opsin-status-unknown-800` | primitive | Unknown ramp, step 800. | `oklch(0.362 0.015 250)` | same | no component |
| `--opsin-status-unknown-900` | primitive | Unknown ramp, step 900. | `oklch(0.276 0.012 250)` | same | no component |
| `--opsin-status-unknown-950` | primitive | Unknown ramp, step 950. | `oklch(0.208 0.009 250)` | same | no component |
| `--opsin-status-unknown-surface` | semantic | The tinted background a component in this ramp sits on. | `oklch(0.972 0.003 250)` | `oklch(0.208 0.009 250)` | no component |
| `--opsin-status-unknown-line` | semantic | The boundary, icon stroke or chart mark. The lightest value in the ramp that clears the non-text floor against `surface`. | `oklch(0.541 0.019 250)` | `oklch(0.806 0.013 250)` | no component |
| `--opsin-status-unknown-ink` | semantic | Text and text-sized icons on `surface`. Clears the text floor against `surface`. | `oklch(0.362 0.015 250)` | `oklch(0.884 0.008 250)` | no component |
| `--opsin-status-unknown-accent` | semantic | 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. | `oklch(0.72 0.02 250)` | `oklch(0.714 0.018 250)` | no component |
### material [#material]
#### material [#material-1]
| Token | Tier | What it controls | Light | Dark | Used by |
| ------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------- | -------------------------------------------------------- |
| `--opsin-material-canvas-tint` | semantic | Rung 0 (canvas): the tint over what is behind it. The application background. Nothing is behind it, so nothing shows through it. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-950)` | surface |
| `--opsin-material-canvas-tint-alpha` | semantic | Rung 0 (canvas): how opaque that tint is. | `1` | same | surface |
| `--opsin-material-canvas-blur` | semantic | Rung 0 (canvas): backdrop blur radius. | `0px` | same | surface |
| `--opsin-material-canvas-saturation` | semantic | Rung 0 (canvas): backdrop saturation multiplier. | `1` | same | surface |
| `--opsin-material-canvas-border` | semantic | Rung 0 (canvas): the boundary. | `none` | same | surface |
| `--opsin-material-canvas-shadow` | semantic | Rung 0 (canvas): the shadow that separates it from what is behind. | `none` | same | surface |
| `--opsin-material-canvas-scrim` | semantic | Rung 0 (canvas): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0` | same | surface |
| `--opsin-material-canvas-opaque` | semantic | Rung 0 (canvas): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-950)` | surface |
| `--opsin-material-inset-tint` | semantic | Rung 0.5 (inset): the tint over what is behind it. The recessed well inside a card: the explanatory note a Callout draws inside the reading it annotates, and the track a RangeBar lays its range along. Its fill sits below the card fill in both themes, so the well reads as carved into the card rather than lifted off it. In light it is neutral-100, one step under the white card, which is where a Callout already sat. In dark it is a value between the page and the card, below the card so the well is a recess and above the page so a Callout used on its own is still visible. | `var(--opsin-neutral-100)` | `oklch(0.178 0.006 250)` | callout, range-bar, surface |
| `--opsin-material-inset-tint-alpha` | semantic | Rung 0.5 (inset): how opaque that tint is. | `1` | same | surface |
| `--opsin-material-inset-blur` | semantic | Rung 0.5 (inset): backdrop blur radius. | `0px` | same | surface |
| `--opsin-material-inset-saturation` | semantic | Rung 0.5 (inset): backdrop saturation multiplier. | `1` | same | surface |
| `--opsin-material-inset-border` | semantic | Rung 0.5 (inset): the boundary. | `none` | same | surface |
| `--opsin-material-inset-shadow` | semantic | Rung 0.5 (inset): the shadow that separates it from what is behind. | `none` | same | surface |
| `--opsin-material-inset-scrim` | semantic | Rung 0.5 (inset): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0` | same | surface |
| `--opsin-material-inset-opaque` | semantic | Rung 0.5 (inset): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-100)` | `oklch(0.178 0.006 250)` | surface |
| `--opsin-material-card-tint` | semantic | Rung 1 (card): the tint over what is behind it. The default home for a health value. Opaque, bounded by a line rather than a shadow, and the rung every ResultCard, MetricTile and RangeBar sits on. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-card-tint-alpha` | semantic | Rung 1 (card): how opaque that tint is. | `1` | same | surface |
| `--opsin-material-card-blur` | semantic | Rung 1 (card): backdrop blur radius. | `0px` | same | surface |
| `--opsin-material-card-saturation` | semantic | Rung 1 (card): backdrop saturation multiplier. | `1` | same | surface |
| `--opsin-material-card-border` | semantic | Rung 1 (card): the boundary. | `var(--opsin-neutral-200)` | `var(--opsin-neutral-800)` | surface |
| `--opsin-material-card-shadow` | semantic | Rung 1 (card): the shadow that separates it from what is behind. | `none` | same | surface |
| `--opsin-material-card-scrim` | semantic | Rung 1 (card): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0` | same | surface |
| `--opsin-material-card-opaque` | semantic | Rung 1 (card): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-raised-tint` | semantic | Rung 2 (raised): the tint over what is behind it. Menus, popovers, tooltips, a dragged card. Still opaque: the shadow, not translucency, is what says 'above'. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-800)` | surface |
| `--opsin-material-raised-tint-alpha` | semantic | Rung 2 (raised): how opaque that tint is. | `1` | same | surface |
| `--opsin-material-raised-blur` | semantic | Rung 2 (raised): backdrop blur radius. | `0px` | same | surface |
| `--opsin-material-raised-saturation` | semantic | Rung 2 (raised): backdrop saturation multiplier. | `1` | same | surface |
| `--opsin-material-raised-border` | semantic | Rung 2 (raised): the boundary. | `var(--opsin-neutral-200)` | `var(--opsin-neutral-700)` | surface |
| `--opsin-material-raised-shadow` | semantic | Rung 2 (raised): the shadow that separates it from what is behind. | `0 1px 2px oklch(0 0 0 / 0.06), 0 4px 12px oklch(0 0 0 / 0.06)` | same | surface |
| `--opsin-material-raised-scrim` | semantic | Rung 2 (raised): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0` | same | surface |
| `--opsin-material-raised-opaque` | semantic | Rung 2 (raised): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-800)` | surface |
| `--opsin-material-sheet-tint` | semantic | Rung 3 (sheet): the tint over what is behind it. A bottom sheet or side panel the reader can dismiss by dragging. Its tint is now near opaque, so the panel lands close to the card colour and a white control inside it no longer floats above a greyer surface. What keeps the covered page recognisable is the scrim around the panel rather than the page reading through the panel, because at this opacity very little of the page shows through the tint. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-sheet-tint-alpha` | semantic | Rung 3 (sheet): how opaque that tint is. | `0.93` | `0.9` | surface |
| `--opsin-material-sheet-blur` | semantic | Rung 3 (sheet): backdrop blur radius. | `20px` | same | surface |
| `--opsin-material-sheet-saturation` | semantic | Rung 3 (sheet): backdrop saturation multiplier. | `1.6` | same | surface |
| `--opsin-material-sheet-border` | semantic | Rung 3 (sheet): the boundary. | `oklch(0 0 0 / 0.08)` | `oklch(1 0 0 / 0.10)` | surface |
| `--opsin-material-sheet-shadow` | semantic | Rung 3 (sheet): the shadow that separates it from what is behind. | `0 -1px 2px oklch(0 0 0 / 0.05)` | same | sheet, surface |
| `--opsin-material-sheet-scrim` | semantic | Rung 3 (sheet): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0.93` | same | surface |
| `--opsin-material-sheet-opaque` | semantic | Rung 3 (sheet): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-overlay-tint` | semantic | Rung 4 (overlay): the tint over what is behind it. A pinned toolbar, a tab bar and a floating action bar are all chrome that content scrolls beneath. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-overlay-tint-alpha` | semantic | Rung 4 (overlay): how opaque that tint is. | `0.74` | `0.7` | surface |
| `--opsin-material-overlay-blur` | semantic | Rung 4 (overlay): backdrop blur radius. | `28px` | same | surface |
| `--opsin-material-overlay-saturation` | semantic | Rung 4 (overlay): backdrop saturation multiplier. | `1.8` | same | surface |
| `--opsin-material-overlay-border` | semantic | Rung 4 (overlay): the boundary. | `oklch(0 0 0 / 0.10)` | `oklch(1 0 0 / 0.12)` | surface |
| `--opsin-material-overlay-shadow` | semantic | Rung 4 (overlay): the shadow that separates it from what is behind. | `0 8px 32px oklch(0 0 0 / 0.12)` | same | combobox, menu, popover, select, surface, toast, tooltip |
| `--opsin-material-overlay-scrim` | semantic | Rung 4 (overlay): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0.74` | same | surface |
| `--opsin-material-overlay-opaque` | semantic | Rung 4 (overlay): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `var(--opsin-neutral-0)` | `var(--opsin-neutral-900)` | surface |
| `--opsin-material-scrim-tint` | semantic | Rung 5 (scrim): the tint over what is behind it. The dimming layer behind a modal dialog or a consent sheet. Its job is to remove the page from consideration, not to look like glass. | `var(--opsin-neutral-950)` | `var(--opsin-neutral-1000)` | surface |
| `--opsin-material-scrim-tint-alpha` | semantic | Rung 5 (scrim): how opaque that tint is. | `0.44` | `0.6` | surface |
| `--opsin-material-scrim-blur` | semantic | Rung 5 (scrim): backdrop blur radius. | `2px` | same | surface |
| `--opsin-material-scrim-saturation` | semantic | Rung 5 (scrim): backdrop saturation multiplier. | `1` | same | surface |
| `--opsin-material-scrim-border` | semantic | Rung 5 (scrim): the boundary. | `none` | same | surface |
| `--opsin-material-scrim-shadow` | semantic | Rung 5 (scrim): the shadow that separates it from what is behind. | `none` | same | surface |
| `--opsin-material-scrim-scrim` | semantic | Rung 5 (scrim): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | `0.44` | same | surface |
| `--opsin-material-scrim-opaque` | semantic | Rung 5 (scrim): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | `oklch(0.205 0.006 250 / 0.72)` | `oklch(0 0 0 / 0.8)` | surface |
### motion [#motion]
#### ease [#ease]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-ease-spring-snap` | semantic | Direct manipulation only: a switch the reader just flipped, a segmented control, a pressed button settling. It overshoots by 1.5%. That is enough to feel physical but not enough to look playful. The 1.5% is the largest overshoot in the system but not the only one: `spring-settle` overshoots by 0.88%. Only `spring-calm` and `spring-sheet` reach their target without passing it. | `linear(0, 0.0715, 0.2271, 0.4053, 0.5722, 0.7119, 0.8198, 0.8978, 0.9505, 0.9836, 1.0025, 1.0118, 1.015, 1.0147, 1.0126, 1.01, 1.0073, 1.0051, 1.0033, 1.0019, 1)` | same | no component |
| `--opsin-ease-spring-settle` | semantic | The workhorse for chrome: popovers, tooltips, menus, chips appearing and disappearing. | `linear(0, 0.0742, 0.2328, 0.4113, 0.5758, 0.7116, 0.8157, 0.8905, 0.9412, 0.9736, 0.9928, 1.003, 1.0076, 1.0088, 1.0082, 1.0068, 1.0053, 1.0038, 1.0026, 1.0017, 1)` | same | no component |
| `--opsin-ease-spring-calm` | semantic | A health value that changes while it is already on screen: a bar re-filling from one reading to the next, a dial travelling between two values the reader has already been shown. Never a first paint and never a first reveal. A value arrives at its final figure, with no count-up, no dial sweep and no line drawing itself in (health/motion-in-health-ui rule 2). Slightly overdamped (zeta just over 1) so it never overshoots and never bounces. | `linear(0, 0.0829, 0.2457, 0.4157, 0.5642, 0.6832, 0.774, 0.841, 0.8893, 0.9236, 0.9476, 0.9643, 0.9757, 0.9836, 0.9889, 0.9926, 0.995, 0.9967, 0.9978, 0.9985, 1)` | same | no component |
| `--opsin-ease-spring-sheet` | semantic | Large surfaces travelling a long distance: sheets, dialogs, full-screen pushes. Overdamped, because a sheet that bounces at the top of its travel reads as a dropped object. | `linear(0, 0.0881, 0.2576, 0.431, 0.5798, 0.6971, 0.7854, 0.8497, 0.8958, 0.9282, 0.9508, 0.9664, 0.9771, 0.9845, 0.9895, 0.9929, 0.9952, 0.9967, 0.9978, 0.9985, 1)` | same | dialog, sheet |
| `--opsin-ease-standard` | semantic | Non-spring transitions where a spring would be overkill: colour, opacity, border. Fast out, slow in. | `cubic-bezier(0.2, 0, 0, 1)` | same | accordion, body-map, button, checkbox, combobox, consent-sheet, dialog, menu, number-field, popover, progress, radio-group, scale-input, scroll-area, segmented-control, select, sheet, skeleton, slider, switch, symptom-picker, tab-bar, tabs, textarea, toast, tooltip |
| `--opsin-ease-enter` | semantic | Something arriving from off-screen or from nothing. Decelerating, because an arrival should feel like it is coming to rest. | `cubic-bezier(0.05, 0.7, 0.1, 1)` | same | no component |
| `--opsin-ease-exit` | semantic | Something leaving. Accelerating and shorter than its enter, because a reader does not need to watch a dismissal finish. | `cubic-bezier(0.3, 0, 0.8, 0.15)` | same | dialog |
#### duration [#duration]
| Token | Tier | What it controls | Light | Dark | Used by |
| -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-duration-spring-snap` | semantic | Settle time for the spring-snap spring, measured from its own parameters. | `283ms` | same | no component |
| `--opsin-duration-spring-settle` | semantic | Settle time for the spring-settle spring, measured from its own parameters. | `382ms` | same | no component |
| `--opsin-duration-spring-calm` | semantic | Settle time for the spring-calm spring, measured from its own parameters. | `550ms` | same | no component |
| `--opsin-duration-spring-sheet` | semantic | Settle time for the spring-sheet spring, measured from its own parameters. | `483ms` | same | dialog, sheet |
| `--opsin-duration-instant` | semantic | State change with no travel: hover tint, focus ring, checkbox tick. | `80ms` | same | no component |
| `--opsin-duration-fast` | semantic | Small elements moving a small distance. | `140ms` | same | accordion, body-map, button, checkbox, combobox, consent-sheet, dialog, menu, number-field, popover, progress, radio-group, scale-input, scroll-area, segmented-control, select, sheet, skeleton, slider, switch, symptom-picker, tab-bar, tabs, textarea, toast, tooltip |
| `--opsin-duration-base` | semantic | The default for chrome that is not spring-driven. | `220ms` | same | dialog, sheet |
| `--opsin-duration-slow` | semantic | Layout change: a list reflowing, a card expanding. | `360ms` | same | no component |
| `--opsin-duration-deliberate` | semantic | The ceiling on a transition a reader is waiting on, such as a first-run reveal or a consent sheet, where the point is that the reader notices. No such transition may exceed it. A looping placeholder period, like the skeleton shimmer, is bounded instead by its iteration count, so a single sweep of it may run longer. | `560ms` | same | no component |
| `--opsin-duration-shimmer` | semantic | One sweep of a loading placeholder's sheen. This is a period, the time for the sheen to make one pass across the bar, and not a travel time. At 1600ms the movement reads as calm rather than as urgency, which the first motion rule requires of anything that is not a value the reader controls. Its consumer pairs it with a finite iteration count so the total motion stays under the five seconds at which WCAG 2.2 SC 2.2.2 engages. | `1600ms` | same | skeleton |
### type [#type]
#### font [#font]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------- | -------- | ------------------------- | ---------------------------------------------------------------------------------------- | ---- | ------------------ |
| `--opsin-font-sans` | semantic | The sans family stack. | `system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif` | same | no component |
| `--opsin-font-mono` | semantic | The mono family stack. | `ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace` | same | no component |
| `--opsin-font-numeric` | semantic | The numeric family stack. | `system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | same | result-card, value |
#### text [#text]
| Token | Tier | What it controls | Light | Dark | Used by |
| -------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-text-large-title-size` | semantic | The largest step in the scale, reserved for a surface whose entire subject is one number or one word. No component in the system sets a value at this step yet. | `2.125rem` | same | no component |
| `--opsin-text-large-title-leading` | semantic | Line height for largeTitle, unitless so it scales with the size. | `1.206` | same | no component |
| `--opsin-text-large-title-tracking` | semantic | Letter spacing for largeTitle. | `-0.011em` | same | no component |
| `--opsin-text-large-title-weight` | semantic | Font weight for largeTitle. | `700` | same | no component |
| `--opsin-text-title1-size` | semantic | A screen title, and the hero treatment a value's number takes at size display. | `1.75rem` | same | result-card, value |
| `--opsin-text-title1-leading` | semantic | Line height for title1, unitless so it scales with the size. | `1.214` | same | result-card, value |
| `--opsin-text-title1-tracking` | semantic | Letter spacing for title1. | `-0.009em` | same | result-card, value |
| `--opsin-text-title1-weight` | semantic | Font weight for title1. | `700` | same | result-card, value |
| `--opsin-text-title2-size` | semantic | Section title; the floor for a primary health value inside a card. | `1.375rem` | same | goal-ring |
| `--opsin-text-title2-leading` | semantic | Line height for title2, unitless so it scales with the size. | `1.273` | same | goal-ring |
| `--opsin-text-title2-tracking` | semantic | Letter spacing for title2. | `-0.006em` | same | goal-ring |
| `--opsin-text-title2-weight` | semantic | Font weight for title2. | `600` | same | goal-ring |
| `--opsin-text-title3-size` | semantic | A Dialog or Sheet title, and the unit beside a value's number at size display. | `1.25rem` | same | dialog, questionnaire, reading-input, sheet, value |
| `--opsin-text-title3-leading` | semantic | Line height for title3, unitless so it scales with the size. | `1.25` | same | dialog, questionnaire, reading-input, sheet, value |
| `--opsin-text-title3-tracking` | semantic | Letter spacing for title3. | `-0.004em` | same | dialog, questionnaire, reading-input, sheet, value |
| `--opsin-text-title3-weight` | semantic | Font weight for title3. | `600` | same | dialog, questionnaire, reading-input, sheet, value |
| `--opsin-text-headline-size` | semantic | An emphasised line of body text, such as a status sentence or a question in a form. | `1.0625rem` | same | accordion, alert-banner, avatar, button, callout, card, care-card, empty-state, field, log-sheet, menu, popover, range-bar, reading-input, result-card, scale-input, segmented-control, status-pill, surface, tabs, textarea, timeline-entry, toast, trend-sparkline, value |
| `--opsin-text-headline-leading` | semantic | Line height for headline, unitless so it scales with the size. | `1.294` | same | accordion, alert-banner, avatar, button, callout, card, care-card, empty-state, field, log-sheet, menu, popover, range-bar, reading-input, result-card, scale-input, segmented-control, status-pill, surface, tabs, textarea, timeline-entry, toast, trend-sparkline, value |
| `--opsin-text-headline-tracking` | semantic | Letter spacing for headline. | `-0.003em` | same | accordion, alert-banner, avatar, button, callout, card, care-card, empty-state, field, log-sheet, menu, popover, range-bar, reading-input, result-card, scale-input, segmented-control, status-pill, surface, tabs, textarea, timeline-entry, toast, trend-sparkline, value |
| `--opsin-text-headline-weight` | semantic | Font weight for headline. | `600` | same | accordion, alert-banner, avatar, button, callout, card, care-card, empty-state, field, log-sheet, menu, popover, range-bar, reading-input, result-card, scale-input, segmented-control, status-pill, surface, tabs, textarea, timeline-entry, toast, trend-sparkline, value |
| `--opsin-text-body-size` | semantic | Everything a reader reads as prose. The anchor of the scale. | `1.0625rem` | same | accordion, alert-banner, badge, callout, card, care-card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, empty-state, field, link, menu, number-field, popover, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, result-card, score-dial, scroll-area, select, sheet, surface, switch, symptom-picker, table, tabs, term, textarea, timeline-entry, toast, tooltip, trend-sparkline, value, visually-hidden |
| `--opsin-text-body-leading` | semantic | Line height for body, unitless so it scales with the size. | `1.294` | same | accordion, alert-banner, badge, callout, card, care-card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, empty-state, field, link, menu, number-field, popover, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, result-card, score-dial, scroll-area, select, sheet, skeleton, surface, switch, symptom-picker, table, tabs, term, textarea, timeline-entry, toast, tooltip, trend-sparkline, value, visually-hidden |
| `--opsin-text-body-tracking` | semantic | Letter spacing for body. | `-0.003em` | same | accordion, alert-banner, badge, callout, card, care-card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, empty-state, field, link, menu, number-field, popover, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, result-card, score-dial, scroll-area, select, sheet, surface, switch, symptom-picker, table, tabs, term, textarea, timeline-entry, toast, tooltip, trend-sparkline, value, visually-hidden |
| `--opsin-text-body-weight` | semantic | Font weight for body. | `400` | same | accordion, alert-banner, badge, callout, card, care-card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, empty-state, field, link, menu, number-field, popover, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, result-card, score-dial, scroll-area, select, sheet, surface, switch, symptom-picker, table, tabs, term, textarea, timeline-entry, toast, tooltip, trend-sparkline, value, visually-hidden |
| `--opsin-text-callout-size` | semantic | Secondary prose inside a card or a callout. | `1rem` | same | dialog, sheet, toast |
| `--opsin-text-callout-leading` | semantic | Line height for callout, unitless so it scales with the size. | `1.313` | same | dialog, sheet, toast |
| `--opsin-text-callout-tracking` | semantic | Letter spacing for callout. | `-0.002em` | same | dialog, sheet, toast |
| `--opsin-text-callout-weight` | semantic | Font weight for callout. | `400` | same | dialog, sheet, toast |
| `--opsin-text-subheadline-size` | semantic | A supporting line under a title. | `0.9375rem` | same | button, care-card, empty-state, goal-ring, metric-tile, progress, radio-group, score-dial, segmented-control, slider, spinner, status-pill, stepper, table |
| `--opsin-text-subheadline-leading` | semantic | Line height for subheadline, unitless so it scales with the size. | `1.333` | same | button, care-card, empty-state, goal-ring, metric-tile, progress, radio-group, score-dial, segmented-control, slider, spinner, status-pill, stepper, table |
| `--opsin-text-subheadline-tracking` | semantic | Letter spacing for subheadline. | `-0.001em` | same | button, care-card, empty-state, goal-ring, metric-tile, progress, radio-group, score-dial, segmented-control, slider, spinner, status-pill, stepper, table |
| `--opsin-text-subheadline-weight` | semantic | Font weight for subheadline. | `400` | same | button, care-card, empty-state, goal-ring, metric-tile, progress, radio-group, score-dial, segmented-control, slider, spinner, status-pill, stepper, table |
| `--opsin-text-subheadline-emphasis-size` | semantic | The emphasised twin of subheadline, for a compact control's label, where the control must read as narrower and never as quieter. | `0.9375rem` | same | avatar, button |
| `--opsin-text-subheadline-emphasis-leading` | semantic | Line height for subheadlineEmphasis, unitless so it scales with the size. | `1.333` | same | avatar, button |
| `--opsin-text-subheadline-emphasis-tracking` | semantic | Letter spacing for subheadlineEmphasis. | `-0.001em` | same | avatar, button |
| `--opsin-text-subheadline-emphasis-weight` | semantic | Font weight for subheadlineEmphasis. | `600` | same | avatar, button |
| `--opsin-text-footnote-size` | semantic | Provenance: who measured this, when, with what. | `0.8125rem` | same | body-map, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, goal-ring, log-sheet, menu, metric-tile, questionnaire, radio-group, range-bar, range-legend, result-card, scale-input, score-dial, sheet, source-citation, status-pill, stepper, switch, symptom-picker, table, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-text-footnote-leading` | semantic | Line height for footnote, unitless so it scales with the size. | `1.385` | same | body-map, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, goal-ring, log-sheet, menu, metric-tile, questionnaire, radio-group, range-bar, range-legend, result-card, scale-input, score-dial, sheet, source-citation, status-pill, stepper, switch, symptom-picker, table, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-text-footnote-tracking` | semantic | Letter spacing for footnote. | `0em` | same | body-map, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, goal-ring, log-sheet, menu, metric-tile, questionnaire, radio-group, range-bar, range-legend, result-card, scale-input, score-dial, sheet, source-citation, status-pill, stepper, switch, symptom-picker, table, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-text-footnote-weight` | semantic | Font weight for footnote. | `400` | same | body-map, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, dose-tracker, goal-ring, log-sheet, menu, metric-tile, questionnaire, radio-group, range-bar, range-legend, result-card, scale-input, score-dial, sheet, source-citation, status-pill, stepper, switch, symptom-picker, table, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-text-caption1-size` | semantic | Axis labels, legend text, and legal text. | `0.75rem` | same | avatar, badge, consent-sheet, dose-tracker, range-bar, skeleton, tab-bar, visually-hidden |
| `--opsin-text-caption1-leading` | semantic | Line height for caption1, unitless so it scales with the size. | `1.333` | same | avatar, badge, consent-sheet, dose-tracker, range-bar, skeleton, tab-bar, visually-hidden |
| `--opsin-text-caption1-tracking` | semantic | Letter spacing for caption1. | `0.002em` | same | avatar, badge, consent-sheet, dose-tracker, range-bar, skeleton, tab-bar, visually-hidden |
| `--opsin-text-caption1-weight` | semantic | Font weight for caption1. | `400` | same | avatar, badge, consent-sheet, dose-tracker, range-bar, skeleton, tab-bar, visually-hidden |
| `--opsin-text-caption2-size` | semantic | The smallest text the system permits, and only for text that repeats a label already present elsewhere. Never the only place a fact appears. | `0.6875rem` | same | no component |
| `--opsin-text-caption2-leading` | semantic | Line height for caption2, unitless so it scales with the size. | `1.182` | same | no component |
| `--opsin-text-caption2-tracking` | semantic | Letter spacing for caption2. | `0.005em` | same | no component |
| `--opsin-text-caption2-weight` | semantic | Font weight for caption2. | `500` | same | no component |
#### numerals [#numerals]
| Token | Tier | What it controls | Light | Dark | Used by |
| ------------------ | -------- | --------------------------------------------------------------------------------------------------- | -------------- | ---- | ------------ |
| `--opsin-numerals` | semantic | Every component that renders a number sets this, so a changing value does not shift its own layout. | `tabular-nums` | same | no component |
### space [#space]
#### space [#space-1]
| Token | Tier | What it controls | Light | Dark | Used by |
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-space-0` | primitive | 0px. | `0rem` | same | no component |
| `--opsin-space-1` | primitive | Gap between an icon and its label. | `0.25rem` | same | badge, button, callout, care-card, checkbox, combobox, dialog, dose-tracker, empty-state, field, goal-ring, log-sheet, menu, questionnaire, radio-group, range-bar, result-card, scale-input, scroll-area, segmented-control, select, sheet, slider, source-citation, status-pill, stepper, surface, symptom-picker, tab-bar, tabs, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-space-2` | primitive | Gap between tightly related lines. For the separation between two interactive targets see targets.separation, which is the same number and carries its scope. | `0.5rem` | same | alert-banner, avatar, badge, body-map, button, callout, combobox, consent-sheet, dialog, disclaimer-note, dose-tracker, empty-state, field, icon-button, log-sheet, menu, metric-tile, number-field, popover, progress, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, scale-input, score-dial, scroll-area, segmented-control, select, sheet, skeleton, slider, spinner, status-pill, surface, symptom-picker, tab-bar, table, tabs, textarea, timeline-entry, toast, tooltip, trend-sparkline, visually-hidden |
| `--opsin-space-3` | primitive | Inner padding of a compact control. | `0.75rem` | same | accordion, alert-banner, button, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, empty-state, field, log-sheet, menu, metric-tile, popover, progress, radio-group, reading-input, result-card, scroll-area, segmented-control, select, sheet, slider, status-pill, stepper, surface, symptom-picker, tab-bar, table, tabs, textarea, timeline-entry, toast, tooltip, value |
| `--opsin-space-4` | primitive | The default gap between elements inside a card. | `1rem` | same | accordion, avatar, badge, button, callout, care-card, consent-sheet, dialog, divider, empty-state, goal-ring, link, log-sheet, menu, popover, range-legend, result-card, scale-input, score-dial, segmented-control, sheet, surface, switch, tabs, timeline-entry, toast |
| `--opsin-space-5` | primitive | Card inner padding on a phone. | `1.25rem` | same | avatar, button, consent-sheet, dialog, questionnaire, sheet, slider, switch, symptom-picker |
| `--opsin-space-6` | primitive | Card inner padding on a wide screen; gap between cards. | `1.5rem` | same | avatar, card, dialog, disclaimer-note, empty-state, field, log-sheet, skeleton, stepper, switch, tab-bar, trend-sparkline |
| `--opsin-space-8` | primitive | Gap between sections within a screen. | `2rem` | same | avatar, body-map, disclaimer-note, popover, range-bar, reading-input, sheet, stepper |
| `--opsin-space-10` | primitive | Space above a section heading. | `2.5rem` | same | avatar, sheet, skeleton, switch |
| `--opsin-space-12` | primitive | Gap between major regions of a screen. | `3rem` | same | avatar, trend-sparkline |
| `--opsin-space-16` | primitive | Top of a screen below the safe area; the space a consent sheet leaves above its first question. | `4rem` | same | dialog, skeleton |
| `--opsin-space-20` | primitive | Empty-state vertical rhythm. | `5rem` | same | no component |
| `--opsin-space-24` | primitive | The largest step. Beyond this, use a layout, not a gap. | `6rem` | same | no component |
| `--opsin-space-px` | primitive | Hairline borders only. | `0.0625rem` | same | body-map, trend-sparkline |
| `--opsin-space-0-5` | primitive | Optical nudges. Not a layout step. | `0.125rem` | same | badge, dose-tracker, radio-group, range-legend, result-card, scroll-area, segmented-control, status-pill, stepper, switch, tab-bar, tabs, term, trend-sparkline |
#### target [#target]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-target-minimum` | semantic | The opsinjs floor for any interactive control, applied to the hit area rather than to the visible box. | `2.75rem` | same | accordion, body-map, button, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, field, icon-button, menu, metric-tile, number-field, popover, questionnaire, radio-group, reading-input, scale-input, segmented-control, select, sheet, slider, source-citation, switch, symptom-picker, tab-bar, tabs, textarea, toast, tooltip |
| `--opsin-target-comfortable` | semantic | The default for a primary action in the product theme. | `3rem` | same | dialog |
| `--opsin-target-generous` | semantic | A single primary action on a consent, escalation or emergency surface, where a mis-tap has a real cost. | `3.5rem` | same | no component |
| `--opsin-target-separation` | semantic | The minimum gap between two adjacent targets whose visible boxes are smaller than 44px. It is a floor for undersized targets only. Two targets that both meet the 44px minimum are outside its scope, and separating those is governed by the differing cost of a mis-tap between them rather than by a fixed gap. | `0.5rem` | same | alert-banner, card, care-card, consent-sheet, result-card |
#### gutter [#gutter]
| Token | Tier | What it controls | Light | Dark | Used by |
| ----------------------- | -------- | ------------------------------------------------ | ------ | ---- | ------------ |
| `--opsin-gutter-phone` | semantic | The screen gutter in the phone responsive mode. | `16px` | same | no component |
| `--opsin-gutter-tablet` | semantic | The screen gutter in the tablet responsive mode. | `24px` | same | no component |
| `--opsin-gutter-wide` | semantic | The screen gutter in the wide responsive mode. | `32px` | same | no component |
#### measure [#measure]
| Token | Tier | What it controls | Light | Dark | Used by |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------- | ------ | ---- | ---------------------------------------------------------------------- |
| `--opsin-measure-tight` | semantic | A caption or a legend. | `45ch` | same | dialog, empty-state, goal-ring, popover, tooltip |
| `--opsin-measure-comfortable` | semantic | The maximum line length for prose anywhere in the product, including a disclaimer nobody wants to read. | `66ch` | same | disclaimer-note, result-card, score-dial, sheet, source-citation, term |
| `--opsin-measure-wide` | semantic | Code and machine output only. | `80ch` | same | no component |
#### graphic [#graphic]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ---- | --------------------- |
| `--opsin-graphic-dial` | semantic | The maximum width of the ScoreDial arc. This is 16em against the product body of 1.0625rem, which is 17rem and lands on the 4px grid at 68 steps, and it yields to w-full when the column is narrower. | `17rem` | same | goal-ring, score-dial |
#### safe [#safe]
| Token | Tier | What it controls | Light | Dark | Used by |
| --------------------- | -------- | -------------------------------------------------------------------------------------------------- | ---------------------------------- | ---- | ------------- |
| `--opsin-safe-top` | semantic | The top safe-area inset, for installed web apps where browser chrome does not protect the edge. | `env(safe-area-inset-top, 0px)` | same | no component |
| `--opsin-safe-right` | semantic | The right safe-area inset, for installed web apps where browser chrome does not protect the edge. | `env(safe-area-inset-right, 0px)` | same | sheet |
| `--opsin-safe-bottom` | semantic | The bottom safe-area inset, for installed web apps where browser chrome does not protect the edge. | `env(safe-area-inset-bottom, 0px)` | same | dialog, sheet |
| `--opsin-safe-left` | semantic | The left safe-area inset, for installed web apps where browser chrome does not protect the edge. | `env(safe-area-inset-left, 0px)` | same | sheet |
### shape [#shape]
#### radius [#radius]
| Token | Tier | What it controls | Light | Dark | Used by |
| ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--opsin-radius-base` | semantic | The one radius the whole ladder is derived from. | `0.875rem` | same | accordion, alert-banner, button, callout, card, care-card, combobox, empty-state, field, menu, metric-tile, popover, questionnaire, radio-group, range-bar, reading-input, result-card, scale-input, segmented-control, select, skeleton, symptom-picker, tab-bar, textarea, timeline-entry, toast, tooltip, trend-sparkline |
| `--opsin-radius-none` | semantic | Full-bleed media and anything that meets a screen edge. | `0rem` | same | no component |
| `--opsin-radius-xs` | semantic | The floor for a nested corner. Tags inside a chip, a swatch inside a legend. | `0.25rem` | same | popover, range-legend, surface, symptom-picker, term, tooltip |
| `--opsin-radius-sm` | semantic | Inputs and small controls inside a card. | `0.4375rem` | same | card, checkbox, combobox, consent-sheet, dialog, divider, field, log-sheet, menu, number-field, progress, reading-input, scroll-area, select, sheet, skeleton, slider, switch, tab-bar, tabs, toast, visually-hidden |
| `--opsin-radius-md` | semantic | The default. Buttons, chips, cards. | `0.875rem` | same | alert-banner, button, callout, card, combobox, empty-state, menu, metric-tile, popover, questionnaire, radio-group, result-card, scale-input, segmented-control, select, skeleton, symptom-picker, tab-bar, textarea, toast, tooltip |
| `--opsin-radius-lg` | semantic | Cards on a phone, where the card is nearly the width of the screen. | `1.3125rem` | same | accordion, card, combobox, disclaimer-note, menu, popover, result-card, scroll-area, segmented-control, select, surface, toast |
| `--opsin-radius-xl` | semantic | Sheets and dialogs. Applied to the leading edge only when the surface meets a screen edge on the other side. | `1.75rem` | same | alert-banner, dialog, sheet |
| `--opsin-radius-inner-card` | semantic | A box inside a card at the comfortable density (card padding p-5). | `max(calc(0.875rem - 5 * var(--spacing)), var(--opsin-radius-xs))` | same | no component |
| `--opsin-radius-inner-card-compact` | semantic | A box inside a card at the compact density (card padding p-4). | `max(calc(0.875rem - 4 * var(--spacing)), var(--opsin-radius-xs))` | same | no component |
#### corner-shape [#corner-shape]
| Token | Tier | What it controls | Light | Dark | Used by |
| ---------------------- | -------- | ------------------------------------------------------------------------------ | ----------------- | ---- | -------------------------------------------------------------------- |
| `--opsin-corner-shape` | semantic | The squircle curvature. Degrades to `round` where corner-shape is unsupported. | `superellipse(4)` | same | alert-banner, callout, card, dialog, metric-tile, result-card, sheet |
#### border [#border]
| Token | Tier | What it controls | Light | Dark | Used by |
| ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--opsin-border-hairline` | semantic | Every boundary in the system by default. | `1px` | same | button, surface |
| `--opsin-border-emphasis` | semantic | The boundary of a surface carrying `attention` or `urgent`, where the boundary is one of the three non-colour carriers of the status. | `2px` | same | disclaimer-note, field, surface |
| `--opsin-border-focus` | semantic | The focus ring. Always 2px with a 2px offset, always in a colour measured against both the surface and the page behind it, and never removed. See /docs/accessibility/keyboard-and-focus. | `2px` | same | accordion, body-map, button, card, checkbox, combobox, consent-sheet, link, menu, number-field, popover, questionnaire, radio-group, scale-input, scroll-area, segmented-control, select, slider, switch, symptom-picker, tab-bar, table, tabs, term, textarea, toast, tooltip |
| `--opsin-border-focus-offset` | semantic | Offset for the focus boundary. | `2px` | same | accordion, body-map, button, card, checkbox, combobox, consent-sheet, link, menu, number-field, popover, questionnaire, radio-group, scale-input, scroll-area, segmented-control, select, slider, switch, symptom-picker, tab-bar, table, tabs, term, textarea, toast, tooltip |
{/* opsinjs:generated:end */}
---
# Types
Source: https://opsinjs.pensievelabs.org/reference/generated/types
Markdown: https://opsinjs.pensievelabs.org/reference/generated/types.md
Section: Reference · kind: reference · also known as: exported types, TypeScript API, ClinicalStatus, HealthCategory
## How this is generated [#how-this-is-generated]
**Source:** the `export` declarations under `apps/www/lib/`, read from the
TypeScript sources themselves. There is no published `.d.ts` behind this table
and there will not be one: opsinjs is distributed by copying source into your
own repository rather than as an npm package, which is
[decision 0002](../../project/decisions/0002-shadcn-registry-distribution.mdx).
**Script:** `scripts/build-reference.mts`, which scans declaration lines
textually rather than through `fumadocs-typescript`. The reason is the one that
makes the props tables textual too: a reference generator has to run before
anything compiles and produce identical bytes on every machine.
**Command:** `pnpm run generate`.
**Read this table as the site's own vocabulary, not as an installable API.**
Every row below is a symbol of this documentation site. The table covers the
routing helpers, the colour maths, and the catalogue and status models the pages
are rendered from.
That is worth publishing, because the status ladder, the category set and the
colour derivation are defined here and are the same definitions the doctrine
pages describe in prose. It is not, however, anything you can install: an
import from `@opsinjs/…` will not resolve, and what a consumer actually receives
is component source copied in from the registry, whose props are documented on
each component's own page rather than here.
This page is an index. Types and interfaces also get a page of their own under
[API](../api/index.mdx), at `reference/api/`, because a type name in
prose should be a link to a page and not an anchor two thousand words down a
single document. Functions and constants are listed here only, because a page per
`STATUS_META` would be a page of nothing. The index carries the summary; the
symbol page carries the declaration line and the file it was read from.
**Four columns.** *Symbol* is the exported identifier, exactly as it is written
in the source. *Kind* is what the declaration is, so `const`, `function`, `type`
or `interface`. *Declared in* is the file it lives in, relative to `apps/www`.
*Summary* is the first sentence of its doc comment, which means the way to
improve this page is to improve the comment; a row whose summary reads `-` is a
symbol nobody has written one for yet.
**Two symbols are load-bearing** and are worth knowing before you read anything
else. `ClinicalStatus` is the union of the four clinical status levels, and it is
the type that makes the never-mix rule checkable rather than merely written down.
`HealthCategory` is the union of the subject categories. Every component that
takes one takes it as a prop of that type, so a mixed pair is a compile error and
not a design review finding. Both are specified in
[Clinical status semantics](../../health/clinical-status-semantics.mdx) and
[Category identity](../../health/category-identity.mdx); their declarations are
on [ClinicalStatus](../api/ClinicalStatus.mdx) and
[HealthCategory](../api/HealthCategory.mdx).
**What is deliberately excluded.** Everything under `lib/generated/`, because a
generated module is documented by the page that generates it rather than twice
over. Anything whose name begins with an underscore. And everything outside
`lib/`: component props are extracted separately from
`registry/bases/base/*.tsx` and render as the props table on each component
page. A symbol appears under the file whose `export` line declares it, so the
shared substrate a copied component carries with it is listed under
`lib/opsinjs.ts` and the clinical vocabulary it re-exports is listed under
`lib/status.ts`, once each.
{/* opsinjs:generated:begin - everything below is replaced by scripts/build-reference.mts */}
## Exported symbols [#exported-symbols]
| Symbol | Kind | Declared in | Summary |
| --------------------------------------------------------------- | --------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `absoluteUrl` | function | `lib/routes.ts` | Turn a root-relative path into a full URL on the canonical origin. |
| `accessibilityPath` | function | `lib/routes.ts` | - |
| `agentRoutes` | const | `lib/routes.ts` | - |
| `aliasesFor` | function | `lib/catalogue.ts` | The exact array a page's `aliases` frontmatter must contain. |
| `aliasIndex` | function | `lib/catalogue.ts` | alias → the id or page it resolves to. Lower-cased, because a reader typing "Blood Pressure" and a reader typing "blood pressure" want the same page. |
| [`AliasProblem`](../api/AliasProblem.mdx) | interface | `lib/catalogue.ts` | One search synonym that cannot be used, either because two components claim it or because it collides with a component id. |
| `apca` | const | `lib/color/apca.ts` | The short name, for callers that just want the number: `apca("#333", "#fff")`. Identical to `apcaContrast`. |
| `APCA_FLOOR` | const | `lib/color/apca.ts` | The published opsinjs APCA floor. These are the system's own commitments, chosen a little above the bare minimum because the audience is reading a number that matters to them on a phone, outdoors, often over 60. |
| `apcaContrast` | function | `lib/color/apca.ts` | Lc for text on a background. Positive means dark-on-light, negative means light-on-dark, and the two arguments are not interchangeable. |
| `apcaContrastOklch` | function | `lib/color/apca.ts` | The same, taking OKLCH. Both colours are clamped into sRGB first, because that is what a screen shows. |
| `apcaVerdict` | function | `lib/color/apca.ts` | - |
| [`ApcaVerdict`](../api/ApcaVerdict.mdx) | interface | `lib/color/apca.ts` | The result of one APCA measurement, with the signed Lc, its polarity, the floor for that use, and how far above or below the floor it landed. |
| `apiRoutes` | const | `lib/routes.ts` | - |
| `apiSymbolPath` | function | `lib/routes.ts` | A generated per-symbol API page. `\` resolves here, never to an anchor. |
| `axisConflict` | function | `lib/status.ts` | THE NEVER-MIX RULE, as a function. |
| [`AxisConflict`](../api/AxisConflict.mdx) | interface | `lib/status.ts` | A refusal. It is what the system answers when a measurement category and a clinical status are asked to share one surface. |
| `BANNED_WORDS` | const | `lib/opsinjs.ts` | Words a component's copy must not use, with the replacement and the reason. Generated from `tokens/glossary.json`. |
| `baseOptions` | function | `lib/layout.shared.tsx` | THERE IS NO TOP NAVIGATION, AND THAT IS THE POINT. |
| `baseStyleQuery` | function | `lib/routes.ts` | The `?base=&style=` query a docs page uses for the same switch. Returns an empty string at the defaults so the canonical URL stays clean. |
| `builtComponentCount` | function | `lib/registry.ts` | How many distinct components have a real renderable behind them. |
| `byNamespace` | function | `lib/tokens.ts` | - |
| `byTier` | function | `lib/tokens.ts` | - |
| [`CatalogueIndexRow`](../api/CatalogueIndexRow.mdx) | interface | `lib/catalogue.ts` | One component as the light registry index publishes it, flattened to the fields a decision to install actually turns on. |
| `CATEGORY_GATED_SECTIONS` | const | `lib/status.ts` | Sections whose presence depends on the component's category rather than on its status. Required when `category` starts with the prefix, and forbidden when it does not. |
| `CHROMA_ENVELOPE` | const | `lib/color/derive.ts` | Chroma as a fraction of the seed's, per step. Mirrors `chromaEnvelope` in tokens/color.json. |
| `CHROMA_STEP` | const | `lib/preset.ts` | The quantisation of chroma: 0.005 per step, so 63 steps reach 0.315. |
| `clampToGamut` | function | `lib/color/oklch.ts` | Bring a colour into gamut by reducing chroma only. |
| `CLINICAL_STATUS_META` | const | `lib/status.ts` | - |
| `CLINICAL_STATUSES` | const | `lib/status.ts` | - |
| [`ClinicalStatus`](../api/ClinicalStatus.mdx) | type | `lib/status.ts` | What a reading means and what, if anything, to do about it. |
| [`ClinicalStatusMeta`](../api/ClinicalStatusMeta.mdx) | interface | `lib/status.ts` | Everything a clinical status carries besides its colour. The word, the example sentence, who may assign it, its icon and its place in the order. |
| [`ClinicalStatusOrUnknown`](../api/ClinicalStatusOrUnknown.mdx) | type | `lib/status.ts` | The four clinical statuses plus `unknown`. Unknown is the absence of an assertion rather than a fifth level of urgency. |
| `cn` | function | `lib/utils.ts` | Join and de-duplicate class names, resolving Tailwind conflicts last-wins. |
| `COLLAPSE_THRESHOLD` | const | `lib/color/cvd.ts` | The minimum Lc at which two swatches shown side by side are still telling two different colours apart. |
| [`ColorInput`](../api/ColorInput.mdx) | type | `lib/color/apca.ts` | Anything the contrast functions accept: an sRGB triple, an OKLCH object, or a CSS colour string (`#0b6bcb`, `oklch(0.62 0.17 15)`, `rgb(11 107 203)`). |
| `COMPONENT_OPTIONAL_SECTIONS` | const | `lib/status.ts` | The sections a component page may leave out. |
| `COMPONENT_SECTIONS_BY_STATUS` | const | `lib/status.ts` | THE COMPONENT PAGE ANATOMY, status-gated. |
| `componentPath` | function | `lib/routes.ts` | A component specification page, by catalogue id. |
| `componentRequiredSections` | function | `lib/status.ts` | The sections a component page at this status and category must actually carry: the outline above with the optional five taken out. This is what an enforcer reports as missing, and it is deliberately a filter over `componentSections()` rather than a second hand-written table, so the two can never name a heading the other does not. |
| `componentSections` | function | `lib/status.ts` | The full ordered outline for a component page at a given status and category: the canonical order, and the set of headings the page is allowed to carry. Some of what comes back is optional, so do not use this as the missing-heading check. `componentRequiredSections()` is that. |
| `compositeOver` | function | `lib/color/oklch.ts` | Composite a translucent `foreground` at `alpha` over an opaque `backdrop`, source-over, and return the opaque colour a reader actually sees. |
| `CONTRAST_USE_LABELS` | const | `lib/color/apca.ts` | - |
| [`ContrastCheck`](../api/ContrastCheck.mdx) | interface | `lib/color/derive.ts` | One measured pair from a derived theme, carrying both the APCA Lc and the WCAG ratio, the use that fixes the floor, and whether it passed. |
| `contrastRatio` | function | `lib/color/wcag.ts` | The contrast ratio, from 1 (identical) to 21 (black on white). |
| `contrastRatioOklch` | function | `lib/color/wcag.ts` | - |
| [`ContrastUse`](../api/ContrastUse.mdx) | type | `lib/color/apca.ts` | What a pair is being used for. Determines which floor applies. |
| `convertUnit` | function | `lib/opsinjs.ts` | One value in another unit, or `undefined` when this system does not own the factor. |
| `CORNER_SHAPE_VALUES` | const | `lib/preset.ts` | - |
| `CVD_LABELS` | const | `lib/color/cvd.ts` | - |
| `CVD_TYPES` | const | `lib/color/cvd.ts` | - |
| [`CvdCollision`](../api/CvdCollision.mdx) | interface | `lib/color/cvd.ts` | Two colours that stop being distinguishable under one kind of colour vision deficiency, and the Lc left between them once simulated. |
| [`CvdType`](../api/CvdType.mdx) | type | `lib/color/cvd.ts` | The three dichromacies, plus the achromatic check every specimen must also survive. |
| [`DataState`](../api/DataState.mdx) | type | `lib/opsinjs.ts` | The state a data surface is in, named once so the surfaces converge on one vocabulary, though no component accepts it yet. |
| [`DecodeError`](../api/DecodeError.mdx) | type | `lib/preset.ts` | Why a preset code could not be read. The five reasons are kept distinct so that the sentence shown to a person can be a specific one. |
| `decodePreset` | function | `lib/preset.ts` | Decode a preset code. |
| [`DecodeResult`](../api/DecodeResult.mdx) | interface | `lib/preset.ts` | The outcome of reading a preset code. Either the preset and its version, or an error and a sentence a person can act on. |
| `decodeTransfer` | function | `lib/color/oklch.ts` | sRGB gamma-encoded channel → light-linear. Also used for Display-P3, which shares sRGB's transfer curve. |
| `DEFAULT_BASE` | const | `lib/registry.ts` | - |
| `DEFAULT_BASE` | const | `lib/routes.ts` | - |
| `DEFAULT_PRESET` | const | `lib/preset.ts` | - |
| `DEFAULT_STYLE` | const | `lib/registry.ts` | - |
| `DEFAULT_STYLE` | const | `lib/routes.ts` | - |
| `DENSITY_VALUES` | const | `lib/preset.ts` | - |
| `deprecatedTokens` | function | `lib/tokens.ts` | - |
| [`DerivedTheme`](../api/DerivedTheme.mdx) | interface | `lib/color/derive.ts` | Everything derivation produces from one brand colour. Both ramps, every role in light and dark, the contrast checks, the warnings, and whether it is sound. |
| [`DeriveOptions`](../api/DeriveOptions.mdx) | interface | `lib/color/derive.ts` | The knobs on theme derivation. Every one of them has a default, so a brand colour on its own is already a complete input. |
| `deriveRamp` | function | `lib/color/derive.ts` | Build one ramp from a seed, clamping to sRGB and recording the P3 enhancement. |
| `deriveTheme` | function | `lib/color/derive.ts` | Derive a complete, contrast-validated theme from one brand colour. |
| [`DeriveWarning`](../api/DeriveWarning.mdx) | interface | `lib/color/derive.ts` | One thing the derivation has to say about a theme, as a level, a stable code and a message. |
| [`Detent`](../api/Detent.mdx) | type | `lib/opsinjs.ts` | How far a sheet is open. |
| `displayP3ToOklch` | function | `lib/color/oklch.ts` | Gamma-encoded Display-P3, 0 to 1 per channel → OKLCH. |
| `DOCS_BASE` | const | `lib/routes.ts` | The docs base segment. Everything else derives from it. |
| `docsMarkdownPath` | function | `lib/routes.ts` | The same page as processed markdown: `/components/range-bar.md`. |
| `docsPath` | function | `lib/routes.ts` | The URL of a docs page from its slug segments. `docsPath()` is the docs index. This is the function a locale segment would be inserted into. |
| `doctrinePath` | function | `lib/routes.ts` | A doctrine page reference as written in frontmatter, such as `two-colour-axes`, or `foundations/colour/contrast-and-apca` when it is not in Health. |
| `editUrl` | function | `lib/routes.ts` | "Edit this page" on GitHub, from a source file path relative to the content root. |
| `encodePreset` | function | `lib/preset.ts` | Encode a theme as a preset code. |
| `encodeTransfer` | function | `lib/color/oklch.ts` | Light-linear channel → gamma-encoded. |
| `EXAMPLE_SOURCE` | const | `lib/opsinjs.ts` | The only citation an opsinjs example, demo or preview may carry. |
| `explainUnresolved` | function | `lib/registry.ts` | Explain an empty lookup. |
| `findAliasProblems` | function | `lib/catalogue.ts` | The uniqueness check, as a function rather than as a comment. |
| `findByAlias` | function | `lib/catalogue.ts` | - |
| `findCollisions` | function | `lib/color/cvd.ts` | Check that a set of named colours stays mutually distinguishable under every simulation. This is the function that turns "status must survive grayscale" from doctrine into a build step. |
| `findUnit` | function | `lib/opsinjs.ts` | The unit a symbol names, or `undefined` when this system has never heard of it. |
| `fit` | function | `lib/color/derive.ts` | Fit an arbitrary colour into the shipped gamut without moving its hue or lightness. |
| `FLOOR_VALUES` | const | `lib/preset.ts` | Which APCA floor the theme is validated against. `standard` is the published one. |
| `FORMAT_VERSION` | const | `lib/preset.ts` | Bump this, and add a decode branch, whenever a field changes shape. |
| `formatDisplayP3` | function | `lib/color/oklch.ts` | `color(display-p3 0.9 0.2 0.3)`. Channels are clamped, because this is an output format. |
| `formatHex` | function | `lib/color/oklch.ts` | `#rrggbb`, clamped into sRGB. |
| `formatOklch` | function | `lib/color/oklch.ts` | `oklch(0.622 0.17 15)`, or with an alpha, `oklch(0.622 0.17 15 / 0.5)`. |
| `formatRatio` | function | `lib/color/wcag.ts` | Ratios are conventionally quoted to one decimal place and rounded DOWN, so a published figure is never better than the measurement. |
| `formatToken` | function | `lib/tokens.ts` | Render a token in one of the copyable formats. |
| `foundationsPath` | function | `lib/routes.ts` | - |
| [`Gamut`](../api/Gamut.mdx) | type | `lib/color/oklch.ts` | The colour spaces this module can express a colour in. |
| `GAMUT_EPSILON` | const | `lib/color/oklch.ts` | The tolerance a channel may exceed 0 or 1 by and still count as in gamut. Half a unit at 8-bit precision: a value that rounds to a representable channel is in gamut for every purpose this system has. |
| `GAMUT_VALUES` | const | `lib/preset.ts` | - |
| [`GeneratedBannedWord`](../api/GeneratedBannedWord.mdx) | interface | `lib/opsinjs.ts` | One banned word: the word a component's copy must not use, what to write in its place, and why the word is barred. |
| `getAllPages` | function | `lib/source.ts` | Every page in the corpus. The input to the sitemap, llms.txt and the shards. |
| `getByCategory` | function | `lib/catalogue.ts` | - |
| `getByStatus` | function | `lib/catalogue.ts` | - |
| `getCatalogue` | function | `lib/catalogue.ts` | Every row. |
| `getEntry` | function | `lib/catalogue.ts` | - |
| `getPage` | function | `lib/source.ts` | One page by its slug segments, or undefined. |
| `getPagesInSection` | function | `lib/source.ts` | Pages under one top-level section, in tree order. |
| `getPageTree` | function | `lib/source.ts` | The page tree the docs layout renders. |
| `getRegistryEntry` | function | `lib/registry.ts` | Resolve one entry, or null. |
| `getShipped` | function | `lib/catalogue.ts` | Every catalogue row. Not a claim about code. Read `implemented` in `/r/index.json`, or `toIndexRow`'s `isBuilt` argument, for that. |
| `getToken` | function | `lib/tokens.ts` | - |
| `getTokens` | function | `lib/tokens.ts` | - |
| `grouped` | function | `lib/tokens.ts` | Tokens grouped for a table: namespace → group → tokens, in emit order. |
| `groupedByCategory` | function | `lib/catalogue.ts` | Every category that has at least one row, in the declared order, with its rows. |
| `hasComponentPage` | function | `lib/catalogue.ts` | True when `/docs/components/\` resolves to a page. |
| `HEALTH_CATEGORIES` | const | `lib/status.ts` | - |
| `HEALTH_CATEGORY_LABELS` | const | `lib/status.ts` | - |
| [`HealthCategory`](../api/HealthCategory.mdx) | type | `lib/status.ts` | What a reading is ABOUT. Identity, never verdict. |
| `healthPath` | function | `lib/routes.ts` | A health doctrine page, by the page id that `governedBy` and `implements` use. |
| `inGamut` | function | `lib/color/oklch.ts` | - |
| `isBuilt` | function | `lib/registry.ts` | True only when a base component for this name is on disk, which a catalogue row alone is not. A specified-but-unbuilt id answers false, and so does a built id asked for at a base that does not carry it. |
| `isClinicalStatus` | function | `lib/status.ts` | - |
| `isDevelopment` | function | `lib/opsinjs.ts` | True in development, and false wherever that cannot be established. |
| `isHealthCategory` | function | `lib/catalogue.ts` | True for the four `health-*` categories, which carry the extra clinical obligations. |
| `isHealthCategory` | function | `lib/status.ts` | - |
| `isKind` | function | `lib/status.ts` | - |
| `isKnownId` | function | `lib/catalogue.ts` | Does this id exist at all, under any status? |
| `isNotImplemented` | function | `lib/status.ts` | True when a page at this status must carry a not-implemented marker. |
| `isResolvable` | function | `lib/registry.ts` | True when the id exists in the catalogue, whatever its status. Used to decide 404 from "no". |
| `isStatus` | function | `lib/status.ts` | - |
| `issueUrl` | function | `lib/routes.ts` | A prefilled docs issue. Used by `\` when the feedback endpoint is unavailable. |
| `joinPath` | function | `lib/routes.ts` | Join path segments into a root-relative path, dropping empties and stray slashes. |
| [`Kind`](../api/Kind.mdx) | type | `lib/status.ts` | A page's kind. This is a contract, not a label: `kind` fully determines the page's headings, and `assert-ia.mts` fails the build on a missing or an unexpected one. |
| `KINDS` | const | `lib/status.ts` | - |
| `LIGHTNESS_STEP` | const | `lib/preset.ts` | The quantisation of lightness: 1% per step. |
| `linearP3ToLinearSrgb` | function | `lib/color/oklch.ts` | Light-linear Display-P3 → light-linear sRGB, through XYZ (D65). |
| `linearSrgbToLinearP3` | function | `lib/color/oklch.ts` | Light-linear sRGB → light-linear Display-P3, through XYZ (D65). |
| `linearSrgbToOklab` | function | `lib/color/oklch.ts` | Light-linear sRGB → OKLab. Ottosson's forward matrices. |
| `linearToSrgb` | function | `lib/color/oklch.ts` | - |
| `listBases` | function | `lib/registry.ts` | - |
| `listByKind` | function | `lib/registry.ts` | Every entry of a kind, for the examples gallery and the screens index. |
| `listStyles` | function | `lib/registry.ts` | - |
| [`MaterialRung`](../api/MaterialRung.mdx) | type | `lib/opsinjs.ts` | A rung of the material ladder, meaning how a surface sits above what is behind it. |
| `maxChroma` | function | `lib/color/oklch.ts` | The largest chroma that is still inside `gamut` at this lightness and hue. |
| `minimumFontSize` | function | `lib/color/apca.ts` | The smallest font size, in px, at which this Lc is usable at the given weight, from a coarse reading of the APCA font-size guidance rather than from the full lookup table. |
| `mixOklch` | function | `lib/color/oklch.ts` | Mix two OKLCH colours. Hue takes the shorter way round the circle, which is what CSS `color-mix` does and what anyone reading the result expects. |
| `MODE_VALUES` | const | `lib/preset.ts` | The colour mode a theme opens in. `system` follows the reader's own setting. |
| `NEUTRAL_CHROMA_VALUES` | const | `lib/preset.ts` | Chroma of the neutral ramp. Index 0 is a dead grey; the default is index 2. |
| `ogUrl` | function | `lib/routes.ts` | The OG image service. One endpoint for the whole site. There is no per-route opengraph-image. |
| [`Oklab`](../api/Oklab.mdx) | interface | `lib/color/oklch.ts` | A colour in OKLab. |
| `oklabToLinearSrgb` | function | `lib/color/oklch.ts` | OKLab → light-linear sRGB. Ottosson's inverse matrices. |
| `oklabToOklch` | function | `lib/color/oklch.ts` | - |
| [`Oklch`](../api/Oklch.mdx) | interface | `lib/color/oklch.ts` | A colour in OKLCH. `l` 0 to 1, `c` unbounded, `h` in degrees. |
| `oklchToDisplayP3` | function | `lib/color/oklch.ts` | OKLCH → gamma-encoded Display-P3, 0 to 1 per channel. Not clamped. |
| `oklchToOklab` | function | `lib/color/oklch.ts` | - |
| `oklchToRgb255` | function | `lib/color/oklch.ts` | OKLCH → 0 to 255 sRGB integers, clamped. The input for both contrast models. |
| `oklchToSrgb` | function | `lib/color/oklch.ts` | OKLCH → gamma-encoded sRGB, 0 to 1 per channel. Not clamped. |
| `OPSIN_ERRORS` | const | `lib/opsinjs.ts` | The table `warnOnce()` reads. Generated from `tokens/errors.json`. |
| [`OpsinError`](../api/OpsinError.mdx) | interface | `lib/opsinjs.ts` | One warning code: what it is called, how severe it is, and the page that prevents it. |
| [`OpsinErrorCode`](../api/OpsinErrorCode.mdx) | type | `lib/opsinjs.ts` | Every warning code opsinjs can emit. |
| [`OpsinErrorParams`](../api/OpsinErrorParams.mdx) | type | `lib/opsinjs.ts` | The values a message template asks for, keyed by placeholder name. |
| [`OpsinErrorSeverity`](../api/OpsinErrorSeverity.mdx) | type | `lib/opsinjs.ts` | How severe a warning is. |
| `ORPHAN_ALLOWLIST` | const | `lib/routes.ts` | Routes that exist but are not reachable from the top nav or the sidebar, and are allowed not to be. |
| `OUTLINE_IS_EXACT` | const | `lib/status.ts` | `kind: project` is free-form and `kind: guide` has task sections between its fixed first and last headings, so for these two the outline is a REQUIRED SUBSET rather than the complete list. Everything else is exact. |
| `P3_CHROMA_GAIN` | const | `lib/color/derive.ts` | How much more chroma Display-P3 is allowed to spend than sRGB at the same step. |
| `parseColor` | function | `lib/color/oklch.ts` | Parse the colour syntaxes a person is likely to paste into the theme playground: `oklch(...)` with or without percentages, `#rrggbb`, and `rgb(r g b)` or `rgb(r, g, b)`. Anything else returns null rather than guessing. A theme derived from a misparsed brand colour is worse than a refusal to derive one. |
| `parseHex` | function | `lib/color/oklch.ts` | `#rgb`, `#rrggbb` or `#rrggbbaa` → sRGB 0 to 1. Returns null on anything else. |
| `passesApca` | function | `lib/color/apca.ts` | - |
| [`Preset`](../api/Preset.mdx) | interface | `lib/preset.ts` | A theme as a person describes it. What `encodePreset` takes and `decodePreset` returns. |
| `PRESET_PREFIX` | const | `lib/preset.ts` | - |
| `presetBrandColor` | function | `lib/preset.ts` | The brand colour a preset describes, as an `oklch()` string the colour engine can parse. |
| `RADIUS_VALUES` | const | `lib/preset.ts` | `--opsin-radius-base` in px. Index 3 (14px) is the product default. |
| [`Ramp`](../api/Ramp.mdx) | type | `lib/color/derive.ts` | One complete lightness ramp, as a value per step. The chromatic ramp and the neutral ramp of a derived theme are both this shape. |
| `RAMP_LIGHTNESS` | const | `lib/color/derive.ts` | - |
| `RAMP_STEPS` | const | `lib/color/derive.ts` | The ramp steps, and the lightness of each. Mirrors `ladder` in tokens/color.json. |
| [`RampStep`](../api/RampStep.mdx) | type | `lib/color/derive.ts` | One rung of the lightness ladder, from 50 at the lightest to 950 at the darkest. The rungs are fixed, so two themes are comparable step by step. |
| [`RampStepValue`](../api/RampStepValue.mdx) | interface | `lib/color/derive.ts` | One rung of a ramp, as the sRGB value that ships, the wide-gamut value where P3 has headroom, and whether sRGB had to clamp the chroma away. |
| `referencePath` | function | `lib/routes.ts` | - |
| [`ReferenceRange`](../api/ReferenceRange.mdx) | interface | `lib/opsinjs.ts` | The interval a reading is being compared against, and who says so. |
| `REFUSED_CONVERSIONS` | const | `lib/opsinjs.ts` | Conversions this system refuses to publish, and why. Generated from `tokens/units.json`. |
| [`RefusedConversion`](../api/RefusedConversion.mdx) | interface | `lib/opsinjs.ts` | A pair people expect to be arithmetic and is not, with the reason. |
| `registryInstallUrl` | function | `lib/routes.ts` | What a consumer types to install an item. Rendered by `\`. |
| [`RegistryLookup`](../api/RegistryLookup.mdx) | type | `lib/registry.ts` | Both call shapes are supported on purpose. |
| `registryMeta` | function | `lib/registry.ts` | Provenance for the registry surfaces: when it was generated and from what. `generatedAt: null` means the placeholder is still in place and `pnpm run generate` has not run. The registry pages say that out loud rather than rendering an empty table. |
| [`RegistryQuery`](../api/RegistryQuery.mdx) | interface | `lib/registry.ts` | A request for one registry item, by id and optionally by base, style and kind. Anything left out falls back to the default. |
| `registryRoutes` | const | `lib/routes.ts` | - |
| `relativeLuminance` | function | `lib/color/wcag.ts` | WCAG 2.2 relative luminance, 0 for black and 1 for white. |
| `REVIEW_FLOOR_NOTICE` | const | `lib/status.ts` | THE REVIEW FLOOR, AS ONE STRING. |
| [`Rgb`](../api/Rgb.mdx) | interface | `lib/color/oklch.ts` | Red, green and blue, each 0 to 1, in whichever space the function says. |
| [`Rgb255`](../api/Rgb255.mdx) | type | `lib/color/apca.ts` | A colour as 0 to 255 sRGB integers, which is what APCA is defined over. |
| [`RoleName`](../api/RoleName.mdx) | type | `lib/color/derive.ts` | The roles a derived theme guarantees. Components consume these and never a step. |
| [`RolePair`](../api/RolePair.mdx) | interface | `lib/color/derive.ts` | One semantic role in both colour modes. Roles are derived as a pair, because a role that exists only in light mode is a role that breaks in dark. |
| [`RoleValue`](../api/RoleValue.mdx) | interface | `lib/color/derive.ts` | What a semantic role resolves to in one colour mode. The ramp step it came from, the OKLCH value, and the CSS it is written as. |
| `routes` | const | `lib/routes.ts` | - |
| `screenLuminance` | function | `lib/color/apca.ts` | Screen luminance Y for an sRGB triple, using APCA's own simple-exponent transfer rather than the piecewise sRGB curve. This is deliberate and is one of the places APCA and WCAG genuinely differ: APCA models the display, not the encoding. |
| `searchCatalogue` | function | `lib/catalogue.ts` | The filter behind `\`: matches an id, a name, the description or any alias. Substring matching, because the reader typing "range" wants both RangeBar and the reference-range guidance and has no reason to know which word the system chose. |
| `searchTokens` | function | `lib/tokens.ts` | Substring match over name, description and value. The `/tokens` browser's filter. |
| `SECTION_OUTLINES` | const | `lib/status.ts` | The H2s a page must have, by `kind`. This is the outline `content/_templates/*.mdx` implements, `\` asserts and `assert-ia.mts` checks; all three read it from here so they cannot drift. |
| `SHIPPED_PRESETS` | const | `lib/preset.ts` | The shipped presets. Names, not codes, are what the documentation refers to; the code is what a reader copies. `build-tokens.mts` materialises each into `/r/themes/\.json`. |
| `sidebarTree` | const | `lib/sidebar-tree.ts` | DELIBERATELY NOT ANNOTATED as `PageTreeTransformer`. That type is generic over the content storage, and naming it here pins the generic to the default `PageData` rather than to the docs collection. `loader()` then infers its own output from the pinned transformer, and every caller of `page.data.status` elsewhere in the app stops compiling. Leaving the object structural lets the loader infer it at the call site, which is what the generic is for. |
| `simulate` | function | `lib/color/cvd.ts` | Simulate a dichromacy. `severity` 0 returns the input unchanged and 1 returns full dichromacy; values between interpolate in linear light. |
| `simulateOklch` | function | `lib/color/cvd.ts` | - |
| `simulateRgb255` | function | `lib/color/cvd.ts` | - |
| `site` | const | `lib/routes.ts` | - |
| `source` | const | `lib/source.ts` | - |
| `spokenUnit` | function | `lib/opsinjs.ts` | How a screen reader should say this unit for this count. |
| `srgbToLinear` | function | `lib/color/oklch.ts` | - |
| `srgbToOklch` | function | `lib/color/oklch.ts` | Gamma-encoded sRGB, 0 to 1 per channel → OKLCH. |
| [`Status`](../api/Status.mdx) | type | `lib/status.ts` | The release phase of a component page or a catalogue entry. |
| `STATUS_META` | const | `lib/status.ts` | - |
| `STATUS_ORDER` | const | `lib/status.ts` | Statuses in display order. |
| `statusCounts` | function | `lib/catalogue.ts` | Counts per status, in display order. The input to `\`. |
| `STATUSES` | const | `lib/status.ts` | A RELEASE PHASE IS NEVER PAINTED, AND THERE IS NOW NOTHING HERE TO PAINT IT WITH. |
| [`StatusMeta`](../api/StatusMeta.mdx) | interface | `lib/status.ts` | What a release phase shows on a badge. A label and one sentence a reader can act on, which is also the chip's title and its accessible name. |
| `themeToCssVariables` | function | `lib/color/derive.ts` | Every custom property a derived theme emits, ready for a stylesheet or a copy button. |
| `toGrayscale` | function | `lib/color/cvd.ts` | Grayscale by relative luminance rather than by an average of the channels. |
| `toIndexRow` | function | `lib/catalogue.ts` | One row of `/r/index.json` and of the generated catalogue reference page. |
| `TOKEN_FORMAT_LABELS` | const | `lib/tokens.ts` | - |
| `TOKEN_NAMESPACE_LABELS` | const | `lib/tokens.ts` | - |
| `TOKEN_NAMESPACES` | const | `lib/tokens.ts` | - |
| `TOKEN_TIER_DESCRIPTIONS` | const | `lib/tokens.ts` | - |
| `TOKEN_TIER_LABELS` | const | `lib/tokens.ts` | - |
| [`TokenFormat`](../api/TokenFormat.mdx) | type | `lib/tokens.ts` | The formats the `/tokens` and `/colors` browsers can copy a value in. |
| `tokenMeta` | function | `lib/tokens.ts` | - |
| `tokensAreGenerated` | function | `lib/tokens.ts` | Whether the generated layer has actually been produced. |
| `tokensUsedBy` | function | `lib/tokens.ts` | Every token one component consumes. The query behind section 17 of a component page, and the reason `usedBy` is populated at generation time rather than inferred at render time. |
| `toRgb255` | function | `lib/color/oklch.ts` | 0 to 1 sRGB → 0 to 255 integers, clamped. |
| `toRgb255Input` | function | `lib/color/apca.ts` | Coerce any accepted colour form to an sRGB triple. |
| [`TrendPoint`](../api/TrendPoint.mdx) | interface | `lib/opsinjs.ts` | One reading in a series, for the components that draw change over time. |
| [`Unit`](../api/Unit.mdx) | interface | `lib/opsinjs.ts` | One unit: what it is called, what a reader sees, and how it is SPOKEN. |
| `UNIT_CONVERSIONS` | const | `lib/opsinjs.ts` | Every convertible ordered pair, composed by the generator. Never authored by hand. |
| `unitConversion` | function | `lib/opsinjs.ts` | The authored conversion between two symbols, or `undefined` when there is none. |
| [`UnitConversion`](../api/UnitConversion.mdx) | interface | `lib/opsinjs.ts` | An exact conversion between two units of the same kind. |
| `UNITS` | const | `lib/opsinjs.ts` | Every unit this system can speak, sorted by id. Generated from `tokens/units.json`. |
| `UNITS_BY_SYMBOL` | const | `lib/opsinjs.ts` | The lookup `findUnit()` reads, derived from `UNITS` rather than emitted twice. |
| `unknownImplementsIds` | function | `lib/catalogue.ts` | Validate a doctrine page's `implements` list. Every entry must be a real catalogue id. This is the check that would have caught `implements: [alert-banner, care-card, toast]` before `toast` was on the roster. |
| [`UnknownStatus`](../api/UnknownStatus.mdx) | type | `lib/status.ts` | The absence of an assertion, which covers a reading never taken, a stale reading, or a reading whose reference range the product does not own. |
| [`UnresolvedReason`](../api/UnresolvedReason.mdx) | interface | `lib/registry.ts` | Why a lookup came back empty. The input to `\`. |
| [`ViewKind`](../api/ViewKind.mdx) | type | `lib/routes.ts` | What a `/view` route can render. |
| [`ViewParams`](../api/ViewParams.mdx) | interface | `lib/routes.ts` | Everything a chrome-less preview URL can carry. Name and kind become path segments, and the rest become query parameters applied before first paint. |
| `viewPath` | function | `lib/routes.ts` | `/view/base/base-lyra/component/range-bar?mode=dark&text=200`. |
| [`WarningLevel`](../api/WarningLevel.mdx) | type | `lib/color/derive.ts` | How serious a derivation warning is. An `error` makes the theme unsound and stops it shipping, while a `warning` and a `note` are advisory. |
| `warnOnce` | function | `lib/opsinjs.ts` | Report a defect in development, once, and never do anything else. |
| `wcag` | const | `lib/color/wcag.ts` | The short name, matching `apca()`. Identical to `contrastRatio`. |
| `WCAG_FLOOR` | const | `lib/color/wcag.ts` | The three WCAG 2.2 thresholds this system checks against. |
| [`WcagUse`](../api/WcagUse.mdx) | type | `lib/color/wcag.ts` | Which WCAG 2.2 contrast threshold applies. Body text, large text and non- text content each have a different floor. |
| `wcagVerdict` | function | `lib/color/wcag.ts` | - |
| [`WcagVerdict`](../api/WcagVerdict.mdx) | interface | `lib/color/wcag.ts` | The result of one WCAG 2.2 contrast measurement, with the ratio, the floor for that use, and the success criterion it is measured against. |
{/* opsinjs:generated:end */}
---
# System glossary
Source: https://opsinjs.pensievelabs.org/reference/system-glossary
Markdown: https://opsinjs.pensievelabs.org/reference/system-glossary.md
Section: Reference · kind: reference · also known as: rung, base vs style, what is a ladder, system vocabulary
This is the vocabulary of the **system**, not of health. If you are looking for
*HbA1c*, *reference range* or *systolic*, you want the
[Plain-English A to Z](../content/plain-english-a-z.mdx), which is written for
readers of your app rather than builders of it. The two glossaries never define
the same word; where a word genuinely has two meanings, both are listed here
with the disambiguation spelled out. The one that bites is `status`.
## How this is generated [#how-this-is-generated]
It is not, and this is the only page in Reference that is hand-written on
purpose. A project's terms of art have no machine-readable source: they exist in
prose, and a generator that scraped them from headings would produce a list of
headings, not a glossary. Inventing a `tokens/system-glossary.json` so that this
page could claim to be generated would add a file nobody maintains and a
provenance claim that means nothing.
What *is* enforced is the other half of the rule: every term below names the page
that owns its definition, and `assert-ia.mts` treats a term whose owning page has
disappeared as a broken cross-reference. A definition here may summarise; it may
never contradict.
| Term | What it means | Owned by |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| **alias** | A search synonym declared in a page's frontmatter. Aliases are indexed, emitted into `llms.txt` and into `/r/index.json`, and are globally unique across the corpus. | [Catalogue](./generated/catalogue.mdx) |
| **axis** | One of the two independent colour dimensions. The **status axis** carries clinical urgency; the **category axis** carries subject identity. They never combine on one element. | [The two colour axes](../health/two-colour-axes.mdx) |
| **base** | A behaviour implementation of a component. It is the React code. `base` is the only base that exists; the segment is in the URL of every machine surface so that adding a second one is additive rather than a migration. | [Namespaces](../registry/namespaces.mdx) |
| **catalogue** | The single declared list of every component id, with its category, status and aliases. `registry/catalogue.ts` is its only definition. | [Catalogue](./generated/catalogue.mdx) |
| **category** | A subject area a value belongs to. This is the identity axis. A category colour says what a number is about; it never says how worried to be. | [Category identity](../health/category-identity.mdx) |
| **chrome** | The documentation site's own interface: sidebar, header, search. Themed with lyra, deliberately unlike the product theme. | [Lyra and the docs chrome](../theming/lyra-and-the-docs-chrome.mdx) |
| **doctrine** | A rule page that constrains components rather than describing one. The doctrine pages are everything under Health, Accessibility and Content & language. Doctrine names the components it governs; components name the doctrine that governs them. | [Health](../health/index.mdx) |
| **foundation** | A cross-cutting system layer with tokens of its own: colour, materials, motion, typography, shape, space, and the rest. | [Foundations](../foundations/index.mdx) |
| **generated marker** | The comment pair `opsinjs:generated:begin` / `opsinjs:generated:end`. Content between them is written by a script and is erased on the next run. | [Reference](./index.mdx) |
| **ladder** | An ordered set of surface treatments where position carries meaning. These are the six material rungs. Distinct from a *scale*, whose steps are sizes rather than meanings. | [The ladder](../foundations/materials/the-ladder.mdx) |
| **namespace** | The `@opsinjs` prefix a consumer registers in `components.json` so that `npx shadcn add @opsinjs/result-card` resolves here. | [Namespaces](../registry/namespaces.mdx) |
| **pillar** | A top-level group in the sidebar. Sixteen exist; a page belongs to exactly one. | [Reading these docs](../start/reading-these-docs.mdx) |
| **preset** | A complete theme expressed as values rather than code. Those values are the token overrides that turn the system a particular colour. | [Preset codes](../registry/preset-codes.mdx) |
| **preset code** | The short opaque string, `opsinjs-*`, that encodes a preset for a URL or a CLI flag. Decodable, versioned, and not a secret. | [Preset codes](../registry/preset-codes.mdx) |
| **product theme** | The theme opsinjs components render under: squircle corners, `system-ui`, generous spacing. Every `/view` route and every component preview uses it. The docs chrome does not. | [Lyra and the docs chrome](../theming/lyra-and-the-docs-chrome.mdx) |
| **ramp** | A perceptually even series of steps generated from one colour in OKLCH, clamped for gamut and checked for contrast. Each axis colour has one. | [How the engine works](../foundations/colour/how-the-engine-works.mdx) |
| **recipe** | A task-shaped page: components plus tokens plus copy rules for one job, in one scroll. | [Recipes](../recipes/index.mdx) |
| **registry item** | The JSON document describing one distributable unit in the shadcn registry schema. It covers the unit's files, dependencies and target paths. Served from `/r`. | [registry-item.json](../registry/registry-item-json.mdx) |
| **role** | A semantic colour slot filled by a ramp step. The four roles are surface, line, ink and accent. Components consume roles; they never consume ramp steps directly. | [Colour roles](../foundations/colour/colour-roles.mdx) |
| **rung** | One step of the material ladder, identified by its position in the stacking order rather than by a blur radius. | [The ladder](../foundations/materials/the-ladder.mdx) |
| **scale** | An ordered set of sizes named by intent rather than by t-shirt size. The scales are type, space and radius. | [Token architecture](../foundations/token-architecture.mdx) |
| **screen** | A whole-screen specimen: several components under one layout, used to show what the parts do together. | [Screens](../screens/index.mdx) |
| **status (clinical)** | One of the four ordinal levels describing a value's relationship to what is expected for a person. The four are steady, watch, attention and urgent. | [Clinical status semantics](../health/clinical-status-semantics.mdx) |
| **status (release)** | One of the three phases describing how much of a promise a component is making. The three are planned, shipped and deprecated, and only a component page carries one. | [Release phases](../project/release-phases.mdx) |
| **style** | A visual variant applied as CSS only, never as behaviour. `base-lyra` is the default. Styles are addressable in `/view` and `/r` URLs; documentation pages have exactly one canonical URL regardless of style. | [Namespaces](../registry/namespaces.mdx) |
| **tier** | Which of the three levels a token sits at: primitive (a raw value), semantic (a role), or component (a part). Components read semantic and component tiers only. | [Token architecture](../foundations/token-architecture.mdx) |
| **twin** | The `.md` version of any documentation page, served by appending `.md` to its URL. Processed markdown, not raw MDX. | [Raw markdown API](../agents/raw-markdown-api.mdx) |