Skip to Content
Veltix
ThemesLight & Dark Themes

Light & Dark Themes

Veltix provides built-in light and dark themes with automatic system preference detection and seamless switching.

Overview

The light and dark theme system includes:

  • Automatic Detection: Detect system theme preferences
  • Manual Switching: Toggle between light and dark themes
  • Consistent Styling: Unified design across all components
  • Accessibility: WCAG compliant color contrasts
  • Smooth Transitions: Animated theme switching

Theme Switching

Automatic Theme Detection

import { useTheme } from '@veltix/ui'; function ThemeAwareComponent() { const { theme, setTheme, isDark, isSystem } = useTheme(); return ( <div className={`theme-${theme}`}> <h1>Current Theme: {theme}</h1> <p>Is Dark: {isDark ? 'Yes' : 'No'}</p> <p>Using System: {isSystem ? 'Yes' : 'No'}</p> <div className="theme-controls"> <button onClick={() => setTheme('light')}>Light</button> <button onClick={() => setTheme('dark')}>Dark</button> <button onClick={() => setTheme('auto')}>Auto</button> </div> </div> ); }

System Theme Detection

// Detect system theme preference const useSystemTheme = () => { const [systemTheme, setSystemTheme] = useState('light'); useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleChange = (e) => { setSystemTheme(e.matches ? 'dark' : 'light'); }; // Set initial theme setSystemTheme(mediaQuery.matches ? 'dark' : 'light'); // Listen for changes mediaQuery.addEventListener('change', handleChange); return () => { mediaQuery.removeEventListener('change', handleChange); }; }, []); return systemTheme; };

Light Theme

Light Theme Colors

