---
title: "Packages"
description: "Five npm packages sit alongside the copy-paste registry, for the jobs that copied source cannot do. None of them has shipped yet."
url: "https://opsinjs.pensievelabs.org/packages"
source: "https://opsinjs.pensievelabs.org/packages.md"
section: "Other pages"
kind: "guide"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["npm packages", "opsinjs packages", "published library", "package list"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> `<StubNotice>` IS THE EXCEPTION, AND IT IS THE ONE TO READ. It is a
> paired element rather than a self-closing one, and the text between
> its opening and closing tags is prose an author wrote, reproduced
> below word for word. That prose is where this page says whether the
> component has been reviewed. Read the children, not only the
> attributes.

<PageTemplate kind="guide" />

## Overview [#overview]

opsinjs distributes components by copying source, not by npm. The reasoning is
in [Registry and distribution](../registry/index.mdx). That model is right for
components and wrong for three other things:

* **Algorithms.** The colour engine is maths. You want one implementation,
  versioned, callable from a script, producing identical answers in your CI and
  on this site. Copying it into ten projects produces ten slightly different
  answers over time.
* **Build-time tooling.** A Tailwind preset and a stylesheet are configuration,
  not application code; nobody wants to hand-merge them.
* **A conventional consumer.** Some teams cannot take copied source at all,
  because of procurement rules, monorepo policy, or a genuine preference. A
  normal package should exist for them.

So there are five packages. All five are `status: planned`, none has been
published, and this group documents them as specifications so that the API can
be reviewed before it is built rather than after.

<StubNotice
  name="opsinjs-packages"
  issue="prashantonomy/opsinjs#0"
  questions="[
  &#x22;Should @opsinjs/react ship the same components the registry copies, or a smaller stable subset?&#x22;,
  &#x22;Does @opsinjs/color version independently of the design system, or in lockstep with the tokens?&#x22;,
  &#x22;What is the deprecation contract for a package that a health product has taken a hard dependency on?&#x22;,
]"
/>

## The five [#the-five]

| Package                                               | What it is                                                    | Why it is a package and not copied source       |
| ----------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- |
| [`@opsinjs/color`](./opsinjs-color.mdx)               | OKLCH, APCA, WCAG 2.2, gamut mapping, `deriveTheme()`         | Maths. One implementation, checkable in your CI |
| [`@opsinjs/motion`](./opsinjs-motion.mdx)             | Spring solving, `linear()` generation, reduced-motion helpers | Same reason. Also generates token values        |
| [`@opsinjs/react`](./opsinjs-react.mdx)               | The components, as a conventional dependency                  | For teams that cannot take copied source        |
| [`@opsinjs/preset`](./opsinjs-preset.mdx)             | The Tailwind theme layer as an importable preset              | Build configuration, not application code       |
| [`@opsinjs/tailwind.css`](./opsinjs-tailwind-css.mdx) | The stylesheet: token layer, variants, degradations           | A file you import, not a file you own           |

## Which of these do you need? [#which-of-these-do-you-need]

For most projects: none of them.

The registry gives you components and a theme with no dependency at all, and a
project can go a long way on copied source plus a stylesheet, arguably all the
way. Reach for a package when you have a specific reason.

<Steps>
  ### You want the contrast check in CI [#you-want-the-contrast-check-in-ci]

  [`@opsinjs/color`](./opsinjs-color.mdx). This is the strongest case for any of
  them: a theme you have changed needs its own measurements, and a browser tab is
  not a CI job. See
  [Validating your theme](../theming/validating-your-theme.mdx).

  ### You are generating tokens [#you-are-generating-tokens]

  [`@opsinjs/color`](./opsinjs-color.mdx) and
  [`@opsinjs/motion`](./opsinjs-motion.mdx) are the engines this repository uses
  to produce its own generated files. If you are running a fork, you are already
  using them; the package makes them usable outside it.

  ### Your organisation cannot take vendored source [#your-organisation-cannot-take-vendored-source]

  [`@opsinjs/react`](./opsinjs-react.mdx). Accept the trade knowingly: you gain a
  normal upgrade path and lose the ability to read and edit the component that
  renders a clinical result.

  ### You have many apps and one theme [#you-have-many-apps-and-one-theme]

  [`@opsinjs/preset`](./opsinjs-preset.mdx) and
  [`@opsinjs/tailwind.css`](./opsinjs-tailwind-css.mdx). Copying a stylesheet into
  twelve applications is how twelve applications end up with eleven themes.
</Steps>

## Versioning and stability [#versioning-and-stability]

Two policies, stated now because a health product will ask before it depends on
anything.

**The engines version independently.** `@opsinjs/color` and `@opsinjs/motion`
are libraries with their own semantics. A design-system release does not imply
an engine release.

**A change to a measured output is a breaking change.** If `apca()` returns a
different number for the same input, that is major, even if the API is
identical. That is because somebody's CI gate depends on the number, not on the
signature. This is stricter than most libraries and it is the correct rule for
something whose output is used as an accessibility claim.

The general policy is in
[Versioning policy](../project/versioning-policy.mdx); these two are the
package-specific parts of it.

## Verify it worked [#verify-it-worked]

Nothing has shipped, so the honest check is on the specification rather than on
an installation. Three things to look for when reviewing these pages:

<Steps>
  ### Every proposed API is marked as proposed [#every-proposed-api-is-marked-as-proposed]

  A fenced signature on these pages is a proposal that may change without a
  deprecation cycle. If you find one that reads as though it exists, that is a
  documentation bug worth reporting.

  ### Nothing here duplicates the registry [#nothing-here-duplicates-the-registry]

  A package that re-solves a problem the registry already solves is scope creep.
  Each of the five should have a one-sentence answer to "why not copied source",
  and it is in the table above.

  ### The clinical invariants survive [#the-clinical-invariants-survive]

  No package may make it easier to mix the two colour axes, invent a fifth status
  level, or assert a threshold. If a proposed API would, say so before it is
  built.
</Steps>

## Troubleshooting [#troubleshooting]

**`npm install @opsinjs/color` fails.** Nothing is published. That is the current
state, not a registry outage.

**An assistant generated an import from one of these.** It read a proposal as an
API. See [Rules for agents](../agents/rules-for-agents.mdx); this is exactly the
failure the not-implemented markers exist to prevent.

**I need the colour maths today.** The implementations are in this repository
under `lib/color/`, hand-written and dependency-free, and the repository is
licensed for you to use them. The package is packaging, not capability.

## Next [#next]

* [`@opsinjs/color`](./opsinjs-color.mdx) is the one with the strongest case for
  existing.
* [Registry and distribution](../registry/index.mdx) is the model these packages
  sit beside.
* [Roadmap](../project/roadmap.mdx) says when any of this might be real.
