Skip to content

About

Angular component to switch between different theming stylesheets

Topics

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

ngx-theme-manager

@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.


sponsors License latest release

open issues open pr

nodejs angular


Table of Contents

Compatibility

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.

How it works

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:

  • inactive theme styles receive media="none"
  • active theme styles have their media attribute removed

Newly added styles are discovered automatically. This also supports themes loaded after application startup.

Getting Started

Install the package

npm install @rb-mwindh/ngx-theme-manager --save

Implement your theme stylesheets

@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';

Load your theme stylesheets

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 { ... }

Implement your theme-picker component

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 and Observables

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);
}

Initial theme selection

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.

Configure synchronization

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.

Migrating to the signal-based API

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.

Building and Testing

This repository requires one of the supported Node.js versions listed in the compatibility table.

  1. Clone the repository
git clone https://github.com/rb-mwindh/ngx-theme-manager.git <workspace>
cd <workspace>
  1. Install the dependencies
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.

  1. Run the demo app
npm run start
  1. Build the library
npm run build
  1. Run the local validation pipeline:
npm run typecheck
npm run lint
npm run test

Contribution Guidelines

Thank you for your interest in contributing to this library.

Please read the Contribution Guidelines

Get help

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.

About

Maintainers

Contributors

  • none ;(

3rd Party Licenses

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

License

License

About

Angular component to switch between different theming stylesheets

Topics

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages