Skip to main content

Theming System

QwickApps React Framework provides a comprehensive theming system that enables seamless light/dark mode switching, multiple color palettes, and consistent design tokens throughout your application. The system automatically synchronizes with Material-UI themes and provides persistent user preferences.

Core Theming Features

🌓 Theme Modes

  • Light Mode - Clean, bright interface perfect for daytime use
  • Dark Mode - Easy on the eyes for low-light environments
  • System Mode - Automatically follows the user's operating system preference
  • Real-time Switching - Instant theme changes without page reload

🎨 Color Palettes

  • Multiple Palettes - Pre-designed color schemes for different moods and brands
  • Dynamic Switching - Change color palettes instantly across the entire application
  • Theme Compatibility - All palettes work seamlessly in both light and dark modes
  • Custom Variables - CSS custom properties for consistent color usage

♿ Accessibility Integration

  • Automatic Contrast Checking - Built-in accessibility monitoring
  • WCAG Compliance - Colors meet accessibility standards
  • High Contrast Support - Enhanced visibility options
  • Screen Reader Support - Proper ARIA labels and semantic structure

💾 Persistent Preferences

  • localStorage Integration - User preferences saved across sessions
  • Per-app Storage - Isolated preferences using unique app identifiers
  • Instant Restoration - Preferences applied immediately on app load

Theme Switcher

Professional theme switching interface with light, dark, and system options:

Interactive Demo: Use the Storybook controls at the top to customize this component.Open in New Tab →

Theme Switcher Features

🎯 User Experience:

  • Visual Indicators - Clear indication of current theme mode
  • Smooth Transitions - Seamless switching between modes
  • Keyboard Navigation - Full accessibility support
  • Tooltip Guidance - Helpful descriptions for each mode

🔧 Integration:

  • Context Management - Works with ThemeContext for state management
  • Material-UI Sync - Automatic synchronization with MUI theme system
  • CSS Variables - Updates design tokens throughout the application
  • Component Library - All Framework components automatically adapt

Theme Switcher Implementation

import { ThemeSwitcher, useTheme } from '@qwickapps/react-framework';

function AppHeader() {
const { currentTheme, actualThemeMode, setPreferredTheme } = useTheme();

return (
<header style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px'
}}>
<h1>My Application</h1>

<div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
{/* Show current theme status */}
<span>
Theme: {currentTheme}{actualThemeMode}
</span>

{/* Theme switcher component */}
<ThemeSwitcher />

{/* Manual theme switching */}
<button onClick={() => setPreferredTheme('light')}>
Force Light
</button>
<button onClick={() => setPreferredTheme('dark')}>
Force Dark
</button>
<button onClick={() => setPreferredTheme('system')}>
Follow System
</button>
</div>
</header>
);
}

Palette Switcher

Dynamic color palette switching for instant visual customization:

Interactive Demo: Use the Storybook controls at the top to customize this component.Open in New Tab →

Palette Features

🌈 Color Palettes:

  • Blue Palette - Professional and trustworthy
  • Green Palette - Natural and calming
  • Purple Palette - Creative and innovative
  • Orange Palette - Energetic and warm
  • Red Palette - Bold and attention-grabbing
  • Teal Palette - Modern and sophisticated

🎨 Visual Feedback:

  • Live Preview - See color changes instantly
  • Color Swatches - Visual representation of each palette
  • CSS Variables - Real-time updates to design tokens
  • Accessibility Monitoring - Automatic contrast validation

Palette Switcher Implementation

import { PaletteSwitcher, usePalette } from '@qwickapps/react-framework';

