Dark Mode
Auto-detect and respect user preferences đ
Dark Mode
The widget automatically adapts to dark mode using four different strategies. Choose what works best for your app.
Quick Start
import { WhatIf } from '@twenty1tools/whatif';
const whatif = new WhatIf('your-app-id', '/api', {
darkMode: 'auto', // Auto-detect system preference
});Dark Mode Strategies
Auto Mode (Recommended)
Automatically detects and respects the user's system preference using prefers-color-scheme.
{
darkMode: 'auto'
}How it works:
- Checks system preference on load
- Automatically switches when user changes system setting
- Listens to real-time preference changes
- Cleans up listeners on unmount
Perfect for:
- Modern apps that respect user preferences
- Apps without their own theme toggle
- Best user experience
// Full example
const whatif = new WhatIf('app-id', '/api', {
darkMode: 'auto',
colors: {
// These will override auto colors if needed
primary: '#3b82f6',
}
});Force Dark Mode
Always show dark theme, regardless of system preference.
{
darkMode: 'dark'
}Use when:
- Your app is always dark
- You want consistent branding
- Testing dark mode
const whatif = new WhatIf('app-id', '/api', {
darkMode: 'dark',
colors: {
// Customize dark colors
primary: '#60a5fa', // Lighter blue for dark mode
background: '#1f2937', // Dark background
textPrimary: '#f9fafb', // Light text
}
});Force Light Mode
Always show light theme, regardless of system preference.
{
darkMode: 'light'
}Use when:
- Your app is always light
- You want consistent branding
- Testing light mode
const whatif = new WhatIf('app-id', '/api', {
darkMode: 'light',
colors: {
primary: '#3b82f6',
background: '#ffffff',
}
});Disabled (Default)
No dark mode support. Uses light theme colors.
{
darkMode: 'disabled'
}
// or simply omit the darkMode propertyUse when:
- You want full control over colors
- Your app doesn't support dark mode
- Backward compatibility
const whatif = new WhatIf('app-id', '/api', {
// darkMode disabled by default
colors: {
// Specify all colors manually
primary: '#3b82f6',
background: '#ffffff',
textPrimary: '#1f2937',
}
});Default Color Palettes
The SDK includes beautiful default colors for both themes:
Light Theme Colors
{
primary: '#f97316', // Orange
primaryHover: '#ea580c',
background: '#ffffff', // White
textPrimary: '#111827', // Dark gray
textSecondary: '#6b7280', // Medium gray
border: '#e5e7eb', // Light gray
borderHover: '#f97316',
hoverBackground: '#f9fafb',
closeButton: '#6b7280',
closeButtonHover: '#111827',
ratingFilled: '#fbbf24', // Gold
ratingEmpty: '#d1d5db', // Light gray
}Dark Theme Colors
{
primary: '#f97316', // Same orange (brand consistency)
primaryHover: '#fb923c',
background: '#1f2937', // Dark slate
textPrimary: '#f9fafb', // Light gray
textSecondary: '#9ca3af', // Medium gray
border: '#374151', // Dark gray
borderHover: '#f97316',
hoverBackground: '#374151',
closeButton: '#9ca3af',
closeButtonHover: '#f9fafb',
ratingFilled: '#fbbf24', // Gold
ratingEmpty: '#4b5563', // Dark gray
}Custom Dark Mode Colors
Override specific colors while using auto mode:
{
darkMode: 'auto',
colors: {
// These override the defaults for both light and dark
primary: '#8b5cf6', // Purple brand color
primaryHover: '#7c3aed',
// Or use conditional logic
...(isDark ? {
background: '#0f172a', // Darker background
} : {
background: '#ffffff',
})
}
}Sync with Your App Theme
React Hook Example
Sync the widget with your app's theme:
import { WhatIfProvider } from '@twenty1tools/whatif';
import { useState, useEffect } from 'react';
function App() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// Listen to your app's theme changes
const checkTheme = () => {
setIsDark(document.body.classList.contains('dark'));
};
checkTheme();
const observer = new MutationObserver(checkTheme);
observer.observe(document.body, { attributes: true });
return () => observer.disconnect();
}, []);
return (
<WhatIfProvider
projectId="project-id"
theme={{
darkMode: isDark ? 'dark' : 'light'
}}
>
<YourApp />
</WhatIfProvider>
);
}Next.js with next-themes
Perfect integration with next-themes:
'use client';
import { WhatIfProvider } from '@twenty1tools/whatif';
import { useTheme } from 'next-themes';
export function Providers({ children }) {
const { theme } = useTheme();
return (
<WhatIfProvider
projectId="project-id"
theme={{
darkMode: theme === 'dark' ? 'dark' : 'light',
colors: {
primary: '#3b82f6',
}
}}
>
{children}
</WhatIfProvider>
);
}Then use in your layout:
import { ThemeProvider } from 'next-themes';
import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<ThemeProvider attribute="class">
<Providers>{children}</Providers>
</ThemeProvider>
</body>
</html>
);
}Vanilla JavaScript
Update theme when your app theme changes:
const whatif = new WhatIf('app-id', '/api', {
darkMode: 'light' // Initial theme
});
// Listen to your app's theme changes
document.getElementById('theme-toggle').addEventListener('click', () => {
const isDark = document.body.classList.toggle('dark-mode');
// Update widget theme
whatif.setTheme({
darkMode: isDark ? 'dark' : 'light'
});
});Or sync with system preference:
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const whatif = new WhatIf('app-id', '/api', {
darkMode: isDark ? 'dark' : 'light'
});
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
whatif.setTheme({
darkMode: e.matches ? 'dark' : 'light'
});
});Testing Dark Mode
Test Manually
const whatif = new WhatIf('app-id', '/api', {
darkMode: 'dark' // Force dark for testing
});Test Auto Mode
In your browser DevTools:
- Open DevTools (F12)
- Press
Cmd+Shift+P(Mac) orCtrl+Shift+P(Windows) - Type "Rendering"
- Select "Show Rendering"
- Find "Emulate CSS media feature prefers-color-scheme"
- Select "dark" or "light"
Test in Code
// Toggle for testing
let isDark = false;
const whatif = new WhatIf('app-id', '/api', {
darkMode: isDark ? 'dark' : 'light'
});
// Test button
document.getElementById('toggle-dark').addEventListener('click', () => {
isDark = !isDark;
whatif.setTheme({
darkMode: isDark ? 'dark' : 'light'
});
});Best Practices
â Do
- Use
'auto'for the best user experience - Let the SDK handle color adaptation
- Override only colors that need to match your brand
- Test both light and dark modes
â Don't
- Don't manually detect
prefers-color-schemeif using'auto' - Don't override all colors - let defaults work
- Don't forget to test theme transitions
- Don't use
'disabled'unless you have a specific reason
Accessibility
The widget's dark mode follows accessibility best practices:
- â Proper contrast ratios (WCAG AAA compliant)
- â
Respects
prefers-color-scheme - â Smooth transitions
- â No flash of unstyled content
- â Works with screen readers
Examples
Stripe-style Dark Mode
{
darkMode: 'auto',
colors: {
primary: '#635bff',
}
}Always Dark (Gaming App)
{
darkMode: 'dark',
colors: {
primary: '#10b981', // Green accent
background: '#0f172a', // Very dark
}
}Match Your App Exactly
{
darkMode: 'auto',
colors: {
// Use your app's CSS variables
primary: 'var(--primary-color)',
background: 'var(--card-background)',
textPrimary: 'var(--text-primary)',
}
}TypeScript
Full type safety for dark mode:
import { type DarkMode } from '@twenty1tools/whatif';
const mode: DarkMode = 'auto'; // Type-checked
// Only accepts: 'auto' | 'light' | 'dark' | 'disabled'