theme-egy
v1.2.1
Published
Modern Angular theme management library powered by Signals. Light/dark mode with CSS custom properties.
Maintainers
Readme
theme-egy
Lightweight Angular theme management powered by Signals. Light/dark mode, CSS custom properties, and configurable persistence — all tree-shakable and SSR-safe.
Features
- Light/dark mode switching with Signals
- Configurable color tokens (primary, background, foreground, border + custom)
- Two color sources: JS config or existing CSS variables
- Automatic CSS custom property injection
data-themeattribute management- Runtime color overrides without losing base palette
- Persistence via localStorage, sessionStorage, or none
- SSR-safe with full guard coverage
- Tree-shakable and standalone
- Zero NgModules
Why theme-egy?
Angular does not ship with a built-in theme system. Implementing light/dark mode requires manually managing mode state, persisting preferences, injecting CSS variables, handling the DOM attribute, and ensuring SSR safety. theme-egy handles all of this in a single, reactive API built on Angular Signals.
Installation
npm install theme-egyQuick Start
import { provideTheme, injectTheme } from 'theme-egy';
bootstrapApplication(App, {
providers: [
provideTheme({
colors: {
light: {
primary: '#2563eb',
background: '#ffffff',
foreground: '#111827',
border: '#e5e7eb',
},
dark: {
primary: '#3b82f6',
background: '#0f172a',
foreground: '#f1f5f9',
border: '#334155',
},
},
}),
],
});@Component({...})
export class AppComponent {
private theme = injectTheme();
readonly isDark = this.theme.isDark;
toggle() {
this.theme.toggle();
}
}<button (click)="toggle()">
{{ isDark() ? '☀️ Light' : '🌙 Dark' }}
</button>Configuration
provideTheme(config: ThemeConfig)
| Option | Type | Default | Description |
|---|---|---|---|
| colors | { light: ColorTokens; dark: ColorTokens } | — | Color palettes for each mode. Required for config source, forbidden for CSS source. |
| colorSource | 'config' \| 'css' | 'config' | Where colors originate. |
| defaultMode | 'light' \| 'dark' | 'light' | Fallback mode when no stored preference exists. |
| storageKey | string | 'theme-egy.mode' | Key for storage persistence. |
| storageStrategy | 'local' \| 'session' \| 'none' | 'local' | Which storage API to use. |
| autoApply | boolean | true | Auto-write CSS vars and data-theme attribute. |
| cssVarPrefix | string | '--theme-' | Prefix for CSS custom properties. |
ColorTokens
interface ColorTokens {
primary: string;
background: string;
foreground: string;
border: string;
[key: string]: string; // custom tokens
}Basic Usage
Inject ThemeService
import { injectTheme } from 'theme-egy';
@Component({...})
export class MyComponent {
private theme = injectTheme();
}Read mode
const mode = this.theme.mode; // Signal<'light' | 'dark'>
const isDark = this.theme.isDark; // Signal<boolean>Set mode
this.theme.setMode('dark');
this.theme.setMode('light');
this.theme.toggle(); // switches between light and darkRead colors
const colors = this.theme.colors; // Signal<ColorTokens>Override colors at runtime
// Merge overrides on top of current palette
this.theme.updateColors({ primary: '#ef4444' });
// Remove overrides, restoring base palette
this.theme.resetColors();Force re-read CSS variables (CSS source mode only)
this.theme.refreshColors();Color Source Modes
Config mode (default)
Colors are defined in the provideTheme() config. The library writes --theme-* CSS custom properties on <html> and manages the data-theme attribute automatically.
provideTheme({
colors: {
light: { primary: '#2563eb', background: '#ffffff', /* ... */ },
dark: { primary: '#3b82f6', background: '#0f172a', /* ... */ },
},
});CSS mode
Colors are defined in your stylesheets (Tailwind, plain CSS, design tokens). The library reads them from document.documentElement computed styles and sets data-theme so the CSS cascade handles mode switching.
provideTheme({
colorSource: 'css',
cssVarPrefix: '--color-',
});CSS mode pairs well with Tailwind's @theme directive or any design token system:
@theme {
--color-primary: #2563eb;
--color-background: #ffffff;
/* ... */
}
[data-theme="dark"] {
--color-primary: #3b82f6;
--color-background: #0f172a;
}Architecture
flowchart TD
A[ThemeConfig]
B[provideTheme]
C[ThemeService]
D[ThemeColorProvider]
E[ConfigColorProvider]
F[CssVariableColorProvider]
G[Effects]
A --> B
B --> C
C --> D
D --> E
D --> F
C --> G
G --> H[data-theme attribute]
G --> I[CSS custom properties]
G --> J[Storage persistence]
G --> K[Runtime overrides]Browser Support
- Angular 20+
- Standalone applications
- SSR compatible
- Zone.js optional (Signals-based)
API Reference
provideTheme(config: ThemeConfig): EnvironmentProviders
Registers the theme configuration. Validates config at runtime — throws if config source without colors, or CSS source with colors.
injectTheme(): ThemeService
Returns the singleton ThemeService instance.
ThemeService
| Member | Type | Description |
|---|---|---|
| mode | Signal<ThemeMode> | Current theme mode |
| isDark | Signal<boolean> | True if current mode is 'dark' |
| colors | Signal<ColorTokens> | Active colors (base + runtime overrides) |
| setMode(mode) | void | Switch to a specific mode |
| toggle() | void | Toggle between light and dark |
| updateColors(overrides) | void | Merge partial color overrides |
| resetColors() | void | Remove all runtime overrides |
| clearRuntimeColors() | void | Alias for resetColors() |
| refreshColors() | void | Force re-read CSS variables (CSS mode only) |
ThemeMode
type ThemeMode = 'light' | 'dark';ColorSource
type ColorSource = 'config' | 'css';StorageStrategy
type StorageStrategy = 'local' | 'session' | 'none';ThemeColorProvider
Abstract base class for color providers.
abstract class ThemeColorProvider {
abstract readonly colors: Signal<ColorTokens>;
}License
MIT
