refract v0.1 · beta
Guides / Angular

Angular

Same story as everywhere: a small injectable service holds the theme, keeps theme.css injected, and exposes getClass; templates bind [class]. Shown with standalone components and signals.

1 · A theme service

A root service owns the theme as a signal and mirrors theme.css into a <style> element via an effect — so setting a new theme re-themes the app.

theme.service.ts
import { Injectable, signal, effect, inject } from "@angular/core";
import { DOCUMENT } from "@angular/common";
import { createTheme, type Theme } from "@theme-registry/refract";
import { createCssAdapter } from "@theme-registry/refract-css";
import { raw } from "./theme.config";

@Injectable({ providedIn: "root" })
export class ThemeService {
  private doc = inject(DOCUMENT);
  private styleEl = this.doc.createElement("style");
  readonly theme = signal<Theme>(createTheme(raw, { adapter: createCssAdapter() }));

  constructor() {
    this.doc.head.appendChild(this.styleEl);
    effect(() => (this.styleEl.textContent = this.theme().css));   // keep <style> in sync
  }

  setTheme(theme: Theme) { this.theme.set(theme); }
  getClass(sub: string, group: string, variant: string) {
    return this.theme().getClass(sub, group, variant);
  }
}

2 · Use a class

Inject the service and bind [class] to getClass. The class list is composed by refract; the template just applies it.

save-button.component.ts
import { Component, inject } from "@angular/core";
import { ThemeService } from "./theme.service";

@Component({
  selector: "app-save-button",
  standalone: true,
  template: `<button [class]="theme.getClass('components', 'buttons', 'primary')">Save</button>`,
})
export class SaveButtonComponent {
  protected theme = inject(ThemeService);
}

3 · Switch themes

Swap in an override() child; the service's effect re-injects its CSS and every bound [class] updates.

brand-switch.ts
const warm = base.override({ colors: { brand: { base: "#e8590c", text: "#ffffff" } } });
themeService.setTheme(warm);   // re-injects theme.css via the effect
For dark mode, author modes and toggle data-theme on document.documentElement (or via Renderer2) — a CSS-cascade flip, no re-inject. SSR (Angular Universal): inject theme.css into the server response the same way; or emit it to a file at build time and add it to the app's global styles.
Technical documentation for @theme-registry/refract · all output is compiled client-side by the real library (CSS adapter). The chrome is theme-aware; the render panes carry each preset's own world. · MIT licensed.