const lightTheme = { colors: { primary: { 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a' }, background: { primary: '#ffffff', secondary: '#f8fafc', tertiary: '#f1f5f9' }, text: { primary: '#0f172a', secondary: '#475569', tertiary: '#64748b', inverse: '#ffffff' }, border: { primary: '#e2e8f0', secondary: '#cbd5e1', focus: '#3b82f6' }, chart: { axis: '#64748b', grid: '#e2e8f0', text: '#475569' } } };

Light Theme Components

// Light theme component styles const lightComponentStyles = { card: { backgroundColor: '#ffffff', borderColor: '#e2e8f0', shadow: '0 1px 3px rgba(0, 0, 0, 0.1)' }, button: { primary: { backgroundColor: '#3b82f6', color: '#ffffff', borderColor: '#3b82f6' }, secondary: { backgroundColor: 'transparent', color: '#3b82f6', borderColor: '#3b82f6' } }, input: { backgroundColor: '#ffffff', borderColor: '#e2e8f0', textColor: '#0f172a', placeholderColor: '#64748b' } };

Dark Theme

Dark Theme Colors

const darkTheme = { colors: { primary: { 50: '#0f172a', 100: '#1e293b', 200: '#334155', 300: '#475569', 400: '#64748b', 500: '#94a3b8', 600: '#cbd5e1', 700: '#e2e8f0', 800: '#f1f5f9', 900: '#f8fafc' }, background: { primary: '#0f172a', secondary: '#1e293b', tertiary: '#334155' }, text: { primary: '#f8fafc', secondary: '#cbd5e1', tertiary: '#94a3b8', inverse: '#0f172a' }, border: { primary: '#334155', secondary: '#475569', focus: '#60a5fa' }, chart: { axis: '#94a3b8', grid: '#334155', text: '#cbd5e1' } } };

Dark Theme Components

// Dark theme component styles const darkComponentStyles = { card: { backgroundColor: '#1e293b', borderColor: '#334155', shadow: '0 1px 3px rgba(0, 0, 0, 0.3)' }, button: { primary: { backgroundColor: '#60a5fa', color: '#0f172a', borderColor: '#60a5fa' }, secondary: { backgroundColor: 'transparent', color: '#60a5fa', borderColor: '#60a5fa' } }, input: { backgroundColor: '#1e293b', borderColor: '#334155', textColor: '#f8fafc', placeholderColor: '#94a3b8' } };

Theme Switching Animation

Smooth Transitions

// Theme transition styles const themeTransitionStyles = { transition: 'all 0.3s ease-in-out', transitionProperties: [ 'background-color', 'color', 'border-color', 'box-shadow' ] }; // CSS for smooth transitions const themeCSS = ` * { transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out, border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out; } .theme-transition { transition: all 0.3s ease-in-out; } `;

Animated Theme Toggle

// Animated theme toggle component function AnimatedThemeToggle() { const { theme, setTheme, isDark } = useTheme(); const [isAnimating, setIsAnimating] = useState(false); const handleThemeToggle = () => { setIsAnimating(true); // Add transition class document.body.classList.add('theme-transition'); // Toggle theme setTheme(isDark ? 'light' : 'dark'); // Remove transition class after animation setTimeout(() => { document.body.classList.remove('theme-transition'); setIsAnimating(false); }, 300); }; return ( <button onClick={handleThemeToggle} disabled={isAnimating} className="theme-toggle" > {isDark ? '☀️' : '🌙'} </button> ); }

Accessibility

Color Contrast

// WCAG compliant color combinations const accessibleColors = { light: { primary: '#3b82f6', // AA compliant text: '#0f172a', // AAA compliant background: '#ffffff' // AAA compliant }, dark: { primary: '#60a5fa', // AA compliant text: '#f8fafc', // AAA compliant background: '#0f172a' // AAA compliant } }; // Contrast ratio checker const checkContrast = (color1, color2) => { // Calculate contrast ratio const ratio = calculateContrastRatio(color1, color2); return { ratio, isAACompliant: ratio >= 4.5, isAAACompliant: ratio >= 7 }; };

High Contrast Mode

// High contrast theme support const highContrastTheme = { light: { primary: '#000000', background: '#ffffff', text: '#000000', border: '#000000' }, dark: { primary: '#ffffff', background: '#000000', text: '#ffffff', border: '#ffffff' } }; // Detect high contrast preference const useHighContrast = () => { const [isHighContrast, setIsHighContrast] = useState(false); useEffect(() => { const mediaQuery = window.matchMedia('(prefers-contrast: high)'); setIsHighContrast(mediaQuery.matches); const handleChange = (e) => { setIsHighContrast(e.matches); }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, []); return isHighContrast; };

Theme Persistence

Local Storage

// Theme persistence with local storage const usePersistentTheme = () => { const [theme, setTheme] = useState(() => { const saved = localStorage.getItem('veltix-theme'); return saved || 'auto'; }); const updateTheme = useCallback((newTheme) => { setTheme(newTheme); localStorage.setItem('veltix-theme', newTheme); }, []); return { theme, setTheme: updateTheme }; };

Server-side Persistence

// Server-side theme persistence const useServerTheme = (userId) => { const { theme, setTheme } = useTheme(); const saveThemeToServer = useCallback(async (newTheme) => { try { await fetch('/api/user/theme', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ theme: newTheme }) }); } catch (error) { console.error('Failed to save theme:', error); } }, []); const loadThemeFromServer = useCallback(async () => { try { const response = await fetch('/api/user/theme'); const { theme: serverTheme } = await response.json(); setTheme(serverTheme); } catch (error) { console.error('Failed to load theme:', error); } }, [setTheme]); return { theme, setTheme: saveThemeToServer, loadTheme: loadThemeFromServer }; };

Component Theming

Chart Components

// Theme-aware chart components const useThemedChart = () => { const { isDark } = useTheme(); const chartTheme = useMemo(() => ({ colors: { axis: isDark ? '#94a3b8' : '#64748b', grid: isDark ? '#334155' : '#e2e8f0', text: isDark ? '#cbd5e1' : '#475569' }, backgroundColor: isDark ? '#1e293b' : '#ffffff', textColor: isDark ? '#f8fafc' : '#0f172a' }), [isDark]); return chartTheme; }; // Using themed chart function ThemedChart({ data }) { const chartTheme = useThemedChart(); return ( <BarChart data={data} theme={chartTheme} height={300} /> ); }

UI Components

// Theme-aware UI components const useThemedComponent = (componentType) => { const { isDark } = useTheme(); const componentTheme = useMemo(() => { const themes = { card: { light: { backgroundColor: '#ffffff', borderColor: '#e2e8f0', shadow: '0 1px 3px rgba(0, 0, 0, 0.1)' }, dark: { backgroundColor: '#1e293b', borderColor: '#334155', shadow: '0 1px 3px rgba(0, 0, 0, 0.3)' } }, button: { light: { primary: '#3b82f6', text: '#ffffff' }, dark: { primary: '#60a5fa', text: '#0f172a' } } }; return themes[componentType][isDark ? 'dark' : 'light']; }, [componentType, isDark]); return componentTheme; };

Best Practices

1. Color Usage

  • Use semantic color names
  • Ensure sufficient contrast ratios
  • Test themes in different lighting conditions
  • Consider color blindness accessibility

2. Component Design

  • Design components to work with both themes
  • Use CSS variables for dynamic theming
  • Test components in both themes
  • Maintain visual hierarchy

3. Performance

  • Minimize theme switching overhead
  • Use efficient CSS variables
  • Cache theme configurations
  • Optimize theme assets

4. User Experience

  • Provide smooth theme transitions
  • Remember user preferences
  • Support system theme detection
  • Include theme toggle controls

Troubleshooting

Common Issues

Theme not switching

  • Check theme provider setup
  • Verify CSS variable definitions
  • Ensure proper theme registration
  • Check browser storage permissions

Colors not updating

  • Verify CSS variable usage
  • Check theme configuration
  • Ensure proper CSS specificity
  • Test in different browsers

Performance issues

  • Optimize theme switching
  • Use efficient CSS variables
  • Minimize theme complexity
  • Implement proper caching
Last updated on