Headless, accessible React components — plus design tokens and a CLI to wire them up
A design system you assemble, not adopt wholesale. Ship unstyled, WAI-ARIA-correct components, bring your own styles (or pull ready-made ones), sync design tokens from Figma, and install it all with one command.
Why Primitiv
- 38 headless, accessible components built on the
WAI-ARIA authoring patterns —
full keyboard support, focus management, and
asChildcomposition. - Zero styles ship by default. Style with whatever you use — CSS, Tailwind, CSS-in-JS, or the generated token layer.
- Design tokens, end to end. DTCG-conformant tokens sync from Figma and emit to CSS, SCSS, and Tailwind (theme + density layers included).
- A real CLI.
primitiv add buttondrops a styled, headless component straight into your Vite or Next.js project — code you own, not a black box. - Perceptually-uniform colour. Palettes are generated by Harmoni, an OkLCH colour engine written in Rust and compiled to WebAssembly.
Packages
| Package | Version | What it is |
|---|---|---|
@primitiv-ui/react |
Headless, accessible React components (zero styles) | |
@primitiv-ui/icons |
41 fill-based SVG icons that inherit currentColor |
|
@primitiv-ui/tokens |
DTCG design-token layer + Figma → DTCG transform | |
primitiv-ui |
The primitiv CLI (add, init, tokens, theme, list) |
|
create-primitiv-ui |
npm create primitiv-ui project scaffold |
Quick start
Set up Primitiv in a Vite or Next.js project:
npm create primitiv-ui@latest
…or add it to an existing app and pull in components as you need them:
npm i -D primitiv-ui
npx primitiv add button
Just want the headless components? Install the library directly:
npm i @primitiv-ui/react
import { Button } from "@primitiv-ui/react";
export function Save() {
return <Button onClick={save}>Save</Button>;
}
The CLI runs a small native binary, so it won't work inside StackBlitz / WebContainer (their in-browser Node can't execute native binaries). Use a local, Codespace, or Docker environment.
Components
@primitiv-ui/react exports 38 headless components built on the WAI-ARIA
authoring patterns. See the
component index for the full list and
per-component docs. Planned components and outstanding workbench examples
live in ROADMAP.md.
What's inside
This is a Rust + pnpm monorepo. Two names, one repo: Primitiv is the product; Harmoni is the colour engine inside it.
primitiv/
├── crates/
│ ├── harmoni-core/ # Pure-Rust palette engine + contrast audit
│ ├── harmoni-wasm/ # wasm-bindgen adapter (browser runtime)
│ ├── primitiv-cli/ # The `primitiv` CLI (add / init / tokens / theme / list)
│ └── primitiv-emit/ # Pure DTCG → CSS / SCSS / Tailwind emitter
├── packages/
│ ├── react/ # Headless React component library
│ ├── icons/ # Fill-based SVG icon library
│ └── tokens/ # DTCG token layer + Figma sync transform
├── npm/ # The CLI wrapper, scaffold, and per-platform binaries
└── apps/
├── workbench/ # React dev surface for iterating on palettes
├── docs/ # Documentation site (VitePress)
├── harmoni-figma-plugin/ # Pull Primitiv palettes into Figma
└── primitiv-sync-figma-plugin/ # Back Figma variables up as DTCG JSON
Powered by Harmoni
Primitiv's colour comes from Harmoni, a perceptually-uniform (OkLCH)
palette engine written in Rust and exposed to the browser through
WebAssembly. Adapters program against the small harmoni_core::api
surface. Full engine docs — generation, neutral ramps, contrast audit, and
the ColorInput parsing path — live in the
harmoni-core README.
Local development
Prerequisites:
- Rust (stable)
- Node 20+ and pnpm 10
wasm-pack— only needed to rebuild the wasm package for the workbench app
pnpm install
cargo test --workspace # Rust test suite
pnpm --filter @primitiv-ui/react qa:units # React tests + coverage
Rebuild the wasm package and start the workbench dev server:
pnpm run build:wasm # regenerates crates/harmoni-wasm/pkg/ (not tracked in git)
pnpm run dev
Contributing
- Strict TDD. New behaviour is driven by a failing test first (red → green → refactor); existing tests stay green. Coverage is held at 100%.
- Small commits. One per red-green(-refactor) cycle, each leaving the tree green.
- Push little and often — short-lived branches via PRs over large unshared histories.
- Adapters program against
harmoni_core::api— extend theapimodule rather than reaching into lower-level modules.
Releasing is documented in RELEASING.md.
License
Primitiv — the @primitiv-ui/* packages and the primitiv CLI — is released
under the MIT License. See LICENSE.