Theme Utility
Description
The theme utility provides a centralized way to manage the application's theme state. It handles theme persistence via localStorage, automatic system preference detection, and reactive updates across components. This utility works independently of the ThemeSwitcher component, allowing any part of your application to read, set, or subscribe to theme changes.
Basic Usage
Import and use the theme utility to manage application themes:
import theme from './utils/theme.js';
// Get current theme
const currentTheme = theme.get(); // 'auto', 'light', or 'dark'
// Set theme
theme.set('dark');
// Subscribe to theme changes
const unsubscribe = theme.subscribe((newTheme) => {
console.log('Theme changed to:', newTheme);
});
API Methods
theme.get()
Returns the current theme value.
Returns: string
One of: 'auto', 'light', or 'dark'
const currentTheme = theme.get();
console.log(currentTheme); // 'auto', 'light', or 'dark'
theme.set(value)
Sets the theme and automatically persists it to localStorage and updates the DOM.
value: string
The theme to set. Must be one of: 'auto', 'light', or 'dark'
theme.set('dark');
theme.subscribe(callback)
Subscribes to theme changes. The callback is immediately invoked with the current theme, then called again whenever the theme changes.
callback: Function
Function to call when the theme changes. Receives the new theme value as an argument.
Returns: Function
An unsubscribe function to remove the subscription.
const unsubscribe = theme.subscribe((newTheme) => {
console.log('Theme is now:', newTheme);
});
// Later, stop listening
unsubscribe();
theme.getCalculated()
Returns the effective theme, resolving 'auto' to either 'light' or 'dark' based on the user's system preference.
Returns: string
Either 'light' or 'dark'
const effectiveTheme = theme.getCalculated();
console.log(effectiveTheme); // Always 'light' or 'dark'
Examples
Reading Theme Without Component
import theme from './utils/theme.js';
// Get theme anywhere in your app
const currentTheme = theme.get();
const isDark = theme.getCalculated() === 'dark';
if(isDark){
// Apply dark-specific logic
}
Component Subscribing to Theme
import { LitElement, html } from 'lit';
import theme from './utils/theme.js';
class MyComponent extends LitElement {
static properties = {
theme: { type: String }
};
connectedCallback() {
super.connectedCallback();
this.unsubscribe = theme.subscribe((t) => {
this.theme = t;
});
}
disconnectedCallback() {
super.disconnectedCallback();
if(this.unsubscribe) this.unsubscribe();
}
render() {
return html`<p>Current theme: ${this.theme}</p>`;
}
}
Setting Theme Programmatically
import theme from './utils/theme.js';
// Set theme based on user preference from API
const userPreferences = await fetchUserPreferences();
theme.set(userPreferences.theme);
// Toggle between light and dark
const toggleTheme = () => {
const current = theme.get();
if(current === 'dark') theme.set('light');
else theme.set('dark');
};
Working Example
<div id="theme-demo">
<p>Current theme: <strong id="theme-display">auto</strong></p>
<p>Calculated: <strong id="calculated-display">dark</strong></p>
<button id="set-auto">Auto</button>
<button id="set-light">Light</button>
<button id="set-dark">Dark</button>
</div>
<script type="module">
import theme from '../src/utils/theme.js';
// Subscribe to updates
theme.subscribe((t) => {
document.getElementById('theme-display').textContent = t;
document.getElementById('calculated-display').textContent =
theme.getCalculated();
});
// Button handlers
document.getElementById('set-auto')
.addEventListener('click', () => theme.set('auto'));
document.getElementById('set-light')
.addEventListener('click', () => theme.set('light'));
document.getElementById('set-dark')
.addEventListener('click', () => theme.set('dark'));
</script>
Current theme: auto
Calculated: dark
Automatic Features
localStorage Persistence
The theme utility automatically saves the current theme to localStorage with the key 'theme'. This means the user's theme preference persists across page reloads and browser sessions.
DOM Attribute Updates & Kempo CSS Integration
Whenever the theme changes, it's automatically applied to document.documentElement as a theme attribute. This sets the color-scheme property which enables Kempo CSS's light-dark() function to work correctly.
Kempo CSS provides theme-aware CSS custom properties that automatically adapt to light and dark modes. See the Kempo CSS documentation for details on available color variables and theming.
System Preference Detection
When the theme is set to 'auto', the utility monitors the system's prefers-color-scheme media query and updates the auto-theme attribute on the document element. The getCalculated() method resolves the actual theme based on this preference.