A beautiful demonstration of dynamic theming with React, TypeScript, and Tailwind CSS. This project showcases a sophisticated theme system that allows real-time theme switching with smooth transitions and CSS custom properties.
theme-demo-recording.mov
Watch the demo in action - real-time theme switching with performance benchmarking
- Dynamic Theme Switching: Real-time theme changes with smooth transitions
- CSS Custom Properties: Uses CSS variables for efficient theme management
- TypeScript: Fully typed for better development experience
- Tailwind CSS: Modern utility-first CSS framework
- Responsive Design: Mobile-first responsive layout using CSS Grid
- Theme Context: React Context API for global theme state management
- Transition Animations: Smooth loading transitions between themes
- Contrast Calculation: Automatic text color calculation based on background luminance
- Performance Benchmarking: Comprehensive comparison between context vs token-based theming
- Real-time Metrics: Live performance measurements with P50/P99 percentiles
- Expandable Results: Detailed individual measurement breakdowns
- Stress Testing: Automated performance testing with rapid theme changes
This project demonstrates two distinct approaches to theme consumption with strict separation:
- ONLY use CSS custom properties:
bg-[var(--primary-color)],text-[var(--text-color)] - NEVER import or use the centralized styling system (which contains CSS token references)
- NEVER access the theme context directly
- Use inline Tailwind classes with CSS variables
- ONLY use stateful theme values:
style={{ backgroundColor: theme.primaryColor }} - NEVER use CSS custom properties or the centralized styling system
- NEVER use CSS token references like
var(--primary-color) - Access theme through React context:
const { theme } = useTheme()
- Can use the centralized styling system
- Can mix both approaches as needed
- Not bound by the strict separation requirements
The project uses a centralized, consistent styling strategy that provides scalable, maintainable styling while respecting theme separation:
- Centralized Style Definitions: All common styles are defined in
src/utils/styling.ts - Type Safety: Full TypeScript support with proper type checking
- Theme Awareness: Styles automatically adapt to the current theme
- Consistency: Reusable patterns across all components
- Maintainability: Single source of truth for styling decisions
- Separation of Concerns: Strict adherence to theme consumption patterns
import { cn } from "../utils/styling";
// Combines class names with proper type safety
const className = cn(
"base-class",
condition && "conditional-class",
"another-class"
);import { cardStyles, buildButtonStyles } from "../utils/styling";
// Card styles
<div className={cardStyles.base}>
<div className={cardStyles.content}>
<h2 className={cardStyles.title}>Title</h2>
</div>
</div>
// Button styles with variants
<button className={buildButtonStyles("primary")}>Click me</button>import { themeAwareStyles } from '../utils/styling';
// Automatically adapts to current theme
<span className={themeAwareStyles.text.primary}>Primary text</span>
<span className={themeAwareStyles.text.secondary}>Secondary text</span>- Core Components:
cardStyles,buttonStyles,sectionStyles,gridStyles - UI Elements:
formStyles,statusStyles,infoStyles - Theme Integration:
themeAwareStyles,animationStyles,responsiveStyles - Builder Functions:
buildCardStyles(),buildButtonStyles(),buildSectionStyles()
The codebase has been refactored to improve maintainability while preserving the core theme separation:
- Eliminated Duplicate Code: Single source of truth for theme definitions and validation
- Shared Components: Unified
SwatchandButtonBasecomponents for both consumer types - Centralized Validation: Comprehensive validation utilities in
src/utils/validation.ts - Centralized Theme Application: Unified theme application logic in
src/utils/themeApplication.ts - Enhanced Type Safety: Shared TypeScript interfaces and consistent prop types
- Unified color swatch for both StatefulTheme and Token consumers
- Accepts either
color(for StatefulTheme) orcssVariable(for Token consumers) - Maintains strict separation between approaches
- Shared base for all button components
- Common button styles and behavior
- TypeScript interfaces for consistent props
- Reduced Code Duplication: Eliminated ~100 lines of duplicate code
- Improved Maintainability: Centralized validation and shared components
- Enhanced Type Safety: Shared interfaces and consistent prop types
- Better Code Organization: Logical grouping of related utilities
- Preserved Architecture: Maintained strict separation between consumer types
- Node.js (v18 or higher)
- npm or yarn
- Clone the repository:
git clone <repository-url>
cd theme-demo- Install dependencies:
npm install- Start the development server:
npm run dev- Open your browser and navigate to
http://localhost:5173
npm run dev- Start development server with hot reloadnpm run build- Build for productionnpm run preview- Preview production buildnpm run lint- Run ESLint
The theme system is built around CSS custom properties and provides:
interface Theme {
primaryColor: string; // Primary brand color
secondaryColor: string; // Secondary brand color
backgroundColor: string; // Main background color
textColor: string; // Main text color (auto-calculated)
}- ThemeProvider: Global theme context provider
- ThemeControls: UI for theme switching and customization
- TokenConsumerCard: Demonstrates theme token consumption
- StatefulConsumerCard: Shows stateful theme usage
- TransitionModal: Smooth loading transitions
- BenchmarkDisplay: Real-time performance comparison interface
import { useTheme } from "./hooks/useTheme";
function MyComponent() {
const { theme, applyTheme } = useTheme();
const changeToBlueTheme = () => {
applyTheme({
primaryColor: "#2563eb",
backgroundColor: "#0f172a",
});
};
return (
<div style={{ backgroundColor: theme.backgroundColor }}>
<h1 style={{ color: theme.textColor }}>Hello World</h1>
</div>
);
}src/
βββ components/ # React components
β βββ ThemeControls.tsx
β βββ TokenConsumerCard.tsx
β βββ StatefulConsumerCard.tsx
β βββ TransitionModal.tsx
β βββ BenchmarkDisplay.tsx
β βββ ui/ # Reusable UI components
β βββ Button.tsx
β βββ ButtonBase.tsx
β βββ Swatch.tsx
β βββ ThemeButton.tsx
β βββ TokenButton.tsx
βββ context/ # React Context providers
β βββ ThemeContext.tsx
β βββ ThemeContextValue.ts
βββ hooks/ # Custom React hooks
β βββ useTheme.ts
βββ constants/ # Application constants
β βββ index.ts
β βββ themes.ts
βββ types/ # TypeScript type definitions
β βββ index.ts
βββ utils/ # Utility functions
β βββ themeUtils.ts
β βββ benchmarkUtils.ts
β βββ styling.ts
β βββ validation.ts
β βββ themeApplication.ts
βββ App.tsx # Main application component
- React 19 - Latest React with concurrent features
- TypeScript - Type-safe JavaScript
- Tailwind CSS v4 - Utility-first CSS framework
- Vite - Fast build tool and dev server
- CSS Custom Properties - Dynamic CSS variables
- CSS Grid - Modern layout system
- Performance API - High-precision timing measurements
- Define your theme in
src/constants/themes.ts:
export const BENCHMARK_THEMES = {
solarized: {
primaryColor: "#268bd2",
secondaryColor: "#2aa198",
backgroundColor: "#002b36",
textColor: "#ffffff",
},
light: {
primaryColor: "#1d4ed8",
secondaryColor: "#059669",
backgroundColor: "#ffffff",
textColor: "#000000",
},
// Add more themes as needed...
};- Use the theme in your components:
const { applyTheme } = useTheme();
applyTheme(BENCHMARK_THEMES.solarized);The benchmark system can be customized by modifying src/utils/benchmarkUtils.ts:
- Measurement Frequency: Adjust update intervals for real-time stats
- Percentile Calculations: Modify P50/P99 calculation methods
- Performance Thresholds: Change color coding thresholds for results
The project uses Tailwind CSS with CSS custom properties. Theme colors are applied through CSS variables:
:root {
--primary-color: #2563eb;
--secondary-color: #22c55e;
--background-color: #0f172a;
--text-color: #ffffff;
}The application includes a comprehensive benchmark system to compare the performance of different theming approaches:
- Real-time Measurement: Live performance tracking during theme changes
- Dual Comparison: Context-based vs CSS token-based theming
- Statistical Analysis: P50 (median) and P99 percentile calculations
- Expandable Results: Detailed breakdown of individual measurements
- Stress Testing: Automated rapid theme switching for intensive testing
- Start Benchmark: Click "Start Benchmark" to begin collecting data
- Single Test: Use "Single Test" for individual theme changes
- Stress Test: Use "Stress Test (5s)" for intensive performance testing
- View Results: Expand the results sections to see detailed measurements
- Compare Performance: See which approach (context vs tokens) performs better
- Context Approach: Full React re-render cycle including state updates and reconciliation
- Token Approach: Direct CSS variable updates with DOM reflow costs
- Real-world Performance: Actual user experience differences between approaches
The benchmark results can vary based on system conditions and load:
- Normal conditions: TokenConsumerCard typically wins because direct DOM manipulation is faster than React's render cycle
- High system load (e.g., video recording): StatefulConsumerCard might win because browser optimizations for CSS variables can be affected by system pressure, while React's batching might be more consistent
- System Load: Additional processes (like video recording) increase system load, which can affect React's rendering performance more than direct DOM manipulation
- Browser Optimization: CSS variable updates are often batched and optimized by the browser, while React re-renders involve more JavaScript execution
- Measurement Timing: TokenConsumerCard measures isolated DOM operations, while StatefulConsumerCard measures the entire React render cycle
- Memory Pressure: High system load increases memory pressure, which can impact React's reconciliation process more than direct DOM operations
This variation demonstrates the real-world complexity of performance benchmarking - results can fluctuate based on system conditions, making it important to run benchmarks multiple times under different conditions.
The application uses CSS Grid for layout and is fully responsive:
- Mobile: Single column layout
- Tablet: Two-column grid layout
- Desktop: Full-width layout with max-width containers
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Vite
- Styled with Tailwind CSS
- Powered by React