Overview
Lambda UI Components provides built-in dark mode support with two dark themes: dark, slate, graphite, deep-cosmic-night, soft-obsidian, midnight. The library also includes six light themes (light, retro, lavender, mint, sunset, ocean). Dark mode can be enabled automatically based on system preferences, manually toggled by users, or forced for specific pages.
The dark mode implementation is powered by the ThemeProvider component and uses CSS variables to ensure smooth transitions and consistent styling across all components.
Quick Start
To enable dark mode in your application, wrap your app with the ThemeProvider:
import { ThemeProvider } from "lambda-ui-components";
export default function App({ children }) {
return (
<ThemeProvider
defaultTheme="system"
enableSystem={true}
darkTheme="dark"
lightTheme="light"
>
{children}
</ThemeProvider>
);
}With enableSystem set to true and defaultTheme="system", the theme will automatically match the user's system preference.
Available Dark Themes
Lambda UI Components includes two dark theme variants:
dark (Default)
A sleek, modern dark theme with teal accents. Features a neutral dark background (#2b2b2b) with carefully balanced contrast for optimal readability.
slate
A cool, sophisticated dark theme with cyan accents. Features a deep slate background (#0e1316) with blue-gray tones for a professional appearance.
System Preference Detection
When enableSystem is enabled, Lambda UI Components automatically detects the user's system color scheme preference using the prefers-color-scheme media query:
import { ThemeProvider } from "lambda-ui-components";
export default function App({ children }) {
return (
<ThemeProvider
defaultTheme="system"
enableSystem={true}
darkTheme="slate" // Use slate for dark mode
lightTheme="light" // Use light for light mode
>
{children}
</ThemeProvider>
);
}The theme will automatically update when the user changes their system preference, without requiring a page reload.
Manual Dark Mode Toggle
Use the ButtonTheme component to provide a manual toggle for users:
import { ButtonTheme } from "lambda-ui-components";
export default function Header() {
return (
<header>
<nav>
<h1>My App</h1>
<ButtonTheme
animation="scale"
color="neutral"
size="medium"
/>
</nav>
</header>
);
}The ButtonTheme component automatically toggles between the configured lightTheme and darkTheme.
Dark Mode Specific Styling
You can apply styles specifically for dark mode using the data-theme attribute:
/* Styles for all dark themes */
[data-theme="dark"] .my-component,
[data-theme="slate"] .my-component {
background-color: var(--surface-a);
border-color: var(--border-color);
}
/* Styles only for the dark theme */
[data-theme="dark"] .my-component {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
/* Styles only for the slate theme */
[data-theme="slate"] .my-component {
box-shadow: 0 4px 6px rgba(14, 19, 22, 0.5);
}Conditional Rendering
You can conditionally render content based on the current theme using the useTheme hook:
import { useTheme } from "lambda-ui-components";
export default function ThemedImage() {
const { resolvedTheme } = useTheme();
const isDark = resolvedTheme === "dark" || resolvedTheme === "slate";
return (
<img
src={isDark ? "/logo-dark.png" : "/logo-light.png"}
alt="Logo"
/>
);
}Preventing Flash of Unstyled Content
The ThemeProvider automatically includes a script that runs before the page renders to prevent the flash of unstyled content (FOUC) when loading a saved theme.
For Next.js applications, add the following to your layout:
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}The suppressHydrationWarning attribute prevents React hydration warnings caused by the theme script modifying the HTML before React hydrates.
Disabling Transitions
To prevent visual glitches during theme changes, you can disable CSS transitions:
import { ThemeProvider } from "lambda-ui-components";
export default function App({ children }) {
return (
<ThemeProvider
defaultTheme="system"
enableSystem={true}
disableTransitionOnChange={true}
>
{children}
</ThemeProvider>
);
}This temporarily disables all CSS transitions when the theme changes, preventing colors from animating during the switch.
Forcing Dark Mode
You can force dark mode for specific pages or sections using the forcedTheme prop:
import { ThemeProvider } from "lambda-ui-components";
export default function DarkOnlyPage({ children }) {
return (
<ThemeProvider forcedTheme="dark">
{children}
</ThemeProvider>
);
}This is useful for pages that should always be dark (like a photo gallery) or always light (like a print preview).
Color Scheme Meta Tag
Enable the color-scheme CSS property to style browser UI elements:
import { ThemeProvider } from "lambda-ui-components";
export default function App({ children }) {
return (
<ThemeProvider
defaultTheme="system"
enableSystem={true}
enableColorScheme={true}
>
{children}
</ThemeProvider>
);
}This sets the color-scheme CSS property on the HTML element, which affects browser UI elements like scrollbars, form controls, and the default background color.
Customizing Dark Mode Colors
You can customize the colors used in dark mode by overriding CSS variables:
/* Customize the dark theme */
[data-theme="dark"] {
/* Background colors */
--background-color: #1a1a1a;
--surface-a: #242424;
--surface-b: #2e2e2e;
/* Text colors */
--foreground-color: #e5e5e5;
--foreground-title-color: #ffffff;
--foreground-secondary-color: #a0a0a0;
/* Primary accent */
--primary-base-color: #60a5fa;
--primary-hover-color: #3b82f6;
/* Borders and shadows */
--border-color: #3a3a3a;
--shadow-color: #00000080;
}
/* Customize the slate theme */
[data-theme="slate"] {
--background-color: #0f172a;
--surface-a: #1e293b;
--primary-base-color: #06b6d4;
--primary-hover-color: #0891b2;
}Best Practices
- Respect user preferences - Enable system preference detection by default
- Provide manual control - Include a theme toggle button for users who want to override system preferences
- Test thoroughly - Ensure all components look good in both light and dark modes
- Maintain contrast - Ensure sufficient color contrast in dark mode for accessibility (WCAG AA: 4.5:1 for normal text)
- Use semantic colors - Use CSS variables instead of hardcoded colors to ensure proper theming
- Avoid pure black - Use dark grays instead of pure black (#000000) for better readability
- Disable transitions - Set
disableTransitionOnChangeto prevent visual glitches
Troubleshooting
Flash of unstyled content (FOUC)
Make sure you're using the ThemeProvider at the root of your app and add suppressHydrationWarning to your HTML element in Next.js.
Theme not persisting
Check that localStorage is available and not blocked. The theme is saved under the key specified in storageKey (default: "theme").
System preference not detected
Ensure enableSystem is set to true and the user's browser supports the prefers-color-scheme media query.
Colors animating during theme change
Set disableTransitionOnChange={true} in the ThemeProvider to temporarily disable CSS transitions during theme changes.