Contributing
How a change gets into opsinjs. The four routes in, what each one has to clear, and the one thing that will get a component proposal rejected fastest.
The short version
Four kinds of change, four different bars:
| Change | Bar | Where |
|---|---|---|
| A documentation fix | Correctness. Open a pull request. | Any page's "Edit this page" |
| A token change | A migration story, because a token is a public API | Contributing tokens |
| A new component | A proposal answering the intake questions first | Proposing a component |
| A change to doctrine | A dated rationale and a named reviewer | Health |
The fastest way to have a component proposal rejected is to open it with an API. The intake questions are about what the component asserts about somebody's health and what happens when it is wrong; a prop table does not answer either.
How it works
The three release phases
Every component arrives the same way:
planned → shipped → deprecated
plannedis a full specification page: intent, when not to use it naming the alternative, the clinical contract, the proposed anatomy, the proposed API and the accessibility bar. Still no code. No id sits atplannedtoday, because every catalogue row is implemented and installable.shippedmeans code exists, it installs, and the API may change in any release. Every component has been audited against WCAG 2.2 AA by its own authors, in a static source pass and a rendered pass, so it is audited but not independently reviewed, and no component has had a clinical review.shippedmeans the source installs and the authors audited it, and it does not mean an independent reviewer or a clinician signed it. Nothing here is for a production health surface until a clinician signs it. See ADR 0025.deprecatedmeans the code still installs, a replacement is named, and a removal version is named.
What each phase promises is set out in Release
phases. The important consequence for a
contributor is that the specification is the deliverable at planned, and it
is reviewed as seriously as code.
Review
Every change needs one review. Some need more:
- Anything under
health/needs a clinical reviewer, and arevieweddate. - Anything that changes a colour token or a contrast pair means the generated contrast report must be regenerated and committed.
- Anything user-visible in a component needs an accessibility review,
recorded by setting
reviewedandreviewer: designon the day it happened. - Any wording that appears on screen needs a content review.
One pair of frontmatter fields carries all four, reviewed and reviewer, which
is what makes Docs freshness able to report
expiry rather than guess at it. There is no second date field: a review is a
review whoever signed it.
Generated files
A large share of this site is generated. If your change touches a token, a type, a keyboard table, a data attribute or the catalogue, the generated output must be regenerated and committed in the same change. CI runs the generators and fails on any diff. See ADR 0006.
Do this
- Open an issue before a large change. A rejected component proposal after two weeks of work is a bad outcome for everybody, and the intake questions can be answered in an hour.
- Read the page kind's template first. The outline is a contract that
assert-iaenforces, and writing to the template is faster than writing prose and re-cutting it. See Documentation templates. - Say when something is your opinion.
evidence: opinionis always preferred to a citation that does not quite say what you needed it to. - Run
pnpm run checkbefore opening the pull request. Four checks, in order:check:generatedregenerates and fails on a diff,check:iaasserts the frontmatter, the section outlines and both link directions,check:a11yreads the component sources for a status carried by colour alone and the rest of the static accessibility rig, andcheck:llmsproves the machine-readable surface. Freshness is not in it.pnpm run check:freshnessis separate, and it reports rather than fails. - Keep the change one thing. A token rename and a new component in one pull request cannot be reviewed by the people who need to review each.
Not this
- Never invent a citation, a statistic, a DOI or a date. This is the one rule with no exception. A fabricated reference in a health document is the worst thing this repository could contain, and it is worse than an honest absence.
- Do not copy NHS or other Crown-copyright text. Cite it; write your own. See Licence and attribution.
- Do not add a component because a competitor has one. A recorded decision not to build something is a real outcome, and saying so in a proposal costs nothing.
- Do not hand-edit a generated file. It will be overwritten and the diff will fail CI.
- Do not add a page without a
kind, or a component page without astatus. The build will reject either, and the rejection is deliberate.
Gotchas
assert-iachecks headings against the page kind. An extra H2 fails the build even if it is a good heading, because the outline is what keeps hundreds of pages navigable.- Aliases are globally unique. They are declared in
registry/catalogue.ts; a page that invents one will collide eventually. - Links in MDX are relative and resolved by
createRelativeLink. An absolute site link fails the IA check, and there is no longer an exception. - British spelling in prose, American in code. A page named
colour-independence.mdxdocumenting aColorScaleis correct. See Naming conventions. - A clean clone needs
pnpm installbeforetypecheck, because.source/is generated bypostinstalland gitignored.
Related
- Proposing a component has the intake questions.
- Component checklist is the definition of done.
- Documentation templates has the page outlines, published verbatim.
- Community is where to ask before you build.
Migrating from shadcn/ui
The token map, the component equivalences and the API translations. It ends with an honest list of what opsinjs has no equivalent for.
Proposing a component
The intake questions a proposal must answer before an API is worth discussing. The four reasons a proposal is normally rejected.