---
title: "Introduction"
description: "opsinjs is a React design system for consumer health apps. It is the components that show a person their own numbers, and the rules that keep them honest."
url: "https://opsinjs.pensievelabs.org/"
source: "https://opsinjs.pensievelabs.org/.md"
section: "Introduction"
kind: "guide"
reviewed: "2026-09-20"
reviewer: "content"
aliases: ["what is opsinjs", "introduction to opsinjs", "opsinjs overview"]
---

> 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="guide" />

## Overview [#overview]

**opsinjs is a React design system for consumer health apps. It is the
components that show a person their own numbers, and the rules that keep those
numbers honest.**

It is built for the reader who is not a clinician: someone opening an app to see a
blood-pressure reading, a sleep score, a lab result, a step count. That reader is
often anxious, often on a phone, often reading a number they have never seen
before. The interface is the whole of their explanation. Most component libraries
treat that as a rendering problem. opsinjs treats it as a communication problem
with a rendering component attached.

Three things follow from that, and they are what makes this system different from
a general-purpose UI kit:

* **Colour carries two independent meanings, and mixing them is a bug.** A
  category colour says *which part of your health this is*. A status colour says
  *how concerning this is*. A system that lets the same green mean "sleep" on one
  card and "you are fine" on the next has made a reading error unavoidable. See
  [The two colour axes](./health/two-colour-axes.mdx).
* **The doctrine is testable.** The rules in [Health](./health/index.mdx) are
  written as prohibitions a reviewer can check on a screenshot rather than as
  principles. "Never show a bare percentage change." "At most one urgent surface
  per screen." "Do not print the word *normal* next to a person's result."
* **Numbers in these docs are generated, never remembered.** Contrast ratios,
  token values, prop tables and bundle sizes come out of the source and fail the
  build when they drift. See [Reading these docs](./start/reading-these-docs.mdx).

### Built rather than unbuilt, and this page will not overstate it either [#built-rather-than-unbuilt-and-this-page-will-not-overstate-it-either]

There is no npm package of components, and there is not meant to be: distribution
is copy-in through a shadcn registry, decided in
[ADR 0002](./project/decisions/0002-shadcn-registry-distribution.mdx). There *is*
a registry. Sixty components are implemented and served from `/r/<name>.json`,
and `npx shadcn add @opsinjs/<name>` writes their source into a shadcn-initialised
project and compiles. Every catalogue row is implemented and installable, and
every one of them is `shipped`: the API may change in any release, and each
change carries a changelog entry. No opsinjs component has had an accessibility
review or a clinical review. `shipped` means the source installs, and it does not
mean either review has happened. Nothing here is for a production health
surface.

The **specification** is the part that is hardest to retrofit: every component
page states what the component asserts about a person's health, what it must
never be read as, which colour axis it may use, who owns its thresholds, and the
accessibility bar it has to clear.

