Theming System (Dark/Light Mode)
Relevant source files
The following files were used as context for generating this wiki page:
The Wasalny landing page implements a robust theming system that supports Light, Dark, and System-preference modes. This system is built using React Context for state management, Tailwind CSS v4 for styling via CSS variables (CSS Custom Properties), and GSAP for animated transitions between states.
The primary goal of the theming system is to provide a consistent visual experience that respects user preferences while maintaining high contrast and accessibility. It utilizes a class-based strategy where a .dark class is applied to the root HTML element to toggle between themes.
Sources: src/context/ThemeContext.tsx:1-10, src/index.css:3-5, AGENTS.md:52-54
Architecture and Data Flow
The theming system is centralized around the ThemeContext, which serves as the single source of truth for the application's visual state. The ThemeProvider wraps the entire application in App.tsx, ensuring that theme data is available to all components.
Theme Logic Flow
The system monitors three possible states: light, dark, and system. When set to system, the application uses the window.matchMedia API to detect the operating system's color scheme preference.
The logic above ensures that theme changes are persisted across sessions and react dynamically to OS changes if the user selects the "System" option.
Sources: src/context/ThemeContext.tsx:16-55, src/App.tsx:28-30
Key Components
ThemeProvider
The ThemeProvider component manages the state and persistence of the theme. It uses localStorage with the key wasalny-theme to store the user's choice. It also exposes a resolvedTheme which simplifies conditional logic for components by translating 'system' into either 'light' or 'dark' based on active environment conditions.
| Property | Type | Description |
|---|---|---|
theme | Theme | The raw theme setting (light, dark, or system). |
setTheme | function | Updates the theme and persists it to localStorage. |
resolvedTheme | 'light' | 'dark' | The actual theme being rendered. |
Sources: src/context/ThemeContext.tsx:8-13, src/context/ThemeContext.tsx:25-30
ThemeToggle
Located in the Header, the ThemeToggle component provides the UI for users to switch themes. It consists of a segmented control with three options: Light (Sun icon), Dark (Moon icon), and System (Monitor icon).
The toggle uses GSAP to animate a background indicator (indicatorRef) with a back.out easing effect when a new theme is selected.
Sources: src/components/ui/ThemeToggle.tsx:10-40, src/components/layout/Header.tsx:205-210
Styling and CSS Variables
The system relies on Tailwind CSS v4 and CSS variables defined in src/index.css. The application uses a "semantic token" approach where colors are defined by their role (e.g., --background, --foreground) rather than specific color names.
Color Tokens Mapping
| Token | Light Mode (HSL) | Dark Mode (HSL) |
|---|---|---|
--background | 220 20% 97% | 222.2 84% 4.9% |
--foreground | 222.2 84% 4.9% | 210 40% 98% |
--card | 0 0% 100% | 217.2 32.6% 12% |
--border | 220 13% 88% | 217.2 32.6% 20% |
Sources: src/index.css:73-95
Implementation Detail
The dark variant is configured using a custom selector:
@custom-variant dark (&:where(.dark, .dark *));
This allows components to use the dark: prefix in Tailwind classes. For example, a card might use bg-[hsl(var(--card))] which automatically shifts colors when the parent .dark class is toggled.
Sources: src/index.css:4-5, src/index.css:126-128
Theme Transitions
To ensure a smooth user experience, the system implements CSS transitions on the body element for color and background properties.
body {
@apply bg-[hsl(var(--background))] text-[hsl(var(--foreground))];
transition: background-color 0.3s ease, color 0.3s ease;
}
Additionally, components like Header.tsx use backdrop-blur and varying opacities (bg-white/90 vs bg-gray-900/90) that adjust based on the active theme to maintain legibility.
Sources: src/index.css:101-105, src/components/layout/Header.tsx:185-188
Conclusion
The theming system in the Wasalny landing page is a specialized implementation that combines React's state management with CSS Custom Properties. By using a class-based approach and semantic tokens, the project achieves a highly maintainable architecture where adding or modifying themes requires minimal changes to individual component logic. The inclusion of system-preference detection ensures the application feels native to the user's operating system environment.