opsinjs

Introduction

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.

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.
  • The doctrine is testable. The rules in Health 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.

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. 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.

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:

DecisionWhere it is fixed
What a colour is allowed to meanTwo colour axes
The four clinical status levels and their wordsClinical status semantics
How a number, a unit and a precision are renderedNumbers, units and precision
Which unit system a reader sees, and how switching it stays correctUnit systems
How many alarming surfaces one screen may carryAlarm fatigue
The contrast floor every token pair must clearThe contrast floor
What a surface may be made of, and what it must stay readable overMaterials
What motion is allowed to communicate, and what it degrades toMotion in health UI
Which words are banned from patient-facing copyContent and language

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. 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.

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? is a checklist that will happily tell you no.

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 covers orientation, fit, installation, and the page you are reading.
  • Recipes is task-first. "Show a value against a reference range" is a recipe; RangeBar is a component.
  • Components has the sixty implemented components and their specifications. Every catalogue name has been built out, so each row points at code.
  • Screens 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 is a whole running application built from thirty-five of the components, and it renders in the page.
  • Health is the doctrine layer. Read it even if you never install anything.
  • Foundations, Accessibility, Content and language are the three pillars that are 100% real today, because they depend on tokens and rules rather than on code.
  • Agents 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

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

"I came here for charts." Data visualisation guidance lives in Foundations → Data visualisation, 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? 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.

"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

  • Start here is the five-minute map of this site, and the shortest path for your role.
  • Safety, scope and limitations is the page to read before you commit to opsinjs for anything regulated or clinical-adjacent.
  • The two colour axes is the single idea that most changes how a health product looks.
  • The diabetes medicines app 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.

On this page