Skip to content
v1.18 · MIT · React 18+

A themeable React design system.

75 components built on OKLCH, Tailwind CSS v4 and Base UI. Five themes, light and dark, with every colour pairing audited against WCAG AA. Set one attribute and every component repaints, including the ones you wrote.

bun add @mikenotthepope/substrateui
pick a theme — this page is the demo
Tint rampstints mixed in oklab · 25 / 50 / 75 / 100%
Primary--primary
Secondary--secondary-fill
Neutral--foreground
Live

The same markup in every theme.

Nothing on the left knows which theme it is in. The map on the right is the only thing that changed — and it is the whole theme, not an excerpt.

Project settings
Draft

Lowercase, no spaces.

Preview deployments

Build every pull request.

theme.ts
import { createTheme, ThemeRegistry } from 'substrateui'

const proof = createTheme({
  name: 'proof',
  light: { primary: 'oklch(0.520 0.133 232)' },
  dark:  { primary: 'oklch(0.750 0.115 233)' },
})

// Wrap once — every component below repaints.
<ThemeRegistry themes={[proof]} defaultTheme="proof">
  <App />
</ThemeRegistry>

A theme sets palette, motion curve and corner radius. Light and dark come with it.

Fit

Who it's for

Every claim below is checked by the build, not by us.

You ship more than one brand

A theme is a token map, and no component ever learns its name. Adding the sixth palette costs a file, not a refactor — and light and dark come with it.

Accessibility is a requirement, not a wish

35 colour pairings per theme are checked against WCAG AA on every commit, and a failing ratio fails the build. Base UI handles focus, dismissal and ARIA underneath.

You want to own your stack

MIT, no runtime service, no account. React 18+ and Tailwind v4 are the only hard peers — Next and next-themes are optional, so it is not a Next.js-only system.

Job docket

What you get

ItemValueDetail
Components75atomic Button through organism App Shell
Themes5proof · substrate · lava · tundra · plum
Audited pairs35per theme, light and dark. A failing ratio fails the build.
ColourOKLCHperceptually uniform ramps, so contrast is computed, not eyeballed
PrimitivesBase UIfocus management, dismissal and ARIA are handled
CSSTailwind v4@theme inline — no config file
Peer depsreact ≥ 18tailwindcss ≥ 4 · next and next-themes optional
LicenceMITfree to fork, ship and sell

Bring your own theme

Five themes ship, but the set is open. A theme is a token map — no component ever learns its name.