Foundations

Themes

Two themes, Paper and Graphite, from one set of tokens. Each token holds both values, so any part of a page can take either theme.

Paper and Graphite

Paper is the light theme: warm neutral greys with a hint of paper. Graphite is the dark one, tuned on its own rather than inverted. ThemeProvider follows the system unless the reader picks one, and writes data-theme on the page.

tsx
import { useTheme } from "@nibapp/ui";function ThemeChoice() {  const { preference, setPreference } = useTheme();  // "system", "light" or "dark"; the choice is remembered on this device.  return <button onClick={() => setPreference("dark")}>Graphite</button>;}

One part of a page in the other theme

Set data-theme on any element. Everything inside it, the components included, takes that theme.

tsx
<section data-theme="dark">  {/* Graphite here, whatever the page's theme */}</section>

Semantic tokens

Components use these names, not raw colours. They are the stable surface of the theme: a renamed token is a breaking change.

bg, bg-subtle, bg-muted
The canvas, a sidebar, a well
surface, surface-raised, surface-overlay
Cards, menus and popovers, dialogs
fg, fg-muted, fg-subtle
Text, from the most to the least important
border, border-subtle, border-strong
Hairlines, which work on any surface
primary, primary-fg
The main action: ink, not colour
accent, focus, selection
Focus rings, selected text and progress
success, warning, danger, info
Status, always with a glyph or words beside it

One accent, used rarely

Actions are ink. The accent marks focus, selected text, progress and a confirmed copy, and almost nothing else: buttons, tabs, switches, checkboxes and links stay neutral. To use your own accent, set these variables after the kit's stylesheet.

app.css
/* After the kit's stylesheet */:root {  --accent: oklch(58% 0.16 150);  --accent-fg: oklch(45% 0.12 150);  --accent-subtle: color-mix(in oklab, oklch(58% 0.16 150) 12%, transparent);  --focus: oklch(58% 0.16 150);  --selection: color-mix(in oklab, oklch(58% 0.16 150) 24%, transparent);}