Dark Mode
Adding dark mode to your Next.js app, it's a one-time process. We use next-themes and CSS variables for a seamless dark mode experience.
Step 1
Install next-themes
Install next-themes by running the following command:
- npm
- yarn
- pnpm
- bun
npm i next-themes
yarn add next-themes
pnpm i -D next-themes
bun add -D next-themes
Step 2
Create a theme provider
Create a theme provider component, components/theme-provider.tsx
'use client';
import { ThemeProvider as NextThemeProvider } from 'next-themes';
export function ThemeProvider({ children }: React.PropsWithChildren<{}>) {
return (
<NextThemeProvider
enableSystem
defaultTheme="system"
disableTransitionOnChange
>
{children}
</NextThemeProvider>
);
}
Step 3
Wrap your root layout
Add the ThemeProvider component to your root layout, app/layout.tsx
import { ThemeProvider } from '@/components/theme-provider';
import '@/app/globals.css';
export const metadata = {
title: 'App Name',
description: 'Write your app description',
};
export default async function RootLayout({ children }: RootLayoutProps) {
return (
<html
// 💡 Prevent next-themes hydration warning
suppressHydrationWarning
>
<body
// 💡 Prevent hydration warnings caused by third-party extensions, such as Grammarly.
suppressHydrationWarning
>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
Step 4
Add theme-switcher
Add a theme switcher components, components/theme-switcher.tsx
'use client';
import React from 'react';
import { useTheme } from 'next-themes';
import { ActionIcon } from 'rizzui/action-icon';
import { DropdownMenu } from 'rizzui/dropdown';
import { MoonIcon, SunIcon } from '@heroicons/react/24/outline';
export function ThemeSwitcher() {
const { setTheme } = useTheme();
return (
<Dropdown>
<Dropdown.Trigger>
<ActionIcon variant="outline">
<SunIcon className="h-5 w-5 dark:hidden" />
<MoonIcon className="absolute h-5 w-5 hidden dark:block" />
<span className="sr-only">Toggle theme</span>
</ActionIcon>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item onClick={() => setTheme('light')}>Light</Dropdown.Item>
<Dropdown.Item onClick={() => setTheme('dark')}>Dark</Dropdown.Item>
<Dropdown.Item onClick={() => setTheme('system')}>System</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
Step 5
Configure globals.css
Configure globals.css​
RizzUI 2.0 uses Tailwind CSS v4 with the modern OKLCH color space for better color perception. Create or update your app/globals.css:
/* Import Tailwind CSS v4 */
@import 'tailwindcss';
/* Configure content sources for RizzUI components */
@source '../node_modules/rizzui/dist';
/* Enable enhanced form styles */
@plugin '@tailwindcss/forms';
/* Enable dark mode support */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
/* Light theme colors */
:root {
/* Base Colors */
--background: oklch(100% 0 0); /* #ffffff */
--foreground: oklch(40.17% 0 0); /* #484848 */
--muted: oklch(91.58% 0 0); /* #e3e3e3 */
--muted-foreground: oklch(66% 0 0); /* #929292 */
/* Border Tokens */
--border-radius: 0.5rem; /* 8px */
--border-width: 0.0625rem; /* 1px */
--border-color: oklch(90.37% 0 0); /* #dfdfdf */
/* Text Tokens */
--text-primary: oklch(0% 0 0); /* #000000 */
--text-secondary: oklch(40.17% 0 0); /* #484848 */
/* Primary Brand Colors */
--primary-lighter: oklch(91.58% 0 0); /* #e3e3e3 */
--primary: oklch(17.76% 0 0); /* #111111 */
--primary-dark: oklch(0% 0 0); /* #000000 */
--primary-foreground: oklch(100% 0 0); /* #ffffff */
/* Secondary Colors */
--secondary-lighter: oklch(91.99% 0.0386 276.02); /* #dde3ff */
--secondary: oklch(50.51% 0.2633 276.95); /* #4e36f5 */
--secondary-dark: oklch(45.41% 0.2431 277.06); /* #432ad8 */
--secondary-foreground: oklch(100% 0 0); /* #ffffff */
/* Danger/Error Colors */
--red-lighter: oklch(89.99% 0.0393 14); /* #f7d4d6 */
--red: oklch(59.6% 0.2445 29.23); /* #ee0000 */
--red-dark: oklch(51.71% 0.2121 29.2338); /* #c50000 */
/* Warning Colors */
--orange-lighter: oklch(95.67% 0.0452 84.5695); /* #ffefcf */
--orange: oklch(78.37% 0.1587 72.99); /* #f5a623 */
--orange-dark: oklch(54.83% 0.1339 53.95); /* #ab570a */
/* Info Colors */
--blue-lighter: oklch(91.66% 0.0404 257.5078); /* #d3e5ff */
--blue: oklch(57.31% 0.2144 258.25); /* #0070f3 */
--blue-dark: oklch(51.58% 0.1888 258.27); /* #0761d1 */
/* Success Colors */
--green-lighter: oklch(92.79% 0.086 155.61); /* #b9f9cf */
--green: oklch(64.01% 0.1776 148.74); /* #11a849 */
--green-dark: oklch(53.79% 0.1441 149.52); /* #11843c */
}
/* Dark theme colors */
[data-theme='dark'] {
/* Base Colors */
--background: oklch(14.11% 0.0112 275.23); /* #08090e */
--foreground: oklch(90.37% 0 0); /* #dfdfdf */
--muted: oklch(32.11% 0 0); /* #333333 */
--muted-foreground: oklch(51.03% 0 0); /* #666666 */
/* Border Tokens */
--border-color: oklch(91.58% 0 0); /* #e3e3e3 */
/* Text Tokens */
--text-primary: oklch(100% 0 0); /* #ffffff */
--text-secondary: oklch(51.03% 0 0); /* #666666 */
/* Primary Brand Colors */
--primary-lighter: oklch(25.2% 0 0); /* #222222 */
--primary: oklch(95.81% 0 0); /* #f1f1f1 */
--primary-dark: oklch(100% 0 0); /* #ffffff */
--primary-foreground: oklch(0% 0 0); /* #000000 */
/* Secondary Colors */
--secondary-lighter: oklch(26.35% 0.1154 280.96); /* #1f165a */
--secondary-dark: oklch(85.2% 0.0733 276.238); /* #c1cbff */
/* Danger/Error Colors */
--red-lighter: oklch(27.08% 0.1111 29.23); /* #500000 */
--red-dark: oklch(86.69% 0.0714 18.6304); /* #ffc1c1 */
/* Warning Colors */
--orange-lighter: oklch(28.29% 0.0698 49.34); /* #441d04 */
--orange-dark: oklch(93.15% 0.1175 98.83); /* #fcea8b */
/* Info Colors */
--blue-lighter: oklch(32.05% 0.0873 254.4); /* #0d335e */
--blue-dark: oklch(90.53% 0.0611 225.72); /* #b5e9ff */
/* Success Colors */
--green-lighter: oklch(27.23% 0.0672 152.71); /* #033016 */
--green-dark: oklch(92.79% 0.086 155.61); /* #b9f9cf */
}
/* Map CSS variables to Tailwind's color system */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--border-radius: var(--border-radius);
--color-border: var(--border-color);
--color-text-primary: var(--text-primary);
--color-text-secondary: var(--text-secondary);
--color-primary-lighter: var(--primary-lighter);
--color-primary: var(--primary);
--color-primary-dark: var(--primary-dark);
--color-primary-foreground: var(--primary-foreground);
--color-secondary-lighter: var(--secondary-lighter);
--color-secondary: var(--secondary);
--color-secondary-dark: var(--secondary-dark);
--color-secondary-foreground: var(--secondary-foreground);
--color-red-lighter: var(--red-lighter);
--color-red: var(--red);
--color-red-dark: var(--red-dark);
--color-orange-lighter: var(--orange-lighter);
--color-orange: var(--orange);
--color-orange-dark: var(--orange-dark);
--color-blue-lighter: var(--blue-lighter);
--color-blue: var(--blue);
--color-blue-dark: var(--blue-dark);
--color-green-lighter: var(--green-lighter);
--color-green: var(--green);
--color-green-dark: var(--green-dark);
}
Note: use our theming tools to customize these CSS variables
Step 6
Configure PostCSS
Create or update your postcss.config.mjs:
const config = {
plugins: {
'@tailwindcss/postcss': {},
},
};
export default config;
You are now ready to use dark mode in your application. Tailwind CSS v4 requires no tailwind.config.js file.
Test Your Dark Mode​
Use the theme switcher component to toggle between light and dark modes:
import { Button } from 'rizzui';
import { ThemeSwitcher } from '@/components/theme-switcher';
export default function App() {
return (
<div>
<ThemeSwitcher />
<Button>Get Started</Button>
</div>
);
}
Your application will now seamlessly switch between light and dark themes based on user preference.