Theme Controls
Theme mode selector (light / dark / system) and compact light–dark toggle—presentational controls for nav and settings.
Theme appearance controls: a theme mode selector (three-way light / dark / system; ThemeModeSelector) and a theme mode toggle (compact light ↔ dark; ThemeModeToggle). Both use shadcn primitives and are controlled—they do not import next-themes; you connect value / onValueChange from useTheme() (or your own store). This site’s docs run inside Fumadocs RootProvider, which integrates with next-themes, so the demos below work without extra wiring.
Next Themes
Mount ThemeProvider from next-themes at your app root when you integrate outside Fumadocs. These components only render UI; they do not depend on next-themes at import time.
Theme Mode Selector
Light, dark, and system via a Select with sun / moon / monitor affordances.
Installation
Registry name theme-mode-selector. Same workflow as Installation.
pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-selector.jsonnpx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-selector.jsonyarn dlx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-selector.jsonbun x shadcn@latest add https://ui.isaacfei.com/r/theme-mode-selector.jsonNamespace: npx shadcn@latest add @f-ui/theme-mode-selector — pulls lucide-react and registry select.
Usage
import { ThemeModeSelector } from '@/components/f-ui/theme-mode-selector';
import { useTheme } from 'next-themes';
function NavTheme() {
const { theme, setTheme } = useTheme();
return (
<ThemeModeSelector
value={(theme as 'light' | 'dark' | 'system') ?? 'system'}
onValueChange={setTheme}
ariaLabel="Theme"
/>
);
}Translated labels:
<ThemeModeSelector
value={theme}
onValueChange={setTheme}
options={[
{ value: 'light', label: '浅色' },
{ value: 'dark', label: '深色' },
{ value: 'system', label: '跟随系统' },
]}
/>Composition
Select with Sun / Moon / Monitor on the trigger and one SelectItem per mode.
Example
Current: —
"use client";
import { useTheme } from "next-themes";
import { ThemeModeSelector } from "@/components/f-ui/theme-mode-selector";
export function ThemeModeSelectorDemo() {
const { theme, setTheme } = useTheme();
const value =
theme === "light" || theme === "dark" || theme === "system"
? theme
: "system";
return (
<div className="max-w-sm space-y-3">
<ThemeModeSelector
value={value}
onValueChange={setTheme}
ariaLabel="Theme"
/>
<p className="text-muted-foreground text-xs">
Current:{" "}
<span className="text-foreground font-medium tabular-nums">{theme ?? "—"}</span>
</p>
</div>
);
}API Reference
| Prop | Type | Default |
|---|---|---|
value | "light" | "dark" | "system" | — |
onValueChange | (value: ThemeMode) => void | — |
options | ThemeModeOption[] | Light / Dark / System |
ariaLabel | string | "Select theme" |
className | string | — |
Theme Mode Toggle
Compact control that flips between light and dark only (no system option on the control itself).
Installation
Registry name theme-mode-toggle.
pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-toggle.jsonnpx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-toggle.jsonyarn dlx shadcn@latest add https://ui.isaacfei.com/r/theme-mode-toggle.jsonbun x shadcn@latest add https://ui.isaacfei.com/r/theme-mode-toggle.jsonNamespace: npx shadcn@latest add @f-ui/theme-mode-toggle — pulls lucide-react and registry button.
Usage
Use when you do not need a system option. Map next-themes “system” to a concrete value for display if needed.
import { ThemeModeToggle } from '@/components/f-ui/theme-mode-toggle';
import { useTheme } from 'next-themes';
function NavThemeToggle() {
const { theme, setTheme } = useTheme();
const resolved = theme === 'dark' ? 'dark' : 'light';
return (
<ThemeModeToggle
value={resolved}
onValueChange={setTheme}
ariaLabel="Toggle theme"
/>
);
}Composition
Ghost Button with stacked sun/moon icons and CSS transitions for dark mode.
Example
Toggle drives light/dark; resolved for icon: light
"use client";
import { useTheme } from "next-themes";
import { ThemeModeToggle } from "@/components/f-ui/theme-mode-toggle";
export function ThemeModeToggleDemo() {
const { theme, setTheme } = useTheme();
const resolved = theme === "dark" ? "dark" : "light";
return (
<div className="flex max-w-sm flex-wrap items-center gap-4">
<ThemeModeToggle
value={resolved}
onValueChange={setTheme}
ariaLabel="Toggle theme"
/>
<p className="text-muted-foreground text-xs">
Toggle drives light/dark; resolved for icon:{" "}
<span className="text-foreground font-medium tabular-nums">{resolved}</span>
</p>
</div>
);
}API Reference
| Prop | Type | Default |
|---|---|---|
value | "light" | "dark" | — |
onValueChange | (value: "light" | "dark") => void | — |
ariaLabel | string | "Toggle theme" |
className | string | — |