Skip to main content

ADR 003: CSS Custom Property Theming Engine

Status: Accepted
Date: 2026-05-23

Context

Synapse UI must support runtime theme switching (Light, Dark, High-Contrast) without requiring consumers to recompile their application or maintain separate build targets. Enterprise apps often need instant theme toggling and brand customization.

We evaluated several theming approaches for an Angular component library.

Decision

Implement theming via CSS custom properties (CSS variables) managed by a ThemeService that injects token maps onto document.body at runtime.

Token naming convention: --synapse-{category}-{name}.

Three built-in themes ship with the library. Consumers can override individual tokens via ThemeService.setCustomTokens().

Consequences

Positive

  • Zero JavaScript per component for theme awareness — components just reference var(--synapse-*).
  • Instant theme switching with no page reload.
  • Consumers can override tokens in CSS without forking the library.
  • Works with Shadow DOM if tokens are inherited (documented limitation for encapsulated components).
  • High-contrast theme is straightforward to implement as an alternate token map.

Negative

  • CSS custom properties have no compile-time validation — typos in token names fail silently.
  • Shadow DOM encapsulation requires explicit token forwarding or :host declarations.
  • No type safety in CSS itself (mitigated by TypeScript token maps in @synapse-ui/theme).

Alternatives Considered

AlternativeWhy rejected
SCSS variables per themeRequires separate builds per theme; no runtime switching
Angular Material themingCouples library to Material Design; heavy dependency
CSS-in-JS (e.g., Emotion)Not idiomatic in Angular; adds runtime overhead
Tailwind CSS pluginForces Tailwind on all consumers; conflicts with token approach
CSS @media (prefers-color-scheme) onlyCannot support manual toggle or high-contrast mode