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:
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:
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
- Complete Application - See theming system in full app context
- Layout System - How themes integrate with layout components
- Authentication - Themed authentication forms and components
- Data Binding - Theme-aware data-bound components
- Component Overview - All components with theming support