---
title: "Handbook"
description: "The mechanics layer is how you change things. It is kept deliberately separate from what a token means and from the generated list of every one."
url: "https://opsinjs.pensievelabs.org/handbook"
source: "https://opsinjs.pensievelabs.org/handbook.md"
section: "Handbook"
kind: "handbook"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["handbook", "mechanics", "how do I change this", "developer guide"]
---

> 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.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="handbook" />

<SectionProgress />

## The short version [#the-short-version]

Three pillars document the same system from three angles, and knowing which one
you are in saves most of the time people lose looking things up:

> **Foundations = what a token *means*. Handbook and Theming = how you *change*
> it. Reference = the generated list of *every* one.**

That sentence is printed at the top of all three sections and it is enforced:
`assert-ia` fails the build when a Handbook page starts explaining meaning, or a
Foundations page starts explaining overrides.

This is the middle layer Base UI deliberately leaves out. Base UI gives you
unstyled primitives and a state-attribute contract, then stops. Stopping there
is correct, because it is a primitive library. What it does not tell you is
where the `className` goes, which attribute your transition should key off,
where the client boundary falls in an App Router application, or what to assert
in a test.
Those questions have real answers, they are the same answers for everyone, and
they belong somewhere.

## How it works [#how-it-works]

The Handbook is organised by the moment you have the question.

**Working with components.** You have a component on screen and you want it to
look or behave differently. [Styling](./styling.mdx) is the entry point and
names the four hooks; [Data attributes](./data-attributes.mdx) is the shared
state vocabulary you style against;
[Composition and render](./composition-and-render.mdx) covers merging opsinjs
parts into your own elements; [Forms](./forms.mdx) is the wiring;
[Motion in practice](./motion-in-practice.mdx) is writing transitions that
respect the reduced-motion contract; [Icons](./icons.mdx) is the icon slot.

**Environment** is the application around the component.
[Dark mode](./dark-mode.mdx), [Server and client
components](./server-and-client-components.mdx),
[Internationalisation](./internationalisation.mdx) and
[TypeScript](./typescript.mdx).

**Correctness and cost.** Keeping it right as it grows.
[Naming conventions](./naming-conventions.mdx), [Error
codes](./error-codes.mdx), [Performance and bundle
size](./performance-and-bundle-size.mdx), [Testing](./testing.mdx), and
[Tooling](./tooling/index.mdx). Tooling covers the lint rules that enforce the
invariants that documentation alone cannot.

**Moving in and contributing.** [Migrating from
shadcn/ui](./migrating-from-shadcn.mdx) and
[Contributing](./contributing/index.mdx).

### How a Handbook page says what is real [#how-a-handbook-page-says-what-is-real]

A Handbook page carries no `status`; only a component page does. What it carries
instead is a `<NotBuiltYet>` marker, and the marker is the thing to read. These
are real now: the dark-mode strategy, the naming conventions and the contributing
process all apply to this repository as it stands, and those pages carry no
marker.

A page that does carry one is describing a mechanism nobody has checked against
the sixty components now built. Code existing is not the same as the convention
holding, and the marker tracks the second. Those pages are specifications you may
review and argue with; they are not yet instructions you can follow.

## Do this [#do-this]

Read [Styling](./styling.mdx) first if you are new. It is short, and it fixes
the mental model that everything else assumes: four override hooks, in a fixed
order of preference, and a rule about which one to reach for.

When you have a question that starts "how do I make it…", you are in the
Handbook. When it starts "why is it…", you are in
[Foundations](../foundations/index.mdx). When it starts "what is the exact list
of…", you are in [Reference](../reference/index.mdx).

## Not this [#not-this]

Do not treat the Handbook as an API reference. The generated lists live in
[Reference](../reference/index.mdx) and are produced from source by
`pnpm run generate`. Those lists are every token, every data attribute, every
CSS variable, every keyboard shortcut and every exported type. A Handbook page
that starts enumerating is drifting, and any number typed into one by hand is
wrong the moment the generator next runs.

Do not look for design rationale here either. Why the status axis has four
levels is [Health](../health/index.mdx); why the material ladder has six rungs
is [Foundations → Materials](../foundations/materials/index.mdx).

## Gotchas [#gotchas]

* **CSS import order in `globals.css` is load-bearing and silent when wrong.**
  The Tailwind v4 layer order, the fumadocs preset and the `dark` variant
  declaration interact, and a wrong order produces a site that mostly works. See
  [Tailwind v4](../theming/tailwind-v4.mdx).
* **`className` on a compound component reaches the part you named, not the
  whole.** The most common styling surprise; [Styling](./styling.mdx) explains
  the resolution order.
* **The client boundary is not where people expect.** Importing one interactive
  component into a server component can pull a provider with it. See
  [Server and client components](./server-and-client-components.mdx).
* **Nothing here is generated except the counts.** If a Handbook page shows you
  a table of token names, it is a Handbook page doing Reference's job. Report
  it.

## Related [#related]

* [Foundations](../foundations/index.mdx) says what each token means, and the
  reasoning behind the scales.
* [Theming and tokens](../theming/index.mdx) is the other half of "how you
  change it": themes, presets, and adding your own tokens.
* [Reference](../reference/index.mdx) has the generated lists, committed and
  searchable.
* [Contributing](./contributing/index.mdx) shows how to change opsinjs itself
  rather than your use of it.
