@su-labs/theme
v1.0.3
Published
A robust and reactive Angular service for managing application themes. It provides a full-featured solution that supports dynamic switching, user preference persistence, and automatic system theme detection.
Maintainers
Readme
SuThemeService
A robust and reactive Angular service for managing application themes. It provides a full-featured solution that supports dynamic switching, user preference persistence, and automatic system theme detection.
Features
- Reactive State: Uses Angular signals for efficient, reactive theme management.
- System Theme Integration: Automatically follows the user's system theme preference (
prefers-color-scheme). - Persistence: Remembers the user's last-selected theme using
localStorage. - Configurable: Customizable with a configuration object for storage keys, CSS variable prefixes, and default themes.
- Programmatic Control: Simple public API to set, get, and track the active theme.
Available Themes
The service currently supports the following theme names by default:
lightdarkcontrastcustomsystem(follows OS preference)
You can provide custom variables for any of these themes via the themes property in SuThemeConfig.
Usage
The SuThemeService must be initialized at application startup to load its configuration and initial theme.
Example 1: Global Initialization with provideAppInitializer
// main.ts or app.config.ts
import { provideAppInitializer, inject } from '@angular/core';
import { SuThemeService, SuThemeConfig } from '@su-labs/theme';
const myThemeConfig: SuThemeConfig = {
// Optional: Set a default theme if none is saved
defaultTheme: 'dark',
// Optional: Provide custom CSS variables for any theme
themes: {
dark: {
color: '#333',
background: '#eee',
'main-accent': '#007bff',
},
},
};
export const appConfig = {
providers: [
provideAppInitializer(() => {
const themeService = inject(SuThemeService);
themeService.init(myThemeConfig);
}),
],
};✅ This ensures the theme is loaded and applied before the application renders. The SuThemeService handles all the logic internally, including loading from localStorage and applying the initial theme.
Example 2: Using the Theme in a Component
Your service automatically applies CSS variables to the :root element. Your application stylesheets can use these variables, and the service will handle updating their values when the theme changes.
/* styles.css */
/* Use the CSS variables in your stylesheet */
body {
color: var(--su-theme-color);
background-color: var(--su-theme-background);
}
h1 {
color: var(--su-theme-main-accent);
}The service dynamically updates these variables based on the active theme, applying a combination of its internal defaults and any custom overrides you provide in the configuration.
Example 3: Dynamic Theme Switching
Use the setTheme() method to switch themes and the theme signal to read the current state.
import { Component, inject } from '@angular/core';
import { SuThemeService, SuThemeName } from '@su-labs/theme';
@Component({
selector: 'app-theme-switcher',
standalone: true,
imports: [],
template: `
<h1>Dynamic Theme Switching</h1>
<p>Current Theme: {{ themeService.theme() }}</p>
<button (click)="setTheme('light')">Light</button>
<button (click)="setTheme('dark')">Dark</button>
<button (click)="setTheme('system')">System</button>
`,
})
export class ThemeSwitcherComponent {
// Use the service directly in the template
public readonly themeService = inject(SuThemeService);
setTheme(themeName: SuThemeName) {
this.themeService.setTheme(themeName);
}
}✅ This correctly shows how to use your service's public API to switch between themes. The service handles the underlying logic, including persistence and CSS variable application.
Configuration Options
The service can be configured to fit your application's needs.
| Option | Description | Default Value |
|--------------------|---------------------------------------------------------------|------------------|
| storageKey | The key used to persist the selected theme in localStorage. | 'su:theme' |
| cssVarPrefix | The prefix for all CSS variables managed by the service. | '--su-theme-' |
| defaultTheme | The theme to use if no preference is saved in localStorage. | 'system' |
| themes | An object with custom CSS variable overrides for each theme. | undefined |
Notes
The service safely handles properties and ignores null or undefined values, preventing issues like prototype pollution.
Works seamlessly with standalone components and reactive setups thanks to its signal-based API.
Persistence is handled automatically: The service saves the user's preference to
localStoragewhen you callsetTheme(..., true). On initialization, it automatically restores this saved preference. You do not need to add this logic in your components.
Contributing
If you find any bugs or have feature requests, please open an issue or submit a pull request on our GitHub repository.
To contribute code, please ensure your changes include unit tests to maintain code quality. Please see the main repository's README.md for details on the monorepo structure.
License
This project is licensed under the MIT License. See the LICENSE file for details.
