---
title: "Component checklist"
description: "The definition of done across design, code, tokens, tests, accessibility, documentation and safety. The items that are actually enforced are marked as such."
url: "https://opsinjs.pensievelabs.org/handbook/contributing/component-checklist"
source: "https://opsinjs.pensievelabs.org/handbook/contributing/component-checklist.md"
section: "Handbook"
kind: "handbook"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["definition of done", "component checklist", "ship criteria", "release checklist"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="handbook" />

## The short version [#the-short-version]

A component is done when it can be used by somebody who has not met the team.
That decomposes into seven groups. Items marked **enforced** fail the build or
the CI check; the rest are review items, and the distinction is deliberate. A
checklist where everything is equally important is a checklist nobody reads.

## How it works [#how-it-works]

### Design [#design]

* The plain-English definition is written for a patient, not an engineer.
* Every state is designed: default, hover, press, focus-visible, disabled,
  loading, selected.
* Every one of the five [data states](../../foundations/data-states.mdx) has a
  designed answer. They are empty, loading, error, stale and partial.
* The component works at the narrowest supported width and at the widest without
  a special case. See [Responsive
  modes](../../foundations/space/responsive-modes.mdx).
* It reads correctly in both themes and under increased contrast.

### Code [#code]

* **Enforced:** a named exported props interface, `<Component>Props`. Without it
  the API table generates empty. See [TypeScript](../typescript.mdx).
* **Enforced:** no raw colour values anywhere in the source.
* Every public prop has a JSDoc line.
* `render` is supported on every part that renders an element.
* `ref` is forwarded on every part.
* `className` is merged with `cn()`, caller last.
* Client boundary is on the leaf, not the module.

### Tokens [#tokens]

* **Enforced:** every value comes from a token; no literals.
* The component reads semantic tokens, never primitives. See [Token
  architecture](../../foundations/token-architecture.mdx).
* **Enforced:** no token crosses the axes. A status token in a category role
  fails.
* Any new component-level CSS variable is documented in the component's CSS
  variables table.

### Tests [#tests]

* Roles and accessible names asserted, not class names.
* Keyboard behaviour asserted against the documented contract.
* Focus position asserted after every interaction that moves it.
* The five data states rendered and asserted.
* No DOM snapshots. See [Testing](../testing.mdx).

### Accessibility [#accessibility]

* **Enforced:** every token pair used by the component passes the published
  contrast floor in both themes, measured by `check-contrast.mts`.
* **Enforced:** status is distinguishable in grayscale. The CVD audit runs in
  CI.
* Keyboard contract documented in the component's `KeyboardTable`.
* Screen-reader announcement contract written down and tested on at least one
  real combination.
* Touch targets meet the 44pt floor. See [Density and touch
  targets](../../foundations/space/density-and-touch.mdx).
* Readable and operable at 200% text.
* Every motion has a defined `prefers-reduced-motion` fallback that is observable.
* `reviewed` and `reviewer: design` set in frontmatter, on the day somebody ran
  the checks above rather than on the day the page was edited.

### Documentation [#documentation]

* **Enforced:** every section required at the declared status is present.
  `PageTemplate` fails the build otherwise.
* **Enforced:** the body is inside the word budget. `assert-ia` rule LEAN001 fails
  the build past a thousand words, or twelve hundred and fifty on a `health-*`
  page.
* **Enforced:** the "when not to use" list names an alternative for every entry.
* **Enforced:** `governedBy` is present when the category starts with `health-`,
  and the named doctrine pages list this component in `implements`.
* Content guidelines written, with one approved and one rejected phrasing, where
  the component renders copy the product writes.
* An `Anatomy` section for any component with two or more parts.
* Aliases added to `registry/catalogue.ts`.

### Safety [#safety]

* **Enforced for `health-*` categories:** the Clinical meaning section exists and
  states what the component asserts, what it must never be read as, which axis
  it may use, and who owns the thresholds.
* The component cannot be made to state something it does not know: no
  interpolation, no carried-forward values, no zero standing in for absence.
* A clinical reviewer has signed the page, with a `reviewed` date.
* Every claim is either cited or marked `evidence: opinion`.

## Do this [#do-this]

* **Work through the list before opening the pull request**, and paste it into
  the description with the boxes ticked. It is faster than three review rounds.
* **Do the accessibility items first**, not last. Retrofitting a keyboard
  contract is a rewrite; designing to one is an afternoon.
* **Regenerate and commit** the generated artefacts in the same change.
* **Ask for the clinical review early** if the component is `health-*`. It is
  the longest lead time on the list.

## Not this [#not-this]

* **Do not mark an item done because it is "handled upstream".**
  Accessibility is mandatory on every page and is never delegated to Base UI.
  Base UI gives you a correct primitive, not a correct component.
* **Do not ship a measured number you typed.** Every figure is generated; a
  hand-typed contrast ratio is wrong the moment a token moves.
* **Do not promote to `shipped` with a `Todo` on the page.** `Todo` is counted in
  the coverage report precisely so that this is visible.
* **Do not skip the empty and stale states** because "the product always has
  data". It does not, at exactly the moment it matters.

## Gotchas [#gotchas]

* **The enforced items fail at different points.** Some at build (`PageTemplate`,
  `assert-ia`), some in CI (`check-contrast`, the CVD audit), some at lint. A
  green local build is not the whole list.
* **`implements` and `governedBy` are checked in both directions**, so adding a
  component to a doctrine page's list without updating the component page fails.
* **The contrast check covers token pairs, not rendered pixels.** A component
  whose text sits on a translucent material needs the scrim rules too; see
  [The contrast floor](../../foundations/materials/the-contrast-floor.mdx).
* **A `reviewed` date expiring does not fail the build**, it appears in the
  freshness report. That means it is easy to let slide. See [Docs
  freshness](../../project/docs-freshness.mdx).

## Related [#related]

* [Proposing a component](./proposing-a-component.mdx) is the stage before this
  one.
* [Anatomy of a component page](../../components/anatomy-of-a-component-page.mdx)
  covers the lean outline and what each status requires.
* [Release phases](../../project/release-phases.mdx) says what each status
  promises.
* [Safety review checklist](../../health/safety-review-checklist.mdx) is the
  pre-ship check for any screen showing a health value.
