AniUI ships dark + light tokens out of the box. The library doesn't ship a toggle UI itself — that's a 5-line component on your end so you have full control over persistence and behavior.
How AniUI defines the modes
/* Light is the default */
:root {
--background: 60 30% 98%;
--foreground: 240 10% 4%;
--primary: 240 6% 10%;
/* ... */
}
/* Dark is opt-in via .dark class */
.dark {
--background: 240 17% 4%;
--foreground: 220 14% 96%;
--primary: 239 84% 67%;
/* ... */
}Toggle between modes by adding/removing the dark class on <html>. Every component re-themes instantly because they consume the variables, not hard-coded colors.
Recommended: next-themes
On Next.js, next-themes handles persistence and avoids the flash-of-wrong-theme on first paint:
// app/providers.tsx
"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }) {
return (
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem={false}>
{children}
</ThemeProvider>
);
}Then a toggle is just useTheme():
import { useTheme } from "next-themes";
const { setTheme, resolvedTheme } = useTheme();
<button onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}>
Toggle
</button>Asset swaps for branding
Logos and illustrations that need an inverse for each mode: render both versions and swap with Tailwind's dark: variant. No JS state, no hydration churn:
<Image src={logoLight} className="block dark:hidden" />
<Image src={logoDark} className="hidden dark:block" />
