ONONC
Theming

Theming

ONONC components style themselves with a small, shared set of Tailwind v4 design tokens. Set them up once — with the CLI or by hand — and every component renders correctly, in both light and dark mode.

Install the theme

Here is the good news: you usually don't have to think about this at all. Installing any component with the shadcn CLI writes the tokens into your project automatically. But if you want the tokens on their own — for example before you copy-paste a component by hand, or to share one theme across a design system — install the standalone theme item once:

1npx shadcn@latest add https://dev.ononc.com/r/ononc-theme.json

This writes the @theme tokens, the :root/.dark variables, keyframes, and utilities into your globals.css — with no component files added.

Design tokens

Rather than hard-coding colors, ONONC components paint themselves with a small set of Tailwind v4 tokens. In your markup they show up as ordinary utility classes — bg-surface, text-foreground, border-border, bg-brand — so a component always adopts the theme it is dropped into. Here is the full set, with the value each takes in light and dark mode:

TokenPurposeLightDark
backgroundPage background#f6f7fb#06070d
surfaceCards & raised panels#ffffff#0b0d18
surface-2Secondary surface#eef0f7#11142370
foregroundPrimary text#0c0e1a#e9ebf5
mutedSecondary text#545b70#8a91a8
muted-2Faint text / hints#8a91a8#5b6178
borderHairline bordersrgba(12,14,26,.1)rgba(255,255,255,.08)
border-strongStronger bordersrgba(12,14,26,.16)rgba(255,255,255,.14)
brandPrimary brand (violet)#8b5cf6#8b5cf6
brand-inkBrand text / ink#6d28d9#c4b5fd
brand-2Secondary brand (cyan)#0891b2#22d3ee
brand-3Accent (rose)#e11d62#fb7185
ringFocus-ring colorrgba(139,92,246,.55)rgba(139,92,246,.55)

Each token maps to a Tailwind color utility through @theme, so surface becomes bg-surface, text-surface, border-surface, and so on. The one exception in feel is ring: it is the color of the :focus-visible outline that every interactive component draws.

Light & dark mode

ONONC is dark-first, but every token is defined twice. :root holds the default (light) values, and .dark overrides the ones that change for dark mode. Dark mode itself switches on the moment the .dark class is present on the <html> element — toggle that class with your theme switcher of choice and every token flips at once. Here is the shape of it, abbreviated:

1@import "tailwindcss";
2
3/* Dark mode turns on via a .dark class on <html> */
4@custom-variant dark (&:where(.dark, .dark *));
5
6:root {
7 --background: #f6f7fb; /* light */
8 --surface: #ffffff;
9 --foreground: #0c0e1a;
10 --brand: #8b5cf6;
11}
12
13.dark {
14 --background: #06070d; /* dark override */
15 --surface: #0b0d18;
16 --foreground: #e9ebf5;
17 --brand-ink: #c4b5fd;
18}
19
20@theme inline {
21 /* expose the vars as Tailwind utilities: bg-surface, text-foreground, ... */
22 --color-background: var(--background);
23 --color-surface: var(--surface);
24 --color-foreground: var(--foreground);
25 --color-brand: var(--brand);
26}

The cn helper

Nearly every ONONC component imports a tiny helper called cn to merge Tailwind class names, so a caller's classNamecleanly overrides the component's defaults instead of fighting them. It lives at src/lib/utils.ts and leans on two small packages, clsx and tailwind-merge. The CLI adds it for you; if you copy a component by hand, add this file too or its import will break:

1import { type ClassValue, clsx } from "clsx";
2import { twMerge } from "tailwind-merge";
3
4/** Merge Tailwind classes, resolving conflicts (clsx + tailwind-merge). */
5export function cn(...inputs: ClassValue[]) {
6 return twMerge(clsx(inputs));
7}

Copying tokens by hand

Not using the CLI? No problem — do these three things once, and every component you copy afterward will render correctly:

  1. 1
    Copy the token layer — the @theme block, the :root/.dark variables, the @keyframes, and the @utility helpers — from src/app/globals.css into your own globals.css.
  2. 2
    Add the cn helper shown above at src/lib/utils.ts.
  3. 3
    Install the two small packages it depends on:
    1npm i clsx tailwind-merge

Want the complete, annotated list of tokens to copy? See the design tokens guide.

Customizing your theme

Because everything routes through CSS variables, restyling ONONC is mostly a matter of editing values — no component changes required. To recolor, change a token's value in both :root and .dark so it holds in either mode. Corner rounding lives in the --radius-4xl token (the rounded-4xl utility), and typography is driven by --font-sans and --font-mono. For example:

1.dark {
2 --brand: #ec4899; /* make the brand pink */
3 --radius-4xl: 1.5rem; /* tighter corners */
4}

A note on token names

A few tokens use deliberately generic names — background, foreground, border, muted, ring. If you install ONONC into a project that already defines those — an existing shadcn theme, say — the incoming values will override yours. That is easy to live with: reconcile the overlapping tokens, or scope ONONC's under a wrapper class, so the two themes can coexist.