opsinjs
HandbookCorrectness and cost

Naming conventions

The published naming contract for component ids, prop names, token names, data attributes, files and CSS custom properties, and where the two spellings diverge.

The short version

Names are a public API. Renaming a component id changes a URL, a registry entry and an agent's guess; renaming a CSS custom property breaks somebody's override. Both are covered by semver, so the conventions are published here rather than left in an internal document. See Versioning policy.

ThingCaseExample
Component id (paths, catalogue, registry)kebab-caseresult-card
Component name in prose and in codePascalCaseResultCard
Props interfacePascalCase, <Component>PropsResultCardProps
PropcamelCaseshowRange
Boolean proppositive, no is/has prefixdisabled, not isDisabled
CSS custom propertykebab-case, --opsin- prefix--opsin-status-urgent-surface
Data attributekebab-case, data- prefixdata-status
Source filekebab-caseresult-card.tsx
Documentation pagekebab-caseresult-disclosure.mdx
Token key in JSONkebab-casestatus.urgent.surface

How it works

The spelling rule

British spelling in prose. American spelling in code.

This looks like an inconsistency and is a deliberate one. The prose is written for a British-English-first audience and uses colour, behaviour, visualisation, organisation. The code cannot: CSS defines color, Tailwind generates --color-*, the DOM exposes backgroundColor, and a codebase that spells it both ways is a codebase with two of every symbol.

So: a page about colour documents a ColorScale component reading a --color-* token. Both spellings are correct in their own layer, and mixing them within a layer is the error.

The one place this bites is documentation file names. Page paths follow the prose: foundations/colour/, accessibility/colour-independence.mdx. Code identifiers inside those pages follow the code.

The two-axis namespaces

The colour system's two axes have two reserved prefixes and they never mix:

--opsin-status-<level>-<role>       steady · watch · attention · urgent · unknown
--opsin-category-<name>-<role>      heart · activity · sleep · nutrition · mind · labs

A token named --opsin-category-heart-alert is a category token trying to carry status and is rejected on sight. This is the naming form of the two-axis rule, and it is what makes it mechanically checkable. The stylelint plugin is what runs that check.

Aliases

Search synonyms are declared once, in registry/catalogue.ts, and referenced by pages. They are globally unique across the corpus, which is only maintainable with a single owner; a page that invents its own alias will eventually collide with another page's, and assert-ia fails the build when it does.

Do this

  • Name a component for what it shows, not for how it looks. ResultCard, not BorderedPanel. StatusPill is a borderline case kept because "pill" is what everybody calls it.
  • Name a prop for the thing, not the implementation. range, not rangeConfigObject.
  • Keep boolean props positive. disabled reads correctly; notEnabled produces double negatives at every call site.
  • Order token names from general to specific: axis, then member, then role. It sorts usefully and it groups in devtools.
  • Match the file name to the id. result-card.mdx, result-card.tsx and result-card.json are the same string everywhere, so a search finds all of it.
  • Use the word the reader uses. Plain-English A to Z is the authority when a clinical term and a plain one compete.

Not this

  • Do not abbreviate. bp is blood pressure to one team and British Petroleum to a search engine. desc, val, cfg and btn all cost more than they save.
  • Do not encode the type in the name. statusString, rangeArray and IResultCardProps all say what TypeScript already knows.
  • Do not use variant as a catch-all prop. In a health system it is the prop most likely to end up carrying both axes at once. Name what varies.
  • Do not name anything normal. It is a banned word in this system, in prose and in code, because of what it implies about a reading and about a person. See Reference ranges.
  • Do not use error for a clinical state. Nothing has failed; see Clinical status semantics.
  • Do not put a version or a date in a name. ResultCardV2 is a migration that never finished.

Gotchas

  • A component id appears in five places: the path, the catalogue, the registry item, the implements and governedBy frontmatter, and every relative link. assert-ia checks them, which is the only reason the rename is survivable.
  • Kebab-case and PascalCase must round-trip. log-sheetLogSheet is unambiguous; a1c-tile is not, and names with digits or acronyms need to be checked in both directions.
  • CSS custom properties are case-sensitive, unlike the rest of CSS. --Opsin-Status is a different property and no error is reported.
  • Tailwind derives utility names from token names. A token rename changes the generated class, which is a breaking change for anyone who used it.
  • The British/American split is easy to get backwards in a file name. The page is colour-independence.mdx; the component inside it is ColorScale.
  • Aliases are matched case-insensitively but stored as written, so two pages differing only in case still collide.
  • Code style covers the conventions as a contribution requirement.
  • TypeScript has the named-props-interface rule that the generated API tables depend on.
  • Versioning policy says what semver covers, which is more than the JavaScript API.
  • The two colour axes is the rule the token namespaces enforce.

On this page