Documentation

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

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 property

Use 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:

  1. Open DevTools (F12)
  2. Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows)
  3. Type "Rendering"
  4. Select "Show Rendering"
  5. Find "Emulate CSS media feature prefers-color-scheme"
  6. 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-scheme if 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'

Next Steps