The component registry
Twenty-nine installable items, shadcn-compatible. What is interesting about them is not what they render — it is what their APIs refuse to express, because a rule in a document is advice and a rule in a type signature is a rule.
A reference implementation of the rules, not a validated library. These
components typecheck under strict and are render-tested in Node against 57
published rules. Nothing here has been opened in a browser, used on a touch device, exercised
with a screen reader, or run in a real application. See
Status & limitations.
Installing
Install foundations first. It carries the token contract — including the
IEC 60601-1-8 alarm extension and its parity requirement — plus njt-tokens.json,
which states which tokens are governed and why in a file your build can read, rather than only
as prose on a site your team may never visit.
$ npx shadcn@latest add https://md.notjustany.tech/r/foundations.json
$ npx shadcn@latest add https://md.notjustany.tech/r/alert-banner.json
The index at /r/registry.json lists every item. Each component page ends with the
command for that component, and check-registry.mjs fails the build if any published
command points at something that is not built — a rule that exists because all twenty-nine of
them returned 404 for months.
Dependencies
None at runtime. Not clsx, not tailwind-merge, not Radix. A design system for regulated
software should not oblige its consumer to justify a dependency tree in a technical file, so the
class joiner is six lines and ships inside foundations. Swap in your own
cn if you already have one.
The components are unstyled beyond the token contract and reference the design tokens throughout, so they inherit whatever theme the tokens define.
The catalogue
Generated from registry/registry.json, so it cannot drift from what is actually
published.
| Item | What it is | What its API refuses |
|---|---|---|
| Foundations — tokens & primitives foundations |
The token contract, including the IEC 60601-1-8 alarm extension, plus the shared types every other item depends on. Install this first. | Alarm tokens identical in both themes by contract, shipped with the reason they may not change. |
| Alert banner alert-banner |
IEC 60601-1-8 priority-coded alarm banner. No bulk acknowledgement, no dismiss for an alarm condition. | No onAcknowledgeAll, no array form, no dismiss for an alarm condition. |
| Status chip status-chip |
The state of one object the reader is already looking at. | No icon-only form. |
| Badge badge |
An exact count of things elsewhere. No capping, so “99+” cannot be produced. | No max prop, so 99+ cannot be produced. |
| Pagination pagination |
A list with its denominator always stated. | total is required and exact — there is no unknown-total mode. |
| Key–value pair key-value |
A measurement, or a typed absence. Not-measured cannot arrive here as a blank. | A discriminated union, not string | null: not-measured cannot arrive as a blank. |
| Empty state empty-state |
Proves the system looked, rather than showing nothing. | searched is required — the proof that it looked. |
| Progress progress |
A determinate bar. Was one item with Activity, Skeleton and PendingValue — four answers to “not here yet” under one command. |
value, max and unit are all required, and there is no shape that could discard them. |
| Activity activity |
The indeterminate indicator, linear, circular or inline. | No value on the type at all — it cannot claim to know how long. |
| Skeleton & pending value skeleton |
Placeholders that reserve the shape of what is coming. | Skeleton has no value variant; a grey bar never stands in for a number. |
| Numeric input numeric-input |
Spin box with a clinical envelope. Empty is distinct from zero; out-of-range is refused, not warned. | number | null that never coerces — an unset rate is not a rate of zero. |
| Text field text-field |
Labelled input with a remedy-bearing error. No placeholder-as-label. | placeholder is omitted from the prop type. |
| Textarea textarea |
Multi-line input that never silently truncates what a clinician typed. | No maxLength attribute, so keystrokes are never silently swallowed. |
| Checkbox checkbox |
Stages a change rather than applying one. | indeterminate reaches the element, not just the styling. |
| Radio radio |
Nothing selected by default. | A pre-selected radio is an answer nobody gave. |
| Switch switch |
Takes effect the moment it moves. | effect has one permitted value, so a switch that defers cannot be declared. |
| Select select |
A native select, with an explicit unselected state. | placeholderOption for anything with no safe default. |
| Search search |
Patient search where the identifier carries equal weight to the name. | Identifier and date of birth carry the same weight as the name. |
| Card card |
Surface container, plus the interactive form as a real link. | The interactive form is a real link, not a div with a handler. |
| Dialog dialog |
Blocking, and only with a real branch. Confirmation is disarmed on appearance. | Both confirm and cancel are required; confirm is disarmed for 400 ms. |
| Toast & notification queue toast-queue |
Transient messages. Alarm conditions are unexpressible by construction. | The tone union omits critical and urgent. |
| Data grid data-grid |
A real table with scoped headers, units in the header and tabular figures. | A real table with scoped headers; empty is required. |
| List list |
Flat lists with a selection header. | The header states what a bulk action would act on — never a bare “Select all”. |
| Tree tree |
Nesting, with its own expansion state and traversal. | Not a variant of a list; a flat worklist has no use for one. |
| Tabs tabs |
Roving-tabindex tab list with counts, so a hidden panel can be skipped safely. | Counts on tabs, so a hidden panel can be skipped safely. |
| Accordion & panel accordion |
Collapsing with a required summary. Panel is the default. | summary is required on every collapsed item. |
| Filter filter |
Filters that state what they are hiding, and never hide an alarm. | hiddenCount is required and exact. |
| Menu menu |
Contextual actions with destructive items separated and last. | Destructive items are separated and last. |
| Button button |
Buttons and button groups. Split out of toolbar, whose install command was the only way to get one while the Button page offered none. | children is required — nothing is icon-only.
disabled is not a prop; unavailable takes the reason. |
| Toolbar toolbar |
One tab stop with arrow-key traversal. Depends on button, so installing it still brings its buttons. | Actions never rearrange themselves — nothing reorders on use. |
| Tooltip tooltip |
Supplementary only, with keyboard parity. Never the sole carrier of anything. | aria-describedby, never aria-labelledby. |
| Masthead masthead |
The application bar: product name, environment banner, signed-in user. | The environment banner cannot be switched off in a non-production build. |
| Side navigation side-nav |
A nav list whose counts say what they count. | The current item is marked, not merely coloured. |
| Breadcrumb breadcrumb |
The trail, as data. | A crumb cannot be a control that does something. |
| View heading view-heading |
Names the view and sets the document title. | The browser tab says which patient is open. |
| App layout app-layout |
The shell, with a docked alarm region that never scrolls away. | Ships with SkipLink, which points at the main region the layout renders. |
| Split pane split-pane |
A two-pane split for one screen. | Not part of the application shell. |
| Panel panel |
A titled container. | Lived in the accordion item, then the layout item; it is neither. |
| Date & time picker date-picker |
Clinical time: zone-aware, and carrying the precision it was known to. | Asks how well the time is known before asking what it is. |
| Device identification panel device-label |
MDR/IVDR identification: intended purpose verbatim, UDI, manufacturer, notified body. | An absent notified body is stated, not left blank. |
Do's and don'ts
Install foundations, then take the
components you need.
The token contract is the part that carries safety meaning. The components are conveniences built on top of it.
Copy a component and leave the tokens behind.
The alarm hues then become whatever your theme says, which is the one thing the parity contract exists to prevent.
Read a missing prop as the rule it encodes.
Badge has no max because a capped count is a
lie with a plus sign on it. The absence is the design.
Fork it to put the prop back without saying why.
Entirely possible, which is why this is a reference implementation rather than a control. If you add it, record the reasoning.
Run your own accessibility and usability evaluation.
Installing this performs no part of your IEC 62366-1 file. The checklist is where to start.
Cite the registry as evidence of conformity.
It is an argument, not evidence. The manufacturer validates under MDR or IVDR regardless of what was installed.
Related
- Using the system — adopting it in an existing product.
- Tokens — what
foundationsinstalls, and the governance classes. - Status & limitations — including that none of this has been rendered in a browser.
- Contributing — the prohibitions these APIs encode.
- Accessibility checklist — what to run against anything you build with these.