Theme System

All colors, fonts, spacing, and shadows live as CSS custom properties in src/app/globals.css. Changes propagate through every shadcn/ui component automatically.

Design tokens

The starter uses oklch for colors: it's perceptually uniform, meaning a lightness change looks equally bright across any hue. This matters when building accessible themes.

:root {
  --background: oklch(0.97 0.01 87.41);    /* warm cream */
  --foreground: oklch(0.31 0.02 148.20);   /* deep green-gray */
  --primary:    oklch(0.52 0.13 144.33);   /* forest green */
  --accent:     oklch(0.90 0.05 146.01);   /* pale sage */
  /* ... */
}

Changing the primary color

Pick a color at oklch.com, then swap the three-number oklch in :root:

--primary: oklch(0.55 0.18 260);     /* deep blue */
--primary-hover: oklch(0.50 0.18 260); /* primary minus ~0.05 L */
--ring: oklch(0.55 0.18 260);          /* match primary */

Do the same in .dark for dark-mode values. Every button, link, badge, and focus ring will update.

Full token list

TokenPurpose
--background / --foregroundPage surface + primary text
--card / --card-foregroundCard surfaces
--primary / --primary-foregroundButtons, CTAs, active links
--secondary / --secondary-foregroundSecondary buttons, muted accents
--muted / --muted-foregroundSubtle backgrounds + caption text
--accent / --accent-foregroundHover states, subtle highlights
--destructive / --destructive-foregroundDelete, error
--border / --input / --ringBorders, form fields, focus rings
--chart-1--chart-5Data visualization palette
--sidebar-*Sidebar-specific surfaces

Dark mode

Dark mode is class-based (.dark on <html>) and managed by next-themes. The @variant dark (...) rule in globals.css lets Tailwind v4 toggle dark styles correctly.

import { useTheme } from "next-themes";

const { theme, setTheme } = useTheme();
setTheme("dark"); // or "light" or "system"

The <ThemeToggle /> component in src/components/theme/ is already wired in the header.

Typography

Fonts load via next/font/google in src/app/layout.tsx:

import { Montserrat, Merriweather, Source_Code_Pro } from "next/font/google";

Swap for any Google Font. Variable fonts are preferred for smaller bundle size.

The CSS variables are:

--font-sans:  var(--font-montserrat), -apple-system, ...;
--font-serif: var(--font-merriweather), Georgia, serif;
--font-mono:  var(--font-source-code-pro), ui-monospace, ...;

Change --font-montserrat--font-your-new-sans in both layout.tsx (variable name) and globals.css (variable reference).

Spacing and radius

The starter uses an 8px grid (--space-1 = 4px through --space-24 = 96px) and a 0.5rem base border radius. Adjust in :root to change the feel of the whole app.

Shadows

Six shadow tokens (--shadow-2xs through --shadow-2xl) tuned for subtle depth. Override for a flatter or more dramatic look.

Next