@rb-mwindh/ngx-theme-manager provides the basics tools for a robust theme-switcher implementation.
This package does not provide ready-made visual components, but only supports their implementation by providing the necessary services and tools.
As of v18, the package major version follows the supported Angular major version.
| Package version | Angular version | Development runtime |
|---|---|---|
22.x |
Angular 22.x |
Node.js ^22.22.3, ^24.15.0 or ^26.0.0 |
21.x |
Angular 21.x |
Node.js ^20.19.0, ^22.12.0 or ^24.0.0 |
20.x |
Angular 20.x |
Node.js ^20.19.0, ^22.12.0 or ^24.0.0 |
19.x |
Angular 19.x |
Node.js ^18.19.1, ^20.11.1 or ^22.0.0 |
18.x |
Angular 18.x |
Node.js ^18.19.1, ^20.11.1 or ^22.0.0 |
The published package declares @angular/common and @angular/core version 22 or newer as peer dependencies.
The Node.js versions above apply to this repository's development, build and release tooling.
This implementation is based on the regular way Angular loads component styles.
Since theme styles are usually global styles, theme components normally use ViewEncapsulation.None.
Angular does not provide a direct way to access the <style> element associated with a component.
Therefore, @rb-mwindh/ngx-theme-manager identifies theme styles through predefined annotations in CSS comments.
The library scans the application's <style> elements, registers annotated themes
and adds a data-theme="<id>" attribute to the corresponding elements.
It then uses the media attribute to activate or deactivate each theme:
media="none"media attribute removedNewly added styles are discovered automatically. This also supports themes loaded after application startup.
npm install @rb-mwindh/ngx-theme-manager --save
@rb-mwindh/ngx-theme-manager identifies CSS, SCSS and other compiled stylesheets as themes
through annotations in CSS comments. Use the /*! ... */ comment format so the annotations
remain available after production minification.
... By default, multi-line comments be stripped from the compiled CSS in compressed mode. If a comment begins with /*!, though, it will always be included in the CSS output. ...
See the Sass documentation on comments
Known annotations are:
| Annotation | Required | Description |
|---|---|---|
@@id |
yes | Unique theme identifier; reads until the end of the line |
@@displayName |
no | User-facing name; defaults to the theme ID |
@@description |
no | Optional user-facing description |
@@default |
no | Marks the theme as the default theme; does not take a value |
/*!
* @@id my-theme-id
* @@displayName My Fantastic Theme
* @@description This is my fantastic theme with magic colors
* @@default
*/
// add all your theme styles below this line, e.g.
@import '@angular/material/prebuilt-themes/indigo-pink.css';
@import 'some-other-library/theme.css';
Use an Angular component to make the compiler pick up and compile the theme stylesheets.
The component must use ViewEncapsulation.None so the styles are global.
@Component({
selector: 'app-themes',
template: '', // no template needed
styleUrls: [
'assets/themes/theme-a.scss',
'assets/themes/theme-b.scss',
'assets/themes/theme-c.scss',
], // load all theme stylesheets here
encapsulation: ViewEncapsulation.None, // make the styles global
})
export class AppThemesComponent {
/* no implementation needed */
}
Import and render this component near the root of the application so theme styles are available throughout the application.
@Component({
selector: 'app-root',
template: `
<app-themes></app-themes>
<!-- your app template here -->
`,
imports: [AppThemesComponent],
})
export class AppComponent { ... }
The package intentionally does not prescribe the appearance of a theme picker. It exposes the registered themes, the current theme and a method for changing the selection.
Assuming an application provides an app-theme-picker component, it can be connected to ThemeService as follows:
@Component({
selector: 'app-root',
imports: [AppThemePickerComponent],
template: `
<app-theme-picker
[themes]="themeService.themes()"
[currentTheme]="themeService.currentTheme()"
(selectionChanged)="themeService.selectTheme($event)"
/>
`,
})
export class AppComponent {
readonly themeService = inject(ThemeService);
}
selectTheme() accepts a registered theme ID. Passing null clears the current in-memory selection.
Unknown theme IDs are ignored once themes have been registered.
Signals are the preferred API for current Angular applications.
| State | Signal API | Observable compatibility API |
|---|---|---|
| Registered themes | themeService.themes() |
themeService.themes$ |
| Current theme ID | themeService.currentTheme() |
themeService.currentTheme$ |
The Observable APIs remain available for applications that use RxJS-based state handling or the async pipe.
@Component({
selector: 'app-current-theme',
imports: [AsyncPipe],
template: `Current theme: {{ themeService.currentTheme$ | async }}`,
})
export class CurrentThemeComponent {
readonly themeService = inject(ThemeService);
}
After the first themes have been discovered, the initial selection is resolved in this order:
| Priority | Source |
|---|---|
| 1 | Valid theme ID from the configured URL query parameter |
| 2 | Valid theme ID from the configured browser storage entry |
| 3 | Theme annotated with @@default |
| 4 | First registered theme |
Unknown route or storage values are ignored. If no themes are registered, the current theme remains null.
By default, the selected theme is synchronized with:
| Integration | Default value |
|---|---|
| Browser storage key | ngx-theme-manager/current-theme |
| URL query parameter | theme |
Both integrations are optional. Angular Router itself is also optional; applications without Router can use the package without additional configuration.
Use provideThemeManager() to customize the storage key and query parameter:
import { ApplicationConfig } from '@angular/core';
import { provideThemeManager } from '@rb-mwindh/ngx-theme-manager';
export const appConfig: ApplicationConfig = {
providers: [
provideThemeManager({
storageKey: 'my-application/current-theme',
queryParam: 'app-theme',
}),
],
};
To disable either integration, provide null or an empty string:
import { ApplicationConfig } from '@angular/core';
import { provideThemeManager } from '@rb-mwindh/ngx-theme-manager';
export const appConfig: ApplicationConfig = {
providers: [
provideThemeManager({
storageKey: '',
queryParam: null,
}),
],
};
Only specify the options you want to customize. All omitted options retain their default values.
The signal-based state API can replace template subscriptions without requiring a breaking migration. The Observable APIs remain supported.
| Previous Observable usage | Preferred signal usage |
|---|---|
themeService.themes$ | async |
themeService.themes() |
themeService.currentTheme$ | async |
themeService.currentTheme() |
The Theme interface now exposes immutable metadata.
Construct theme metadata as readonly data and replace objects
instead of mutating individual properties.
import { Theme } from '@rb-mwindh/ngx-theme-manager';
const darkTheme: Theme = {
id: 'dark',
displayName: 'Dark',
description: 'Dark application theme',
defaultTheme: true,
};
The demo application uses standalone APIs and signals, but the library does not require a specific application bootstrap style.
This repository requires one of the supported Node.js versions listed in the compatibility table.
git clone https://github.com/rb-mwindh/ngx-theme-manager.git <workspace>
cd <workspace>
npm ci
npm ci installs the exact dependency versions recorded in package-lock.json.
This is the recommended way to install dependencies for development and testing.
During installation, npm automatically runs the prepare script, which configures
the repository's Husky Git hooks. No separate initialization command is required.
npm run start
npm run build
npm run typecheck
npm run lint
npm run test
Thank you for your interest in contributing to this library.
Please read the Contribution Guidelines
Please check my wiki::faq and wiki::troubleshooting wiki page first.
If this doesn't answer your question, you may post a question to the issue tracker.
| Name | License | Type |
|---|---|---|
| @angular/animations | MIT | Dependency |
| @angular/cdk | MIT | Dependency |
| @angular/common | MIT | Dependency |
| @angular/compiler | MIT | Dependency |
| @angular/core | MIT | Dependency |
| @angular/forms | MIT | Dependency |
| @angular/platform-browser | MIT | Dependency |
| @angular/platform-browser-dynamic | MIT | Dependency |
| @angular/router | MIT | Dependency |
| @standard-schema/spec | MIT | Dependency |
| entities | BSD-2-Clause | Dependency |
| material-icons | Apache-2.0 | Dependency |
| ngx-theme-manager | MIT | Dependency |
| parse5 | MIT | Dependency |
| rxjs | Apache-2.0 | Dependency |
| tslib | 0BSD | Dependency |
| zod | MIT | Dependency |
| zone.js | MIT | Dependency |