If you are an agent: read `status` on the page, or `implemented` on the row in
`/r/index.json`, and do not generalise from one page to the next. `shipped`
means you may generate against the published API, provided you say it may move.
`planned` is a definitive negative answer, not a gap. Do not
generate an implementation from a proposed API. See
[Reading these docs](./start/reading-these-docs.mdx#for-agents).

## What opsinjs decides for you [#what-opsinjs-decides-for-you]

A design system earns its keep by removing decisions. These are the ones opsinjs
takes off your team's plate, permanently and identically across every surface:

| Decision                                                            | Where it is fixed                                                    |
| ------------------------------------------------------------------- | -------------------------------------------------------------------- |
| What a colour is allowed to mean                                    | [Two colour axes](./health/two-colour-axes.mdx)                      |
| The four clinical status levels and their words                     | [Clinical status semantics](./health/clinical-status-semantics.mdx)  |
| How a number, a unit and a precision are rendered                   | [Numbers, units and precision](./health/numbers-units-precision.mdx) |
| Which unit system a reader sees, and how switching it stays correct | [Unit systems](./health/unit-systems.mdx)                            |
| How many alarming surfaces one screen may carry                     | [Alarm fatigue](./health/alarm-fatigue.mdx)                          |
| The contrast floor every token pair must clear                      | [The contrast floor](./foundations/materials/the-contrast-floor.mdx) |
| What a surface may be made of, and what it must stay readable over  | [Materials](./foundations/materials/index.mdx)                       |
| What motion is allowed to communicate, and what it degrades to      | [Motion in health UI](./health/motion-in-health-ui.mdx)              |
| Which words are banned from patient-facing copy                     | [Content and language](./content/index.mdx)                          |

## What it refuses to decide [#what-it-refuses-to-decide]

This list matters more than the one above, because a design system that is vague
about its boundary invites teams to assume it is doing clinical work it is not.

* **Thresholds and reference ranges.** opsinjs will render a value against a
  range. It will never supply the range. Ranges depend on assay, laboratory,
  population, age, sex and context, and they are a clinical decision belonging to
  your product's clinical owner.
* **Triage and diagnosis.** No component decides that a reading is dangerous. Your
  product decides; a component displays the decision your product passed it.
* **Clinical wording.** opsinjs supplies the shape of a sentence and the words it
  bans. It does not supply the sentence that tells someone what their result
  means.
* **Data.** No fetching, no caching, no schema, no units conversion service. The
  formatting contract is ours; the data layer is yours.
* **Localisation.** There is no locale segment yet, and the reasons and the exact
  retrofit are recorded in
  [ADR 0005](./project/decisions/0005-no-lang-segment-yet.mdx). Unit systems are
  handled as a *correctness* surface instead, because millimoles and milligrams
  per decilitre are not a translation problem.
* **Regulatory status.** opsinjs is not a medical device and confers nothing on
  the product that uses it. This is important enough to have its own page:
  [Safety, scope and limitations](./start/safety-scope-and-limitations.mdx).

## Who it is for [#who-it-is-for]

opsinjs is aimed at product teams building **consumer or patient-facing** health
experiences: wellness and fitness apps, remote-monitoring companion apps, patient
portals, at-home test result readouts, care-plan and medication apps, and the
health surfaces inside a product that is not otherwise about health.

It is aimed at **laypeople**, and that constraint shows up in every component. A
clinician reading a lab panel wants density, abbreviations and raw values. A
patient reading the same panel wants one number at a time, the unit spelled out,
and a sentence telling them whether they need to do anything. Those two products
are not the same product, and a design system cannot serve both without lying to
one of them. opsinjs serves the second.

It is explicitly **not** for clinician-facing EHR interfaces, for regulated device
software, or for a charting library. If you want to check that before you invest
a sprint, [Is opsinjs right for this project?](./start/is-opsinjs-right.mdx) is a
checklist that will happily tell you no.

## How this site is organised [#how-this-site-is-organised]

Sixteen groups, one persistent sidebar, and one rule that resolves almost every
"where does this live?" question:

> **Foundations tells you what a token means. Handbook and Theming tell you how to
> change it. Reference is the generated list of every one.**

Beyond that:

* **[Start here](./start/index.mdx)** covers orientation, fit, installation, and
  the page you are reading.
* **[Recipes](./recipes/index.mdx)** is task-first. "Show a value against a
  reference range" is a recipe; `RangeBar` is a component.
* **[Components](./components/index.mdx)** has the sixty implemented components
  and their specifications. Every catalogue name has been built out, so each row
  points at code.
* **[Screens](./screens/index.mdx)** is the assembly, and it is where to go if
  you want to see the system working rather than read about it. The
  [diabetes medicines app](./screens/diabetes-medicines-app.mdx) is a whole
  running application built from thirty-five of the components, and it renders in
  the page.
* **[Health](./health/index.mdx)** is the doctrine layer. Read it even if you
  never install anything.
* **[Foundations](./foundations/index.mdx)*&#x2A;, &#x2A;*[Accessibility](./accessibility/index.mdx)*&#x2A;,
  &#x2A;*[Content and language](./content/index.mdx)** are the three pillars that are
  100% real today, because they depend on tokens and rules rather than on code.
* **[Agents](./agents/index.mdx)** is written for machine readers. This site is
  designed to be read by machines as well as people. Every page has a `.md` twin
  at the same URL plus `.md`, and the corpus is published as `llms.txt` shards.

## Verify it worked [#verify-it-worked]

You have read this page correctly if you can answer these three questions without
scrolling back:

1. **What is the one thing opsinjs will never do for you?** Decide what a health
   value means. It renders your product's decision; it does not make it.
2. **What can you install today?** Every component in the catalogue, as copied
   source from the registry. That is not a package, and not anything you should
   put in front of a patient yet, because none of it has been reviewed. Every
   page says so in machine-readable form.
3. **Why are there two kinds of colour?** Because "which measurement is this" and
   "how worried should I be" are different questions, and one palette cannot
   answer both without ambiguity.

## Troubleshooting [#troubleshooting]

**"I came here for charts."** Data visualisation guidance lives in
[Foundations → Data visualisation](./foundations/data-visualisation/index.mdx), but
opsinjs is not a charting library and will not become one. `TrendSparkline` is a
deliberately small, deliberately uninteractive specimen for one series.

**"I need a clinician-facing view."** You are outside the audience. Read
[Is opsinjs right for this project?](./start/is-opsinjs-right.mdx) and then use a
density-first system.

**"The component page I want returned a specification, not code."** Check its
`status`. Every component page reads `shipped` and describes code you can
install. A page that reads `planned` is a specification, and returning it rather
than inventing an API is working as designed. See
[Reading these docs](./start/reading-these-docs.mdx#for-agents).

**"I cannot find the rule that governs a component."** Every component page lists
its `governedBy` doctrine pages in its frontmatter, and every doctrine page lists the
components that implement it. The link exists in both directions on purpose.

## Next [#next]

* [Start here](./start/index.mdx) is the five-minute map of this site, and the
  shortest path for your role.
* [Safety, scope and limitations](./start/safety-scope-and-limitations.mdx) is
  the page to read before you commit to opsinjs for anything regulated or
  clinical-adjacent.
* [The two colour axes](./health/two-colour-axes.mdx) is the single idea that
  most changes how a health product looks.
* [The diabetes medicines app](./screens/diabetes-medicines-app.mdx) is the
  system assembled into a running product, with the refusals that keep a
  medicines record on the safe side of the line between a diary and a regulated
  device.
