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
| Path | Contains | Generated from |
|---|---|---|
/r/registry.json | The shadcn-spec catalog: every item, its type, title, description and dependencies | registry/catalogue.ts |
/r/index.json | Name, type, status, category, links and aliases per item, which make up the opsinjs view rather than the shadcn one | registry/catalogue.ts |
/r/<name>.json | One registry item, resolved at the default base and style | registry/catalogue.ts |
/r/docs.json | The documentation corpus, versioned and size-capped. See the offline bundle | the page tree |
Two more are files in the repository rather than routes, because their consumers are build tools rather than clients:
| File | Contains |
|---|---|
tokens/*.json | The authored token source: colour, material, motion, type, space, shape |
tokens/glossary.json | Every 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 replacementshipped 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 -30You 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
- Offline docs bundle covers everything above in one request.
- Registry overview says what the
/r/surface is and why it follows someone else's spec. - Writing docs for agents shows how a page becomes well-formed structured data.