function UserPreferences() {
const {
currentPalette,
availablePalettes,
setPreferredPalette
} = usePalette();

return (
<div style={{ padding: '24px' }}>
<h2>Customize Appearance</h2>

{/* Current palette info */}
<div style={{ marginBottom: '24px' }}>
<p>Current Palette: <strong>{currentPalette}</strong></p>
<p>Available Options: {availablePalettes.length} palettes</p>
</div>

{/* Built-in palette switcher */}
<div style={{ marginBottom: '24px' }}>
<h3>Quick Palette Switch:</h3>
<PaletteSwitcher />
</div>

{/* Custom palette selection */}
<div>
<h3>All Available Palettes:</h3>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
gap: '16px'
}}>
{availablePalettes.map((palette) => (
<button
key={palette.id}
onClick={() => setPreferredPalette(palette.id)}
style={{
padding: '16px',
border: currentPalette === palette.id ? '2px solid var(--palette-primary-main)' : '1px solid var(--palette-border-main)',
borderRadius: '8px',
backgroundColor: 'var(--palette-surface-main)',
cursor: 'pointer'
}}
>
<div style={{
width: '100%',
height: '32px',
backgroundColor: palette.colors.primary,
borderRadius: '4px',
marginBottom: '8px'
}} />
<div style={{ color: 'var(--palette-text-primary)', fontWeight: 'bold' }}>
{palette.name}
</div>
</button>
))}
</div>
</div>
</div>
);
}

CSS Variables Integration

The theming system automatically updates CSS custom properties for consistent styling:

/* These variables are automatically updated when themes/palettes change */

/* Primary Colors */
--palette-primary-main: #1976d2;
--palette-primary-dark: #1565c0;
--palette-primary-light: #42a5f5;

/* Secondary Colors */
--palette-secondary-main: #dc004e;
--palette-secondary-dark: #9a0036;
--palette-secondary-light: #e33371;

/* Background Colors */
--palette-background-main: #ffffff;
--palette-surface-main: #f5f5f5;
--palette-paper-main: #ffffff;

/* Text Colors */
--palette-text-primary: #212121;
--palette-text-secondary: #757575;
--palette-text-disabled: #bdbdbd;

/* Border Colors */
--palette-border-main: #e0e0e0;
--palette-border-light: #f5f5f5;
--palette-divider: #e0e0e0;

/* Semantic Colors */
--palette-success-main: #4caf50;
--palette-warning-main: #ff9800;
--palette-error-main: #f44336;
--palette-info-main: #2196f3;

Using CSS Variables

import { Box, Typography } from '@mui/material';

function CustomStyledComponent() {
return (
<Box
sx={{
// Use CSS variables for consistent theming
backgroundColor: 'var(--palette-surface-main)',
border: '1px solid var(--palette-border-main)',
borderRadius: 2,
p: 3,

// Variables automatically update with theme/palette changes
color: 'var(--palette-text-primary)',

'&:hover': {
backgroundColor: 'var(--palette-background-main)',
borderColor: 'var(--palette-primary-main)'
}
}}
>
<Typography
variant="h6"
sx={{ color: 'var(--palette-text-primary)', mb: 1 }}
>
Custom Component
</Typography>

<Typography
variant="body2"
sx={{ color: 'var(--palette-text-secondary)' }}
>
This component uses CSS variables that automatically update
when users switch themes or color palettes.
</Typography>

<div
style={{
marginTop: '16px',
padding: '12px',
backgroundColor: 'var(--palette-primary-main)',
color: 'white',
borderRadius: '4px',
textAlign: 'center'
}}
>
Primary Color Showcase
</div>
</Box>
);
}

Theme Context Integration

Access theme state and controls throughout your application:

import { useTheme, usePalette } from '@qwickapps/react-framework';

