@su-labs/theme
Version: 
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.
140 lines (102 loc) • 5.49 kB
Markdown
# 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:
* `light`
* `dark`
* `contrast`
* `custom`
* `system` (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
```typescript
// 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.
```CSS
/* 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.
```TypeScript
import { Component, inject } from '@angular/core';
import { SuThemeService, SuThemeName } from '@su-labs/theme';
({
  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 `localStorage` when you call `setTheme(..., 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**](https://github.com/Greezaaa/settings-utils-lib.git).
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**](https://github.com/Greezaaa/settings-utils-lib/blob/main/projects/su-labs/theme/LICENSE) file for details.