opsinjs

Machine-readable schemas

The catalogue, the token set, the status vocabulary and the glossary as JSON at stable paths, so a tool can resolve a fact instead of parsing prose.

Overview

Prose is for people. A build script, a linter, a validator or an agent with a narrow question should not have to read a paragraph to learn that urgent is a valid status and critical is not.

Four things are published as JSON at stable paths, and each of them is the same source the site itself renders from. None of them is an export, none is a copy, and none is a periodically refreshed mirror. If the site and the JSON ever disagree, the JSON is not the one that is wrong.

The four surfaces

PathContainsGenerated from
/r/registry.jsonThe shadcn-spec catalog: every item, its type, title, description and dependenciesregistry/catalogue.ts
/r/index.jsonName, type, status, category, links and aliases per item, which make up the opsinjs view rather than the shadcn oneregistry/catalogue.ts
/r/<name>.jsonOne registry item, resolved at the default base and styleregistry/catalogue.ts
/r/docs.jsonThe documentation corpus, versioned and size-capped. See the offline bundlethe page tree

Two more are files in the repository rather than routes, because their consumers are build tools rather than clients:

FileContains
tokens/*.jsonThe authored token source: colour, material, motion, type, space, shape
tokens/glossary.jsonEvery clinical term with its plain-English replacement

The status vocabulary

Three values, closed, and used identically by a component page's frontmatter, the catalogue and the registry:

type Status =
  | "planned"     // specified, not implemented. No component sits here today
  | "shipped"     // installable source. The API may change in any release
  | "deprecated"  // installable source, on its way out, with a named replacement

shipped covers every component id today. It means installable source and a changing API, and it makes no claim about clinical safety, because no opsinjs component has had a clinical review. Every shipped component has been audited against WCAG 2.2 AA by its own authors, in a static source pass and a rendered pass, with findings fixed in the same change; that is not an independent accessibility review, so status still makes no claim of accessibility conformance. See the audit is author-run. planned is a specification you may read and must not generate against. Handle all three anyway: the vocabulary is closed, not fixed, and a client that only knows the values it saw once will misread the day one of them moves.

status appears on a kind: component page and on a catalogue row, and nowhere else. A documentation page carries no status at all, so a tool that reads one from a page about colour or consent is reading a field that does not exist.

The clinical status levels are a different, unrelated four-value vocabulary, and the four are steady, watch, attention and urgent. Confusing the two is a real failure mode worth guarding against in any tool you write. One describes whether a component's code exists; the other describes a person's health data. The clinical vocabulary also has a fifth token stem, unknown, which is the absence of an assertion rather than a fifth level; a schema that ranks it alongside the four has already got it wrong.

The catalogue

registry/catalogue.ts is the single source of truth for component identity: id, title, plain-English description, category, status, since, aliases and the reviewing discipline. It has no field for an independent accessibility review date or a clinical review date, because neither has happened; the WCAG 2.2 AA audit that has happened was run by the authors and is recorded in the audit is author-run, not in this file. Every id in it resolves somewhere. That rule is enforced by scripts/assert-ia.mts rather than by convention.

It is also the sole owner of the alias namespace. Aliases are search synonyms, and lab result finds result-card. Every alias is required to be globally unique across the corpus, which is only achievable if one file declares them all. A documentation page references an alias; it never invents one.

The glossary

tokens/glossary.json pairs a clinical term with a plain-English replacement and a short definition written for a reader with no medical training. It drives <Term>, <PlainLanguage> and the filterable A to Z on Plain-English A to Z.

Every definition in it is original prose. Public health bodies publish excellent plain-language glossaries and most of them are under a licence that does not permit reuse. The NHS A to Z is Crown copyright. opsinjs cites such sources and never copies them, and any contribution that pastes one will be rejected. See Licence and attribution.

Using them well

Resolve, do not guess

If your tool needs to know whether symptom-picker exists, fetch /r/index.json and look. Do not infer it from a URL pattern and do not ask a model.

Check status and implemented on every read

Every catalogue row is implemented and installable, and every one has been audited against WCAG 2.2 AA by its own authors and none has had an independent accessibility review or a clinical review. status tells you whether code exists, not whether it is safe; implemented tells you whether there is source to install, which is the question most tools are actually asking. A tool that ignores either will behave, next year, as though a specification were a component, or it will refuse to install a component that ships.

Treat aliases as input, not as output

Match a user's phrasing against aliases. Never emit an alias as an identifier. lab result is not importable.

Cache with the version

/r/docs.json carries docsVersion and generatedAt in its body. A markdown twin carries the same version on its x-opsinjs-docs-version response header and not in its frontmatter, so a twin saved to a file loses it. Record it as you fetch. Cache the version alongside the data either way, so a stale answer can be identified as stale.

Verify it worked

curl -s https://opsinjs.pensievelabs.org/r/index.json | head -30

You should get JSON with one entry per catalogue row, each carrying a status and an implemented flag. Every entry reports implemented: true today. Confirm you read the flag rather than inferring it from the URL: the first planned row after this sentence will report implemented: false, and that is the field a tool must check.

Troubleshooting

/r/registry.json is missing items I can see in the sidebar. The registry lists distributable items. Documentation pages that describe doctrine rather than code have no registry entry, by design.

Aliases in the JSON differ from the ones on a page. They cannot, unless the build is stale. Both come from the catalogue. Rebuild, and report it if it persists.

The token JSON does not match the CSS. pnpm run generate has not run. The CSS is generated from the JSON, and --opsin-tokens-generated will say placeholder when it has not.

I want a schema for the frontmatter. It is content/_templates/frontmatter.schema.json, and source.config.ts is the copy the build actually enforces.

Next

On this page