Theme System
Ever Works includes a professional, flexible theme system that allows users to customize the appearance of your directory website.
Overviewβ
The theme system provides:
- π¨ Multiple pre-built themes - EverWorks, Corporate, Material, Funny
- π Dynamic theme switching - Change themes without page reload
- πΎ Persistent preferences - Themes saved in localStorage
- βΏ Accessible - ARIA labels and keyboard navigation
- π― Type-safe - Full TypeScript support
- β‘ Performant - Optimized with React.memo and useMemo
Architectureβ
Componentsβ
-
Context Layer (
components/context/LayoutThemeContext.tsx)- Centralized state management
- CSS custom properties injection
- Local storage persistence
-
Custom Hook (
hooks/useTheme.ts)- Business logic abstraction
- Memoized computations
- Clean API for theme operations
-
UI Components (
components/header/ThemeSwitch.tsx)- Theme switcher interface
- Visual theme previews
- Accessibility compliance
-
Utility Functions (
lib/theme-utils.ts)- CSS class builders
- Theme color getters
- Helper functions
Available Themesβ
| Theme | Description | Primary Color | Use Case |
|---|---|---|---|
| EverWorks | Default modern theme | Blue (#0070f3) | General purpose |
| Corporate | Professional business theme | Navy (#0056b3) | Business directories |
| Material | Google Material Design | Indigo (#3f51b5) | Modern apps |
| Funny | Playful, colorful theme | Purple (#9c27b0) | Creative projects |
Usageβ
Basic Theme Switchingβ
import { ThemeSwitcher } from "@/components/header/ThemeSwitch";
function Header() {
return (
<header>
<ThemeSwitcher />
</header>
);
}
Compact Theme Selectorβ
<ThemeSwitcher compact className="w-full" />
Using Theme in Componentsβ
import { useTheme } from "@/hooks/useTheme";
function MyComponent() {
const { currentThemeInfo, isThemeActive, changeTheme } = useTheme();
return (
<div style={{ color: currentThemeInfo.colors.primary }}>
<h2>Current theme: {currentThemeInfo.label}</h2>
<button onClick={() => changeTheme('corporate')}>
Switch to Corporate
</button>
</div>
);
}
CSS Custom Propertiesβ
The system automatically injects CSS variables:
:root {
--theme-primary: #0070f3;
--theme-secondary: #00c853;
--theme-accent: #0056b3;
--theme-background: #ffffff;
--theme-surface: #f5f5f5;
--theme-text: #333333;
--theme-text-secondary: #666666;
}
Use in your CSS:
.my-element {
background-color: var(--theme-primary);
color: var(--theme-text);
border: 1px solid var(--theme-accent);
}
Theme Configurationβ
Theme Type Definitionβ
export type ThemeKey = "everworks" | "corporate" | "material" | "funny";
export interface ThemeConfig {
readonly primary: string;
readonly secondary: string;
readonly accent: string;
readonly background: string;
readonly surface: string;
readonly text: string;
readonly textSecondary: string;
}
useTheme Hook APIβ
const {
themeKey, // Current active theme key
currentTheme, // Current theme configuration
currentThemeInfo, // Theme metadata (label, description, colors)
availableThemes, // Array of all available themes
changeTheme, // Function to change theme
isThemeActive, // Check if a theme is active
getThemeInfo, // Get info for a specific theme
} = useTheme();
Creating Custom Themesβ
Step 1: Define Theme Configurationβ
// In components/context/LayoutThemeContext.tsx
const THEMES: Record<ThemeKey, ThemeConfig> = {
// ... existing themes
custom: {
primary: "#ff6b6b",
secondary: "#4ecdc4",
accent: "#ffe66d",
background: "#ffffff",
surface: "#f7f7f7",
text: "#2d3436",
textSecondary: "#636e72",
},
};
Step 2: Add Theme Metadataβ
const THEME_INFO: Record<ThemeKey, ThemeInfo> = {
// ... existing themes
custom: {
key: "custom",
label: "Custom Theme",
description: "Your custom theme",
colors: THEMES.custom,
},
};
Step 3: Update Typeβ
export type ThemeKey = "everworks" | "corporate" | "material" | "funny" | "custom";
Best Practicesβ
1. Component Developmentβ
- Always use the
useThemehook for theme operations - Memoize expensive computations with
useMemo - Follow accessibility guidelines (ARIA labels, keyboard navigation)
- Use TypeScript strictly for type safety
2. Stylingβ
- Prefer CSS custom properties over hardcoded colors
- Use utility classes from
theme-utils.ts - Maintain consistent spacing and sizing
- Support both light and dark modes
3. Performanceβ
- Avoid unnecessary re-renders with
React.memo - Implement proper dependency arrays in hooks
- Monitor bundle size impact
- Use
useCallbackfor stable function references
Troubleshootingβ
Hydration Mismatchβ
Issue: Theme flickers on page load
Solution: Use mounted state to prevent SSR issues
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return null;
CSS Variables Not Applyingβ
Issue: Theme colors not updating
Solution: Check CSS custom property injection in context
// Verify in browser DevTools
getComputedStyle(document.documentElement).getPropertyValue('--theme-primary')
localStorage Not Persistingβ
Issue: Theme resets on page reload
Solution: Verify localStorage is available and not blocked
try {
localStorage.setItem('theme', themeKey);
} catch (error) {
console.error('localStorage not available:', error);
}
Next Stepsβ
- Dynamic Colors - Learn about the dynamic color system
- Customization - General customization guide
- Development - Set up your development environment