ZeroStarterRC

Theming

Dark mode, CSS variables, and styling with Tailwind CSS v4.

Overview

ZeroStarter uses Tailwind CSS v4 with CSS custom properties for theming, next-themes for dark/light mode switching, and the Fumadocs UI theme for documentation pages.

Dark Mode

Dark mode is handled by next-themes with system preference detection:

// web/next/src/app/providers.tsx
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
  {children}
</ThemeProvider>

The ModeToggle component at web/next/src/components/mode-toggle.tsx is a smart toggle: from system it switches to the opposite of the OS preference, it flips between light and dark while they diverge from the OS preference, and it returns to system once the explicit theme matches the OS preference again.

CSS Variables

Theme colors are defined in web/next/src/app/globals.css using OKLch color space:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --muted: oklch(0.97 0 0);
  --border: oklch(0.922 0 0);
  /* ... */
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.922 0 0);
  /* ... */
}

These variables are consumed by Tailwind utility classes like bg-background, text-foreground, border-border, etc.

Tailwind Configuration

Tailwind v4 is configured via PostCSS in web/next/postcss.config.mjs:

export default { plugins: { "@tailwindcss/postcss": {} } }

web/next/src/app/globals.css imports Tailwind, animations, and the Fumadocs UI and shadcn presets:

@import "tailwindcss";
@import "tw-animate-css";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";
@import "shadcn/tailwind.css";

It then wires the design tokens to Tailwind v4 via an @theme inline block that maps --color-* utilities to the --* custom properties (background, foreground, primary, card, popover, chart, sidebar, radius, and so on).

Fonts

Fonts are loaded with next/font/local from .woff2 files in web/next/src/fonts/, defined in web/next/src/lib/fonts.ts (not via @fontsource). Four variable fonts are included:

  • DM Sans: Primary UI font (body text, headings)
  • JetBrains Mono: Monospace font (code blocks)
  • Caveat: Handwriting accent font
  • Newsreader: Serif accent font

Each exposes a CSS variable (e.g. --font-dm-sans) that the @theme inline block maps onto Tailwind's font utilities.

Component Styling

Components use the shadcn/ui base-nova preset with Base UI primitives. The configuration is in web/next/components.json:

{
  "style": "base-nova",
  "iconLibrary": "remixicon",
  "tailwind": {
    "baseColor": "neutral",
    "cssVariables": true
  }
}

Utility Functions

import { cn } from "@/lib/utils"

// Merge Tailwind classes with conflict resolution
<div className={cn("text-sm", isActive && "font-bold")} />

Customizing the Theme

  1. Edit CSS variables in web/next/src/app/globals.css for global color changes
  2. Use Tailwind utility classes that reference the CSS variables
  3. Update components.json and run bun run shadcn:update to regenerate components with a different preset