function ThemeAwareComponent() {
const {
currentTheme, // 'light', 'dark', or 'system'
actualThemeMode, // 'light' or 'dark' (resolved from system)
setPreferredTheme, // Function to change theme
isSystemTheme // Boolean: is using system theme
} = useTheme();

const {
currentPalette, // Current palette ID (e.g., 'blue')
availablePalettes, // Array of available palette objects
setPreferredPalette, // Function to change palette
getPaletteColors // Function to get palette colors
} = usePalette();

// Get colors from current palette
const colors = getPaletteColors(currentPalette);

return (
<div style={{
padding: '24px',
backgroundColor: colors.surface,
color: colors.text.primary,
borderRadius: '8px'
}}>
<h3>Theme Status</h3>

<div style={{ marginBottom: '16px' }}>
<p><strong>Theme Mode:</strong> {currentTheme}</p>
<p><strong>Actual Mode:</strong> {actualThemeMode}</p>
<p><strong>Using System:</strong> {isSystemTheme ? 'Yes' : 'No'}</p>
</div>

<div style={{ marginBottom: '16px' }}>
<p><strong>Current Palette:</strong> {currentPalette}</p>
<p><strong>Available Palettes:</strong> {availablePalettes.length}</p>
</div>

<div style={{
display: 'flex',
gap: '12px',
flexWrap: 'wrap'
}}>
<button
onClick={() => setPreferredTheme('light')}
style={{
padding: '8px 16px',
backgroundColor: colors.primary,
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Light Theme
</button>

<button
onClick={() => setPreferredTheme('dark')}
style={{
padding: '8px 16px',
backgroundColor: colors.secondary,
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Dark Theme
</button>

<button
onClick={() => setPreferredTheme('system')}
style={{
padding: '8px 16px',
backgroundColor: colors.info,
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
System Theme
</button>
</div>
</div>
);
}

Accessibility Features

Automatic Contrast Checking

The theming system includes built-in accessibility monitoring:

import { AccessibilityChecker } from '@qwickapps/react-framework';

function AccessibilityAwareComponent() {
return (
<div style={{
padding: '24px',
backgroundColor: 'var(--palette-surface-main)',
border: '1px solid var(--palette-border-main)',
borderRadius: '8px'
}}>
<h3 style={{ color: 'var(--palette-text-primary)' }}>
Accessibility Monitoring
</h3>

<p style={{ color: 'var(--palette-text-secondary)' }}>
The AccessibilityChecker automatically monitors color contrast
ratios and provides real-time feedback as you switch themes
and palettes.
</p>

{/* Automatic accessibility checking */}
<AccessibilityChecker />

<div style={{ marginTop: '16px' }}>
<h4 style={{ color: 'var(--palette-text-primary)' }}>
Accessibility Features:
</h4>
<ul style={{ color: 'var(--palette-text-secondary)' }}>
<li>WCAG AA contrast ratio compliance</li>
<li>Real-time color contrast validation</li>
<li>Automatic high contrast detection</li>
<li>Screen reader friendly markup</li>
<li>Keyboard navigation support</li>
</ul>
</div>
</div>
);
}

High Contrast Support

import { useTheme } from '@qwickapps/react-framework';

function HighContrastComponent() {
const { actualThemeMode } = useTheme();

// Automatically adjust for high contrast
const highContrastStyles = {
padding: '24px',
backgroundColor: actualThemeMode === 'dark' ? '#000000' : '#ffffff',
color: actualThemeMode === 'dark' ? '#ffffff' : '#000000',
border: `2px solid ${actualThemeMode === 'dark' ? '#ffffff' : '#000000'}`,
borderRadius: '8px'
};

return (
<div style={highContrastStyles}>
<h3>High Contrast Mode</h3>
<p>
This content automatically adjusts for high contrast when
using system themes that prefer reduced transparency.
</p>
</div>
);
}

Advanced Theming Patterns

Custom Theme Provider

import { QwickApp, DataProvider } from '@qwickapps/react-framework';

function CustomThemedApp() {
return (
<QwickApp
appId="com.example.custom-theme"
defaultTheme="system" // Start with system preference
defaultPalette="blue" // Default to blue palette
enableAccessibilityChecker={true} // Enable accessibility monitoring
>
<DataProvider dataSource={{ dataProvider: myDataProvider }}>
<MyApplication />
</DataProvider>
</QwickApp>
);
}

Dynamic Palette Creation

import { usePalette } from '@qwickapps/react-framework';

function DynamicPaletteComponent() {
const { setCustomPalette } = usePalette();

const createBrandPalette = () => {
const brandColors = {
primary: '#1e3a8a', // Brand blue
secondary: '#dc2626', // Brand red
success: '#16a34a', // Success green
warning: '#ea580c', // Warning orange
error: '#dc2626', // Error red
info: '#0284c7', // Info blue
background: '#f8fafc', // Light background
surface: '#ffffff', // Surface white
text: {
primary: '#1e293b', // Dark text
secondary: '#64748b' // Secondary text
}
};

setCustomPalette('brand', brandColors, 'Brand Colors');
};

return (
<button onClick={createBrandPalette}>
Create Brand Palette
</button>
);
}

Theme-Aware Component Library

import { useTheme, usePalette } from '@qwickapps/react-framework';

function ThemedButton({
children,
variant = 'primary',
size = 'medium',
onClick
}) {
const { actualThemeMode } = useTheme();
const { getPaletteColors } = usePalette();

const colors = getPaletteColors();

const getButtonStyles = () => {
const baseStyles = {
padding: size === 'small' ? '8px 16px' : size === 'large' ? '16px 32px' : '12px 24px',
borderRadius: '6px',
border: 'none',
cursor: 'pointer',
fontWeight: 'bold',
fontSize: size === 'small' ? '0.875rem' : size === 'large' ? '1.125rem' : '1rem',
transition: 'all 0.2s ease-in-out'
};

switch (variant) {
case 'primary':
return {
...baseStyles,
backgroundColor: colors.primary,
color: 'white',
':hover': {
backgroundColor: actualThemeMode === 'dark' ? colors.primary + '20' : colors.primary + '80'
}
};
case 'secondary':
return {
...baseStyles,
backgroundColor: colors.secondary,
color: 'white'
};
case 'outlined':
return {
...baseStyles,
backgroundColor: 'transparent',
color: colors.primary,
border: `2px solid ${colors.primary}`
};
default:
return baseStyles;
}
};

return (
<button
style={getButtonStyles()}
onClick={onClick}
>
{children}
</button>
);
}

Performance Optimization

CSS Variable Caching

import { useEffect, useMemo } from 'react';
import { usePalette, useTheme } from '@qwickapps/react-framework';

function PerformantThemedComponent() {
const { currentPalette } = usePalette();
const { actualThemeMode } = useTheme();

// Memoize expensive style calculations
const computedStyles = useMemo(() => {
return {
container: {
'--local-bg': actualThemeMode === 'dark' ? '#1a1a1a' : '#ffffff',
'--local-text': actualThemeMode === 'dark' ? '#ffffff' : '#000000',
'--local-accent': `var(--palette-${currentPalette}-main)`
},
content: {
backgroundColor: 'var(--local-bg)',
color: 'var(--local-text)',
borderColor: 'var(--local-accent)'
}
};
}, [currentPalette, actualThemeMode]);

return (
<div style={{...computedStyles.container, ...computedStyles.content}}>
<h3>Performance Optimized Component</h3>
<p>Styles are computed once and cached until theme changes.</p>
</div>
);
}

Lazy Theme Loading

import { lazy, Suspense } from 'react';
import { useTheme } from '@qwickapps/react-framework';

// Lazy load theme-specific components
const LightThemeComponent = lazy(() => import('./LightThemeComponent'));
const DarkThemeComponent = lazy(() => import('./DarkThemeComponent'));

function ConditionalThemedComponent() {
const { actualThemeMode } = useTheme();

return (
<Suspense fallback={<div>Loading theme...</div>}>
{actualThemeMode === 'light' ? (
<LightThemeComponent />
) : (
<DarkThemeComponent />
)}
</Suspense>
);
}

Learn More