Dark Mode
Relevant source files This document explains the dark mode implementation in@ui8kit/core, including the theme provider mechanism, state management, persistence strategies, and integration with Tailwind CSS. For general theming and variant styling, see Variant System. For component-specific styling patterns, see Best Practices.
Purpose and Scope
The dark mode system provides automatic theme switching with persistent user preferences and system-level dark mode detection. The implementation is built around a React Context provider pattern that manages theme state, applies CSS classes to the document root, and synchronizes preferences to localStorage. Sources: src/themes/providers/ThemeProvider.tsx1-93ThemeProvider Architecture
TheThemeProvider component wraps the application and manages all theme-related state through React Context. It accepts a theme object conforming to the ThemeBase type and provides theme values and control functions through the context.
- localStorage value at key
"ui:dark"(if present) - System preference via
window.matchMedia('(prefers-color-scheme: dark)') - Default fallback value of
false
Dark Mode State Initialization and Persistence
The dark mode state follows a specific initialization and persistence flow that ensures user preferences are preserved across sessions while respecting system preferences when no explicit preference exists."1" (dark) or "0" (light) in localStorage at key "ui:dark".
Sources: src/themes/providers/ThemeProvider.tsx28-35 src/themes/providers/ThemeProvider.tsx37-44
ThemeContextValue Interface
TheThemeContext provides a typed interface for accessing theme state and control functions. Components consume this context via the useTheme hook.
| Property | Type | Description |
|---|---|---|
theme | T extends ThemeBase | The theme configuration object |
rounded | T['rounded'] | Rounded corner variants from theme |
buttonSize | T['buttonSize'] | Button size variants from theme |
isDarkMode | boolean | Current dark mode state |
isNavFixed | boolean | undefined | Optional navigation fixed state |
prefersReducedMotion | boolean | User’s reduced motion preference |
toggleDarkMode | () => void | Function to toggle dark mode |
setDarkMode | (value: boolean) => void | Function to set dark mode explicitly |
ThemeBase type defines the minimum structure required for theme objects:
Using the useTheme Hook
Components access theme context through theuseTheme hook, which enforces usage within a ThemeProvider and returns the typed context value.
Hook Usage Pattern:
Dark Mode Toggle Implementation
The library provides two methods for controlling dark mode state:toggleDarkMode Function
Inverts the current dark mode state:setDarkMode Function
Sets dark mode to a specific value:Accessibility: Reduced Motion Support
TheThemeProvider detects and tracks the user’s prefers-reduced-motion system preference through the same media query pattern used for dark mode detection.
addListener/removeListener instead of addEventListener/removeEventListener. Components can access this value through the context to conditionally disable animations.
Sources: src/themes/providers/ThemeProvider.tsx46-68
Tailwind CSS Integration
The dark mode system integrates with Tailwind CSS through the.dark class applied to document.documentElement. This requires Tailwind configuration with darkMode: 'class':
isDarkMode is true, the provider adds the dark class to the root element, activating Tailwind’s dark: variant modifiers:
document.documentElement.style.colorScheme to "dark" or "light", which signals the user’s preference to the browser for native form controls and scrollbars.
Sources: src/themes/providers/ThemeProvider.tsx37-44 README.md219-233
Theme Configuration
Applications must wrap their root component withThemeProvider and pass a theme object. The library exports three pre-configured themes:
| Theme Export | Module | Theme Name |
|---|---|---|
modernUITheme | @ui8kit/core | ”modernUI” |
skyOSTheme | @ui8kit/core | ”skyOS” |
lesseUITheme | @ui8kit/core | ”lesseUI” |
ThemeBase type constraint with name, rounded, and buttonSize properties. Custom themes can be created by implementing this interface.
Sources: src/themes/index.ts1-9 README.md223-233
Server-Side Rendering Considerations
The implementation includes SSR-safe checks usingtypeof window !== 'undefined' and typeof document === 'undefined' guards to prevent runtime errors during server rendering. Initial state computation avoids accessing browser APIs until the client hydration phase.
The lazy state initializer pattern ensures that:
- State initialization logic runs only once during mount
- Browser API access is safely guarded
- SSR environments receive sensible defaults (
falsefor dark mode)