UI.MD

Theming

Override CSS custom properties to make the design system yours. Every token resolves automatically in light and dark mode.

How theming works

Changing the accent color

The most common customization. Define both light and dark values.

:root {
  --accent: oklch(0.65 0.22 150);
}

.dark {
  --accent: oklch(0.72 0.19 150);
}

This changes links, active states, and primary buttons across every component. Colors use the OKLCH color space for perceptual uniformity, so equal lightness steps look equally bright.

What can I theme?

Every token category can be overridden. See the design system pages for the full catalog.

CategoryTokens
ColorsAccent, destructive, warning, success, surfaces, text hierarchy
Typography12 sizes from 3xs to jumbo, font families, weights, tracking
SpacingBase unit, container width, max-width scale, breakpoints
ShadowsFour-level shadow scale from xs to lg
RadiiComponent tier system: form controls, buttons, cards, modals, indicators

Overriding multiple tokens

Override as many or as few tokens as you need.

:root {
  /* Colors */
  --accent: oklch(0.65 0.22 150);
  --background: oklch(0.98 0.005 90);
  --card: oklch(1 0 0);

  /* Radius (override the scale step you want to reshape) */
  --radius-lg: 0.75rem;
}

.dark {
  --accent: oklch(0.72 0.19 150);
  --background: oklch(0.15 0.005 90);
  --card: oklch(0.18 0.005 90);
}

Each component tier reads a step of the --radius-* scale: inputs use --radius-md, buttons and cards use --radius-lg, drawers and sheets use --radius-xl. Override the step you want to reshape.

Typography

Font families resolve through a three-level chain, so the base theme never ships a licensed typeface.

Default: Geist Sans and Geist Mono

Do nothing and you get Geist. create-next-app already wires this up via next/font/google, so most projects need no extra setup:

import { Geist, Geist_Mono } from "next/font/google"

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
})

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
})

Preferred: Innovator Grotesk

This site renders in Innovator Grotesk, the UI.MD brand face. It’s a commercial font and isn’t part of the registry, so licensees load it themselves and wire it through the --font-sans-custom hook:

// app/layout.tsx
import localFont from "next/font/local"

const innovatorGrotesk = localFont({
  src: "./fonts/InnovatorGroteskVF-VF.woff2",
  variable: "--font-innovator",
  weight: "100 900",
  display: "swap",
})
:root {
  --font-sans-custom: var(--font-innovator);
}

Any other font

The hook isn’t specific to Innovator Grotesk. Load any font with next/font, then point --font-sans-custom (or --font-mono-custom for code) at its variable to override the default without touching component code.

Next steps