Component

Theme Toggle

Sun/moon icon button that switches the site between light and dark mode. Reads and writes the html.dark class, persists the preference to localStorage, and renders a stable placeholder before mount to avoid hydration mismatch.

How it works

StepWhat happens
On mountReads document.documentElement.classList — the FOUC-prevention script in layout.tsx has already applied the correct class from localStorage.
Before mountRenders a Moon icon with pointer-events disabled — stable size prevents layout shift.
On clickToggles .dark on <html>, updates local state, persists "theme" key to localStorage.
Global overrideAll data-theme="dark|light" section overrides are scoped under :not(.dark) — the global toggle always wins.

Appearance

Light mode

Dark mode

32×32px circular hit target. Icon is 15px at 1.5 stroke weight. Foreground color is text-foreground/50 at rest,text-foreground on hover.

Props

PropTypeDefaultDescription
classNamestringOptional class names merged into the button element via cn().

Usage

tsx
import { ThemeToggle } from '@true-north/ui'

// In SiteNav — already wired, no action needed
<ThemeToggle />

// With custom positioning
<ThemeToggle className="ml-auto" />

ThemeToggle is already included in SiteNav — you do not need to add it separately unless building a custom nav.