ngx-theme-stack 3.5.0 โ 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -160
- package/fesm2022/ngx-theme-stack.mjs +16 -4
- package/fesm2022/ngx-theme-stack.mjs.map +1 -1
- package/package.json +1 -1
- package/schematics/sync/index.js +7 -2
- package/schematics/sync/index.js.map +1 -1
- package/types/ngx-theme-stack.d.ts +32 -5
package/README.md
CHANGED
|
@@ -1,96 +1,140 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ๐จ ngx-theme-stack
|
|
4
|
+
|
|
5
|
+
**A simple and powerful headless theme manager for Angular.**
|
|
6
|
+
Built for performance and SSR support.
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/ngx-theme-stack)
|
|
9
|
+
[](https://github.com/WanderleeDev/ngx-theme-stack/blob/main/LICENSE)
|
|
10
|
+
[](https://angular.dev/)
|
|
11
|
+
[](https://angular.dev/guide/signals)
|
|
12
|
+
[](https://angular.dev/guide/ssr)
|
|
13
|
+
|
|
14
|
+
[๐ Live Demo](https://demo-ngx-theme-stack.wanderlee.site/) ยท [๐ Documentation](https://ngx-theme-stack-docs.wanderlee.site/) ยท [โญ Star on GitHub](https://github.com/WanderleeDev/ngx-theme-stack)
|
|
2
15
|
|
|
3
16
|

|
|
4
17
|
|
|
5
|
-
|
|
18
|
+
</div>
|
|
6
19
|
|
|
7
|
-
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ๐ Table of Contents
|
|
23
|
+
|
|
24
|
+
- [๐ Features](#-features)
|
|
25
|
+
- [๐ฆ Installation](#-installation)
|
|
26
|
+
- [๐ค What does `ng add` do for you?](#-what-does-ng-add-do-for-you)
|
|
27
|
+
- [๐๏ธ Architecture & Extensibility](#๏ธ-architecture--extensibility)
|
|
28
|
+
- [๐ Supported Versions](#-supported-versions)
|
|
29
|
+
- [โ๏ธ Configuration](#๏ธ-configuration)
|
|
30
|
+
- [๐ ๏ธ Usage](#๏ธ-usage)
|
|
31
|
+
- [๐ก๏ธ CoreThemeService API](#๏ธ-advanced-corethemeservice-api)
|
|
32
|
+
- [๐จ Styling](#-styling)
|
|
33
|
+
- [๐ช๏ธ Tailwind CSS v4 Integration](#๏ธ-tailwind-css-v4-integration)
|
|
34
|
+
- [โก Performance Strategies](#-performance-strategies)
|
|
35
|
+
- [๐ License](#-license)
|
|
36
|
+
|
|
37
|
+
---
|
|
8
38
|
|
|
9
39
|
## ๐ Features
|
|
10
40
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
41
|
+
| | Feature | Description |
|
|
42
|
+
| --- | ------------------------------- | --------------------------------------------------- |
|
|
43
|
+
| โก | **Single command setup** | Automatic configuration via `ng add` |
|
|
44
|
+
| ๐ | **System preference detection** | Auto-syncs with OS via `prefers-color-scheme` |
|
|
45
|
+
| ๐ | **Dynamic switching** | Toggle, cycle, or select between themes |
|
|
46
|
+
| ๐ ๏ธ | **Highly customizable** | Custom themes, class prefixes, configurable storage |
|
|
47
|
+
| ๐งฑ | **Angular Signals** | Maximum reactivity and performance |
|
|
48
|
+
| ๐ | **SSR ready** | Safe in server-side rendering environments |
|
|
49
|
+
| ๐ซ | **Zero flicker** | Anti-flash script + Critters Trick strategy |
|
|
18
50
|
|
|
19
|
-
|
|
51
|
+
---
|
|
20
52
|
|
|
21
|
-
|
|
53
|
+
## ๐ฆ Installation
|
|
22
54
|
|
|
23
55
|
```bash
|
|
24
56
|
ng add ngx-theme-stack
|
|
25
57
|
```
|
|
26
58
|
|
|
27
|
-
### Installation Modes
|
|
28
|
-
|
|
29
59
|
> [!TIP]
|
|
30
60
|
> **๐ Using Bun?**
|
|
31
|
-
> Since `ng add` is currently not supported for Bun environments,
|
|
32
|
-
>
|
|
33
|
-
> 1. **Install:** `bun add ngx-theme-stack`
|
|
34
|
-
> 2. **Configure:** `ng generate ngx-theme-stack:ng-add`
|
|
61
|
+
> Since `ng add` is currently not supported for Bun environments, use this two-step process:
|
|
35
62
|
>
|
|
36
|
-
>
|
|
63
|
+
> ```bash
|
|
64
|
+
> bun add ngx-theme-stack
|
|
65
|
+
> ng generate ngx-theme-stack:ng-add
|
|
66
|
+
> ```
|
|
67
|
+
|
|
68
|
+
### Installation modes
|
|
69
|
+
|
|
70
|
+
When running `ng add`, you choose between two modes:
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary><strong>โก Quick mode</strong> โ default, applies instantly</summary>
|
|
74
|
+
|
|
75
|
+
| Option | Value |
|
|
76
|
+
| ---------------- | ------------------------- |
|
|
77
|
+
| Initial theme | `system` |
|
|
78
|
+
| Apply mode | `class` on `<html>` |
|
|
79
|
+
| Available themes | `light`, `dark`, `system` |
|
|
80
|
+
| Strategy | `critters` โ zero flash |
|
|
37
81
|
|
|
38
|
-
|
|
82
|
+
</details>
|
|
39
83
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- Initial theme: `system`.
|
|
43
|
-
- Apply mode: `class` (adds the theme class to the `<html>` element).
|
|
44
|
-
- Available themes: `['light', 'dark', 'system']`.
|
|
45
|
-
- **Strategy**: `critters` (Zero-flash via CSS inlining).
|
|
84
|
+
<details>
|
|
85
|
+
<summary><strong>๐ ๏ธ Custom mode</strong> โ full control</summary>
|
|
46
86
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
87
|
+
- Choose which themes to include (e.g. `blue`, `high-contrast`)
|
|
88
|
+
- Configure the default theme on startup
|
|
89
|
+
- Change the `localStorage` key
|
|
90
|
+
- Apply via `class`, `data-theme` attribute, or both
|
|
91
|
+
- Pick your anti-flash strategy: `critters` or `blocking`
|
|
92
|
+
|
|
93
|
+
</details>
|
|
94
|
+
|
|
95
|
+
---
|
|
53
96
|
|
|
54
97
|
## ๐ค What does `ng add` do for you?
|
|
55
98
|
|
|
56
|
-
|
|
99
|
+
The installation command automates the following:
|
|
57
100
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
| File | What changes |
|
|
102
|
+
| --------------- | ----------------------------------------------------------------------- |
|
|
103
|
+
| `app.config.ts` | Injects `provideThemeStack()` using AST โ follows imports automatically |
|
|
104
|
+
| `index.html` | Injects the blocking anti-flash script into `<head>` |
|
|
105
|
+
| `package.json` | Adds a `"prebuild"` script for theme synchronization |
|
|
106
|
+
| `angular.json` | Registers `themes.css` and optimizes build config |
|
|
107
|
+
| `themes.css` | Scaffolds base theme tokens if they don't exist |
|
|
63
108
|
|
|
64
109
|
> [!TIP]
|
|
65
|
-
> **Re-configuration support:**
|
|
66
|
-
|
|
67
|
-
## ๐๏ธ Architecture & Extensibility
|
|
110
|
+
> **Re-configuration support:** Run `ng add` multiple times freely. The schematic updates existing code without duplicating it.
|
|
68
111
|
|
|
69
|
-
|
|
112
|
+
---
|
|
70
113
|
|
|
71
|
-
|
|
72
|
-
- **Extensibility:** You can inject `CoreThemeService` to build your own custom services or components with specific business logic.
|
|
114
|
+
## ๐๏ธ Architecture & Extensibility
|
|
73
115
|
|
|
74
|
-
|
|
116
|
+
The **`CoreThemeService`** is the foundation โ it manages state (Signals), persistence (localStorage), system detection (matchMedia), and safe DOM manipulation (SSR compatible).
|
|
75
117
|
|
|
76
|
-
|
|
118
|
+
### Utility services
|
|
77
119
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
| Service | Pattern | Description |
|
|
121
|
+
| -------------------- | ------- | ------------------------------------------------------------------------- |
|
|
122
|
+
| `ThemeToggleService` | Toggle | Binary switch between `light` and `dark` |
|
|
123
|
+
| `ThemeSelectService` | Select | Exposes the full theme list; ideal for dropdowns |
|
|
124
|
+
| `ThemeCycleService` | Cycle | Rotates through all themes; exposes `upcoming`, `preceding`, `cycleIndex` |
|
|
81
125
|
|
|
82
126
|
---
|
|
83
127
|
|
|
84
|
-
##
|
|
128
|
+
## ๐ Supported Versions
|
|
85
129
|
|
|
86
|
-
| Angular Version |
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
130
|
+
| Angular Version | Status |
|
|
131
|
+
| --------------- | --------- |
|
|
132
|
+
| Angular 21 | โ
Stable |
|
|
133
|
+
| Angular 20 | โ
Stable |
|
|
90
134
|
|
|
91
|
-
|
|
135
|
+
---
|
|
92
136
|
|
|
93
|
-
|
|
137
|
+
## โ๏ธ Configuration
|
|
94
138
|
|
|
95
139
|
```typescript
|
|
96
140
|
import { provideThemeStack } from 'ngx-theme-stack';
|
|
@@ -98,34 +142,38 @@ import { provideThemeStack } from 'ngx-theme-stack';
|
|
|
98
142
|
export const appConfig: ApplicationConfig = {
|
|
99
143
|
providers: [
|
|
100
144
|
provideThemeStack({
|
|
101
|
-
themes: ['light', 'dark', 'sunset'], //
|
|
102
|
-
defaultTheme: 'system', //
|
|
103
|
-
mode: 'class', // 'class'
|
|
104
|
-
strategy: 'critters', // 'critters'
|
|
105
|
-
storageKey: 'ngx-theme-stack', //
|
|
145
|
+
themes: ['light', 'dark', 'sunset'], // your theme identifiers
|
|
146
|
+
defaultTheme: 'system', // resolves via matchMedia
|
|
147
|
+
mode: 'class', // 'class' | 'attribute' | 'both'
|
|
148
|
+
strategy: 'critters', // 'critters' | 'blocking'
|
|
149
|
+
storageKey: 'ngx-theme-stack', // localStorage key
|
|
106
150
|
}),
|
|
107
151
|
],
|
|
108
152
|
};
|
|
109
153
|
```
|
|
110
154
|
|
|
111
|
-
|
|
112
|
-
| :------------- | :----------- | :---------------------------- | :------------------------------------------------------------------------ |
|
|
113
|
-
| `themes` | `string[]` | `['light', 'dark', 'system']` | List of supported theme identifiers. |
|
|
114
|
-
| `defaultTheme` | `string` | `'system'` | Theme used on first visit or when no preference is saved. |
|
|
115
|
-
| `mode` | `NgMode` | `'class'` | How the theme is applied: `class`, `attribute` (`data-theme`), or `both`. |
|
|
116
|
-
| `strategy` | `NgStrategy` | `'critters'` | Anti-flash performance strategy: `critters` (inlined CSS) or `blocking`. |
|
|
117
|
-
| `storageKey` | `string` | `'ngx-theme-stack'` | Key used to persist theme preference in `localStorage`. |
|
|
155
|
+
### Options reference
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
| Option | Type | Default | Description |
|
|
158
|
+
| -------------- | ------------ | ----------------------------- | ------------------------ |
|
|
159
|
+
| `themes` | `string[]` | `['light', 'dark', 'system']` | Merged with built-ins |
|
|
160
|
+
| `defaultTheme` | `string` | `'system'` | Theme on first visit |
|
|
161
|
+
| `mode` | `NgMode` | `'class'` | How the theme is applied |
|
|
162
|
+
| `strategy` | `NgStrategy` | `'critters'` | Anti-flash strategy |
|
|
163
|
+
| `storageKey` | `string` | `'ngx-theme-stack'` | Persistence key |
|
|
121
164
|
|
|
122
|
-
|
|
165
|
+
> [!NOTE]
|
|
166
|
+
> Custom themes are **merged** with built-ins. Passing `['sepia', 'ocean']` resolves to `['system', 'light', 'dark', 'sepia', 'ocean']`. After config changes, run:
|
|
167
|
+
>
|
|
168
|
+
> ```bash
|
|
169
|
+
> ng generate ngx-theme-stack:sync --project YOUR_PROJECT_NAME
|
|
170
|
+
> ```
|
|
123
171
|
|
|
124
|
-
|
|
172
|
+
---
|
|
125
173
|
|
|
126
|
-
|
|
174
|
+
## ๐ ๏ธ Usage
|
|
127
175
|
|
|
128
|
-
|
|
176
|
+
### 1 โ Simple toggle (dark/light)
|
|
129
177
|
|
|
130
178
|
```typescript
|
|
131
179
|
import { inject } from '@angular/core';
|
|
@@ -143,9 +191,7 @@ export class ThemeToggleComponent {
|
|
|
143
191
|
}
|
|
144
192
|
```
|
|
145
193
|
|
|
146
|
-
### 2
|
|
147
|
-
|
|
148
|
-
Use `ThemeCycleService` to rotate through all your configured themes in order.
|
|
194
|
+
### 2 โ Multi-theme cycle
|
|
149
195
|
|
|
150
196
|
```typescript
|
|
151
197
|
import { inject } from '@angular/core';
|
|
@@ -155,7 +201,7 @@ import { ThemeCycleService } from 'ngx-theme-stack';
|
|
|
155
201
|
selector: 'app-theme-cycler',
|
|
156
202
|
standalone: true,
|
|
157
203
|
template: `
|
|
158
|
-
<button (click)="theme.cycle()">Next
|
|
204
|
+
<button (click)="theme.cycle()">Next: {{ theme.upcoming() }}</button>
|
|
159
205
|
<p>Theme {{ theme.cycleIndex() + 1 }} of {{ theme.availableThemes.length }}</p>
|
|
160
206
|
`,
|
|
161
207
|
})
|
|
@@ -164,9 +210,7 @@ export class ThemeCycleComponent {
|
|
|
164
210
|
}
|
|
165
211
|
```
|
|
166
212
|
|
|
167
|
-
### 3
|
|
168
|
-
|
|
169
|
-
Use `ThemeSelectService` to build selection interfaces like dropdowns or radio groups.
|
|
213
|
+
### 3 โ Direct selection (dropdowns/lists)
|
|
170
214
|
|
|
171
215
|
```typescript
|
|
172
216
|
import { inject } from '@angular/core';
|
|
@@ -192,9 +236,7 @@ export class ThemeSelectComponent {
|
|
|
192
236
|
|
|
193
237
|
---
|
|
194
238
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
If you need to build custom logic or want full control over the state, use the foundational `CoreThemeService`.
|
|
239
|
+
## ๐ก๏ธ Advanced: CoreThemeService API
|
|
198
240
|
|
|
199
241
|
```typescript
|
|
200
242
|
import { inject } from '@angular/core';
|
|
@@ -204,39 +246,45 @@ import { CoreThemeService } from 'ngx-theme-stack';
|
|
|
204
246
|
export class MyAdvancedComponent {
|
|
205
247
|
themeService = inject(CoreThemeService);
|
|
206
248
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// Helper boolean signals evaluating the applied theme
|
|
216
|
-
isDark = this.themeService.isDark;
|
|
217
|
-
isLight = this.themeService.isLight;
|
|
218
|
-
isSystem = this.themeService.isSystem;
|
|
219
|
-
|
|
220
|
-
// True after the first browser render. Great for preventing SSR flickering!
|
|
221
|
-
isHydrated = this.themeService.isHydrated;
|
|
222
|
-
|
|
223
|
-
/* --- ๐ ๏ธ Methods --- */
|
|
249
|
+
// โโ Signals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
250
|
+
selectedTheme = this.themeService.selectedTheme; // chosen by user
|
|
251
|
+
resolvedTheme = this.themeService.resolvedTheme; // applied to DOM
|
|
252
|
+
isDark = this.themeService.isDark;
|
|
253
|
+
isLight = this.themeService.isLight;
|
|
254
|
+
isSystem = this.themeService.isSystem;
|
|
255
|
+
isHydrated = this.themeService.isHydrated; // true after first render
|
|
224
256
|
|
|
257
|
+
// โโ Methods โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
225
258
|
changeTheme(newTheme: string) {
|
|
226
|
-
//
|
|
227
|
-
this.themeService.setTheme(newTheme);
|
|
259
|
+
this.themeService.setTheme(newTheme); // validates + applies + persists
|
|
228
260
|
}
|
|
229
261
|
}
|
|
230
262
|
```
|
|
231
263
|
|
|
264
|
+
> [!NOTE]
|
|
265
|
+
> `isDark` and `isLight` are `false` when a custom theme is active (e.g. `'sunset'`). For custom theme guards, use `resolvedTheme()` directly:
|
|
266
|
+
>
|
|
267
|
+
> ```typescript
|
|
268
|
+
> const isSunset = computed(() => themeService.resolvedTheme() === 'sunset');
|
|
269
|
+
> ```
|
|
270
|
+
>
|
|
271
|
+
> Use `isHydrated()` to guard conditional content (icons, images) against SSR hydration mismatches:
|
|
272
|
+
>
|
|
273
|
+
> ```html
|
|
274
|
+
> @if (theme.isHydrated()) {
|
|
275
|
+
> <img [src]="theme.isDark() ? 'dark-logo.png' : 'light-logo.png'">
|
|
276
|
+
> }
|
|
277
|
+
> ```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
232
281
|
## ๐จ Styling
|
|
233
282
|
|
|
234
|
-
|
|
283
|
+
`ng add` creates `src/themes.css` automatically. Define your CSS variables there:
|
|
235
284
|
|
|
236
285
|
```css
|
|
237
286
|
/* src/themes.css */
|
|
238
287
|
|
|
239
|
-
/* Using Classes (Default Mode) */
|
|
240
288
|
:root,
|
|
241
289
|
.light {
|
|
242
290
|
--bg-color: #ffffff;
|
|
@@ -254,15 +302,14 @@ The `ng add` command automatically creates a **`src/themes.css`** file. This is
|
|
|
254
302
|
}
|
|
255
303
|
```
|
|
256
304
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
If you are using **Tailwind CSS v4**, you can map your `themes.css` variables directly to Tailwind design tokens for a clean, theme-aware utility class experience.
|
|
305
|
+
---
|
|
260
306
|
|
|
261
|
-
|
|
307
|
+
## ๐ช๏ธ Tailwind CSS v4 Integration
|
|
262
308
|
|
|
263
|
-
|
|
309
|
+
### Map semantic variables (recommended)
|
|
264
310
|
|
|
265
311
|
```css
|
|
312
|
+
/* src/styles.css */
|
|
266
313
|
@import 'tailwindcss';
|
|
267
314
|
|
|
268
315
|
@theme {
|
|
@@ -271,29 +318,22 @@ In your `src/styles.css`, expose your theme variables as Tailwind tokens:
|
|
|
271
318
|
}
|
|
272
319
|
```
|
|
273
320
|
|
|
274
|
-
###
|
|
275
|
-
|
|
276
|
-
Now you can write clean, theme-aware classes. Colors update **automatically** whenever `ngx-theme-stack` switches the active theme โ no `dark:` prefix needed:
|
|
321
|
+
### Use in components โ no `dark:` prefix needed
|
|
277
322
|
|
|
278
323
|
```html
|
|
279
324
|
<div class="bg-main-bg text-main-text shadow-xl">
|
|
280
|
-
<!--
|
|
325
|
+
<!-- automatically reflects the active theme -->
|
|
281
326
|
</div>
|
|
282
327
|
```
|
|
283
328
|
|
|
284
|
-
>
|
|
329
|
+
> **Why this works:** CSS variables are set on `<html>` before Angular boots. Tailwind tokens point directly to those variables, covering all themes (dark, light, sunset, etc.) without extra configuration.
|
|
285
330
|
|
|
331
|
+
<details>
|
|
332
|
+
<summary><strong>Optional: enable the <code>dark:</code> prefix</strong></summary>
|
|
286
333
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
### (Optional) Enable the `dark:` Tailwind Prefix
|
|
290
|
-
|
|
291
|
-
Only needed if you want to use `dark:` utilities directly (e.g. `dark:bg-black`) tied to ngx-theme-stack's toggle instead of the OS system preference.
|
|
334
|
+
Only needed if you want `dark:` utilities tied to ngx-theme-stack's toggle:
|
|
292
335
|
|
|
293
336
|
```css
|
|
294
|
-
/* src/styles.css */
|
|
295
|
-
@import 'tailwindcss';
|
|
296
|
-
|
|
297
337
|
/* Class mode */
|
|
298
338
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
299
339
|
|
|
@@ -301,59 +341,49 @@ Only needed if you want to use `dark:` utilities directly (e.g. `dark:bg-black`)
|
|
|
301
341
|
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
302
342
|
```
|
|
303
343
|
|
|
304
|
-
> **โ ๏ธ**
|
|
305
|
-
|
|
344
|
+
> **โ ๏ธ** This disconnects `dark:` from OS preference and only covers the built-in `dark` theme. For multi-theme support, prefer the CSS variable approach above.
|
|
306
345
|
|
|
346
|
+
</details>
|
|
307
347
|
|
|
348
|
+
---
|
|
308
349
|
|
|
309
350
|
## โก Performance Strategies
|
|
310
351
|
|
|
311
352
|
### How the theme is applied on first load
|
|
312
353
|
|
|
313
|
-
`ng add` injects a minimal blocking script as the **first child of `<head
|
|
354
|
+
`ng add` injects a minimal blocking script as the **first child of `<head>`**. It runs before any stylesheet or Angular bundle:
|
|
314
355
|
|
|
315
356
|
```
|
|
316
|
-
1.
|
|
317
|
-
2. If
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
4. Sets color-scheme CSS property for native browser adaptation
|
|
357
|
+
1. Read stored theme from localStorage
|
|
358
|
+
2. If 'system' โ resolve OS preference via matchMedia('prefers-color-scheme: dark')
|
|
359
|
+
3. Apply theme to <html> (class, attribute, or both)
|
|
360
|
+
4. Set color-scheme CSS property for native browser adaptation
|
|
321
361
|
```
|
|
322
362
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
### Anti-flash strategies
|
|
326
|
-
|
|
327
|
-
There are two layers to prevent a flash:
|
|
328
|
-
|
|
329
|
-
| Layer | What it prevents | Always active? |
|
|
330
|
-
|---|---|---|
|
|
331
|
-
| Anti-flash script | Wrong theme class on `<html>` | โ
Yes, always |
|
|
332
|
-
| Strategy | Unstyled variables flash | Depends on chosen strategy |
|
|
363
|
+
### Strategy comparison
|
|
333
364
|
|
|
334
|
-
|
|
365
|
+
| | critters (default) | blocking |
|
|
366
|
+
| ------------------------- | ---------------------------------- | ------------------------------------------ |
|
|
367
|
+
| **How it works** | Inlines all CSS vars into `<head>` | Loads `themes.css` as render-blocking file |
|
|
368
|
+
| **Network requests** | Zero | One (then cached) |
|
|
369
|
+
| **Flash risk** | None | None |
|
|
370
|
+
| **Works with CSR** | โ
| โ
|
|
|
371
|
+
| **Works with SSR/SSG** | โ
| โ ๏ธ May flash on SSG |
|
|
372
|
+
| **Strict CSP compatible** | โ requires `unsafe-inline` | โ
|
|
|
373
|
+
| **Best for** | Most apps | Strict CSP, many themes |
|
|
335
374
|
|
|
336
|
-
|
|
375
|
+
<details>
|
|
376
|
+
<summary><strong>When to choose blocking over critters</strong></summary>
|
|
337
377
|
|
|
338
|
-
**
|
|
378
|
+
- **Strict CSP** โ Critters generates inline `<style>` tags requiring `'unsafe-inline'` in `style-src`
|
|
379
|
+
- **Many themes** โ All theme variables get inlined into HTML on every request; a cached file is more efficient
|
|
380
|
+
- **Critters conflicts** โ Complex CSS pipelines (PostCSS, CSS Modules) can conflict with Critters
|
|
381
|
+
- **Simpler debugging** โ An explicit stylesheet is easier to inspect in DevTools
|
|
339
382
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
**When to choose Blocking over Critters:**
|
|
343
|
-
|
|
344
|
-
- **Strict CSP** โ Critters generates inline `<style>` tags, which require `'unsafe-inline'` in `style-src`. A blocking stylesheet avoids this.
|
|
345
|
-
- **Many themes** โ All theme variables get inlined into the HTML on every request with Critters. If you have many themes with many variables, a cached external file is more efficient.
|
|
346
|
-
- **Critters conflicts** โ Complex CSS pipelines (PostCSS, CSS Modules, etc.) can conflict with Critters. Blocking is the safe fallback.
|
|
347
|
-
- **Simpler debugging** โ An explicit stylesheet is easier to inspect in DevTools than inlined styles.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Use the **Sync Command** to refresh your `index.html` if you change your configuration:
|
|
351
|
-
|
|
352
|
-
```bash
|
|
353
|
-
ng generate ngx-theme-stack:sync --project YOUR_PROJECT_NAME
|
|
354
|
-
```
|
|
383
|
+
</details>
|
|
355
384
|
|
|
385
|
+
---
|
|
356
386
|
|
|
357
387
|
## ๐ License
|
|
358
388
|
|
|
359
|
-
[MIT](
|
|
389
|
+
[MIT](https://github.com/WanderleeDev/ngx-theme-stack/blob/main/LICENSE)
|
|
@@ -70,8 +70,10 @@ const NGX_THEME_STACK_CONFIG = new InjectionToken('NGX_THEME_STACK_CONFIG', {
|
|
|
70
70
|
/**
|
|
71
71
|
* Provides Theme Stack configuration to Angular's DI system.
|
|
72
72
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
73
|
+
* The `themes` option accepts **additional** theme identifiers beyond the
|
|
74
|
+
* built-in defaults. They are merged with `'light'`, `'dark'`, and `'system'`
|
|
75
|
+
* so that the resolved {@link NgConfig.themes} array always includes all of
|
|
76
|
+
* them โ your custom values are appended after the built-ins.
|
|
75
77
|
*
|
|
76
78
|
* **Defaults:**
|
|
77
79
|
* - `themes`: `['light', 'dark', 'system']`
|
|
@@ -184,9 +186,19 @@ class CoreThemeService {
|
|
|
184
186
|
const theme = this.#selectedTheme();
|
|
185
187
|
return theme === 'system' ? this.#systemPreference() : theme;
|
|
186
188
|
}, ...(ngDevMode ? [{ debugName: "resolvedTheme" }] : /* istanbul ignore next */ []));
|
|
187
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* Whether the currently applied theme is dark.
|
|
191
|
+
*
|
|
192
|
+
* Returns `false` when a custom theme (e.g. `'sunset'`) is active โ use
|
|
193
|
+
* `resolvedTheme()` directly if you need to inspect the current custom theme.
|
|
194
|
+
*/
|
|
188
195
|
isDark = computed(() => this.resolvedTheme() === 'dark', ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
|
|
189
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Whether the currently applied theme is light.
|
|
198
|
+
*
|
|
199
|
+
* Returns `false` when a custom theme (e.g. `'sepia'`) is active โ use
|
|
200
|
+
* `resolvedTheme()` directly if you need to inspect the current custom theme.
|
|
201
|
+
*/
|
|
190
202
|
isLight = computed(() => this.resolvedTheme() === 'light', ...(ngDevMode ? [{ debugName: "isLight" }] : /* istanbul ignore next */ []));
|
|
191
203
|
/** Whether the currently applied theme is system. */
|
|
192
204
|
isSystem = computed(() => this.selectedTheme() === 'system', ...(ngDevMode ? [{ debugName: "isSystem" }] : /* istanbul ignore next */ []));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/errors.ts","../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/config/index.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/**\n * Base error class for `ngx-theme-stack`.\n *\n * Thrown when the library configuration is invalid.\n * Consumers can use `instanceof NgxThemeStackError` to catch only\n * errors originating from this library.\n *\n * @example\n * try {\n * bootstrapApplication(AppComponent, appConfig);\n * } catch (e) {\n * if (e instanceof NgxThemeStackError) {\n * console.error('Bad ngx-theme-stack config:', e.message);\n * }\n * }\n */\nexport class NgxThemeStackError extends Error {\n override readonly name = 'NgxThemeStackError';\n\n constructor(message: string) {\n super(`[ngx-theme-stack] ${message}`);\n // Restore prototype chain (required when targeting ES5 or older)\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * Runtime list of built-in themes.\n *\n * Lives here (and not in config/index.ts) because it defines a type:\n * config/index.ts already imports from types.ts, so placing DEFAULT_THEMES\n * here avoids any circular dependency.\n *\n * โ KEEP IN SYNC with the duplicate in:\n * projects/ngx-theme-stack/schematics/ng-add/constants.ts โ DEFAULT_THEMES\n *\n * Schematics compile to CommonJS and cannot import from this ESM file,\n * so the values are intentionally duplicated. Change both at the same time.\n */\nexport const DEFAULT_THEMES = ['system', 'light', 'dark'] as const;\n\n/** Literal union of built-in themes: `'system' | 'light' | 'dark'`. */\nexport type DefaultNgTheme = (typeof DEFAULT_THEMES)[number];\n\n/**\n * Theme type.\n *\n * - **Without** `T`: open union โ accepts any `string` with IDE autocomplete\n * hints for the built-in themes (`'system' | 'light' | 'dark'`).\n * - **With** `T`: closed union โ exactly `DefaultNgTheme | T`, enabling\n * full type-safety for custom theme sets.\n *\n * @example\n * NgTheme // 'system' | 'light' | 'dark' | (string & {})\n * NgTheme<'sepia'> // 'system' | 'light' | 'dark' | 'sepia'\n */\nexport type NgTheme<T extends string = string & {}> = DefaultNgTheme | T;\n\n/**\n * Resolved theme โ always `'light'` or `'dark'`, never `'system'`.\n * Represents the value that comes from `matchMedia`, not user selection.\n */\nexport type NgSystemTheme = Exclude<DefaultNgTheme, 'system'>;\n\n/**\n * Theme application mode.\n * - `'attribute'`: sets `data-theme` attribute on `<html>`\n * - `'class'`: adds theme class to `<html>`\n * - `'both'`: uses both attribute and class\n */\nexport type NgMode = 'attribute' | 'class' | 'both';\n\n/**\n * Theme application strategy.\n * - `'blocking'`: theme CSS is loaded synchronously before rendering\n * - `'critters'`: theme CSS is inlined using Critters for SSR/SSG\n */\nexport type NgStrategy = 'blocking' | 'critters';\n\n/**\n * Library configuration.\n *\n * @typeParam T - Custom theme literals. Defaults to open `string`, preserving\n * backwards compatibility. Pass specific literals (e.g. `'sepia' | 'ocean'`)\n * via {@link provideThemeStack} to get a closed, type-safe theme union.\n */\nexport interface NgConfig<T extends string = string & {}> {\n /** The theme to use on first visit or when no preference is saved. Default: 'system'. */\n defaultTheme: NgTheme<T>;\n\n /** Key used to persist theme preference in localStorage. Default: 'ngx-theme-stack'. */\n storageKey: string;\n\n /** \n * How the theme should be applied to the document (via class, attribute or both). \n * Default: 'class'.\n */\n mode: NgMode;\n\n /** \n * Performance strategy for anti-flash.\n * Use 'critters' (default) to inline all theme CSS in <head> โ works for CSR, SSR, and SSG.\n * Use 'blocking' to load themes.css as a render-blocking stylesheet (HTTP-cacheable).\n */\n strategy: NgStrategy;\n\n /** List of supported theme identifiers. Default: ['light', 'dark', 'system']. */\n themes: NgTheme<T>[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { NgxThemeStackError } from '../errors';\nimport { DEFAULT_THEMES, DefaultNgTheme, NgConfig } from '../types';\n\n/**\n * โ ATTENTION: SHARED CONFIGURATION VALUES\n *\n * These defaults MUST match the schematic defaults in:\n * projects/ngx-theme-stack/schematics/ng-add/constants.ts โ DEFAULTS\n *\n * Schematics compile to CommonJS and cannot import from this ESM file,\n * so the values are intentionally duplicated. Change both at the same time.\n *\n * If you change defaults here, also update:\n * schematics/ng-add/constants.ts โ DEFAULTS + DEFAULT_THEMES\n */\n\nexport const DEFAULT_NG_CONFIG = {\n defaultTheme: 'system',\n storageKey: 'ngx-theme-stack',\n mode: 'class',\n strategy: 'critters',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\n// The token uses NgConfig<string> because Angular DI resolves types at runtime\n// and cannot carry generic parameters. Type-safety is enforced at the\n// provideThemeStack() call site instead.\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig<string>>(\n 'NGX_THEME_STACK_CONFIG',\n {\n factory: () => DEFAULT_NG_CONFIG,\n },\n);\n\n/**\n * Provides Theme Stack configuration to Angular's DI system.\n *\n * Custom `themes` are **merged** with the built-in defaults\n * (`'light'`, `'dark'`, `'system'`), so you never lose the base themes.\n *\n * **Defaults:**\n * - `themes`: `['light', 'dark', 'system']`\n * - `defaultTheme`: `'system'`\n * - `storageKey`: `'ngx-theme-stack'`\n * - `mode`: `'class'`\n * - `strategy`: `'critters'`\n\n *\n * The type parameter `T` is **inferred automatically** from the `themes` array\n * when passed as a `const` โ no need to specify it manually.\n *\n * @typeParam T - Custom theme string literals, inferred from the `themes` option.\n *\n * @param config - Optional partial configuration. Omitted fields fall back to\n * {@link DEFAULT_NG_CONFIG}.\n *\n * @throws {@link NgxThemeStackError}\n * - If any entry in `themes` is an empty or whitespace-only string.\n * - If `defaultTheme` is not present in the resolved (merged) themes array.\n * - If `storageKey` is an empty or whitespace-only string.\n *\n * @example\n * // Default โ uses built-in themes and sensible defaults\n * provideThemeStack()\n *\n * @example\n * // Critters strategy โ inlines all theme CSS in <head> (works for CSR, SSR, and SSG)\n * provideThemeStack({\n * strategy: 'critters',\n * mode: 'class',\n * })\n *\n * @example\n * // Closed union: TypeScript infers 'sepia' | 'ocean' from the array\n * provideThemeStack({\n * themes: ['sepia', 'ocean'] as const,\n * defaultTheme: 'sepia', // โ
in resolved themes\n * // defaultTheme: 'nope', // โ throws NgxThemeStackError at runtime\n * })\n *\n * @example\n * // Custom storage key and mode\n * provideThemeStack({\n * storageKey: 'my-app-theme',\n * mode: 'attribute',\n * })\n */\nexport function provideThemeStack<const T extends string = DefaultNgTheme>(\n config: Partial<NgConfig<T>> = {},\n) {\n config.themes?.forEach((t) => {\n if (t.trim() === '') throw new NgxThemeStackError('Theme cannot be empty or whitespace.');\n });\n\n const themes = config.themes\n ? Array.from(new Set([...DEFAULT_NG_CONFIG.themes, ...config.themes]))\n : DEFAULT_NG_CONFIG.themes;\n\n if (config.defaultTheme && !(themes as string[]).includes(config.defaultTheme as string)) {\n throw new NgxThemeStackError(\n `\"defaultTheme\" must be one of the resolved themes: [${themes.join(', ')}].`,\n );\n }\n\n if (config.storageKey !== undefined && config.storageKey.trim() === '') {\n throw new NgxThemeStackError('\"storageKey\" cannot be empty or whitespace.');\n }\n\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n themes,\n } as NgConfig<string>,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../config';\nimport { NgxThemeStackError } from '../errors';\nimport { NgSystemTheme, NgTheme } from '../types';\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // โโ Dependencies โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // โโ Theme configuration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /** The initial stored theme read from localStorage on startup. */\n readonly #initialStoredTheme = this.readStoredTheme();\n\n /** List of available themes for Select/Cycle services. Defaults to ['system', 'light', 'dark']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n /**\n * The anti-flash class to remove from the host element.\n * Internal mechanism to bridge the gap between the blocking script's\n * initial DOM state and Angular's first effect run.\n */\n #antiFlashClass: string | null = null;\n\n // โโ System preference โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // โโ Theme state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) โ never `'system'`. */\n readonly resolvedTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = computed(() => this.resolvedTheme() === 'dark');\n\n /** Whether the currently applied theme is light. */\n readonly isLight = computed(() => this.resolvedTheme() === 'light');\n\n /** Whether the currently applied theme is system. */\n readonly isSystem = computed(() => this.selectedTheme() === 'system');\n\n /**\n * Whether the service has completed client-side initialization.\n *\n * `false` during SSR and on the very first render pass before the initial theme\n * is resolved from `localStorage`. Becomes `true` immediately after the\n * first browser render pass.\n *\n * **Important:** Guard template elements that display `selectedTheme()` or\n * `resolvedTheme()` behind this signal to prevent hydration-mismatch flashes\n * (e.g. if the server renders the default 'system' but the user has 'dark' stored).\n *\n * @example\n * ```html\n * {{ theme.isHydrated() ? theme.selectedTheme() : '...' }}\n * ```\n */\n readonly isHydrated = signal(false);\n\n // โโ Event handler โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // โโ Lifecycle โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n constructor() {\n this.captureAntiFlashClass();\n\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.resolvedTheme()));\n afterNextRender(() => this.isHydrated.set(true));\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // โโ Public API โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved theme did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#validThemes.has(theme)) {\n throw new NgxThemeStackError(\n `Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n if (!this.#isBrowser) return;\n if (theme === this.#selectedTheme()) return;\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // โโ Private โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n const theme = this.#initialStoredTheme;\n if (theme && this.#validThemes.has(theme as NgTheme)) {\n return theme as NgTheme;\n }\n return this.#config.defaultTheme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n\n if (this.#antiFlashClass) {\n host.classList.remove(this.#antiFlashClass);\n this.#antiFlashClass = null;\n }\n\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, theme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, theme);\n }\n\n this.applyColorSchemeHint(host, theme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n host.classList.remove(...this.availableThemes);\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private captureAntiFlashClass(): void {\n if (!this.#isBrowser || !this.#initialStoredTheme) return;\n\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(this.#initialStoredTheme)) {\n this.#antiFlashClass = null;\n return;\n }\n\n if (this.#initialStoredTheme === 'system') {\n this.#antiFlashClass = this.resolveSystemPreference();\n return;\n }\n\n this.#antiFlashClass = this.#initialStoredTheme;\n }\n\n private readStoredTheme(): string | null {\n if (!this.#isBrowser) return null;\n\n try {\n return localStorage.getItem(this.#config.storageKey);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { computed, inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'system'` โ `'light'` โ `'dark'` โ `'system'` โ ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to `['light', 'dark', 'system']`. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** Index of the currently selected theme in the cycle. */\n readonly cycleIndex = computed(() => {\n return this.availableThemes.indexOf(this.selectedTheme());\n });\n\n /** The theme that comes before the currently selected theme in the cycle. */\n readonly preceding = computed(() => {\n const index = this.cycleIndex();\n const len = this.availableThemes.length;\n return this.availableThemes[(index - 1 + len) % len];\n });\n\n /** The theme that comes after the currently selected theme in the cycle. */\n readonly upcoming = computed(() => {\n const index = this.cycleIndex();\n return this.availableThemes[(index + 1) % this.availableThemes.length];\n });\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n this.#core.setTheme(this.upcoming());\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to `['light', 'dark', 'system']`. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.resolvedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\nexport * from './lib/errors';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/errors.ts","../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/config/index.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/**\n * Base error class for `ngx-theme-stack`.\n *\n * Thrown when the library configuration is invalid.\n * Consumers can use `instanceof NgxThemeStackError` to catch only\n * errors originating from this library.\n *\n * @example\n * try {\n * bootstrapApplication(AppComponent, appConfig);\n * } catch (e) {\n * if (e instanceof NgxThemeStackError) {\n * console.error('Bad ngx-theme-stack config:', e.message);\n * }\n * }\n */\nexport class NgxThemeStackError extends Error {\n override readonly name = 'NgxThemeStackError';\n\n constructor(message: string) {\n super(`[ngx-theme-stack] ${message}`);\n // Restore prototype chain (required when targeting ES5 or older)\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * Runtime list of built-in themes.\n *\n * Lives here (and not in config/index.ts) because it defines a type:\n * config/index.ts already imports from types.ts, so placing DEFAULT_THEMES\n * here avoids any circular dependency.\n *\n * โ KEEP IN SYNC with the duplicate in:\n * projects/ngx-theme-stack/schematics/ng-add/constants.ts โ DEFAULT_THEMES\n *\n * Schematics compile to CommonJS and cannot import from this ESM file,\n * so the values are intentionally duplicated. Change both at the same time.\n */\nexport const DEFAULT_THEMES = ['system', 'light', 'dark'] as const;\n\n/** Literal union of built-in themes: `'system' | 'light' | 'dark'`. */\nexport type DefaultNgTheme = (typeof DEFAULT_THEMES)[number];\n\n/**\n * Theme type.\n *\n * - **Without** `T`: open union โ accepts any `string` with IDE autocomplete\n * hints for the built-in themes (`'system' | 'light' | 'dark'`).\n * - **With** `T`: closed union โ exactly `DefaultNgTheme | T`, enabling\n * full type-safety for custom theme sets.\n *\n * @example\n * NgTheme // 'system' | 'light' | 'dark' | (string & {})\n * NgTheme<'sepia'> // 'system' | 'light' | 'dark' | 'sepia'\n */\nexport type NgTheme<T extends string = string & {}> = DefaultNgTheme | T;\n\n/**\n * Resolved theme โ always `'light'` or `'dark'`, never `'system'`.\n * Represents the value that comes from `matchMedia`, not user selection.\n */\nexport type NgSystemTheme = Exclude<DefaultNgTheme, 'system'>;\n\n/**\n * Theme application mode.\n * - `'attribute'`: sets `data-theme` attribute on `<html>`\n * - `'class'`: adds theme class to `<html>`\n * - `'both'`: uses both attribute and class\n */\nexport type NgMode = 'attribute' | 'class' | 'both';\n\n/**\n * Theme application strategy.\n * - `'blocking'`: theme CSS is loaded synchronously before rendering\n * - `'critters'`: theme CSS is inlined using Critters for SSR/SSG\n */\nexport type NgStrategy = 'blocking' | 'critters';\n\n/**\n * Library configuration.\n *\n * @typeParam T - Custom theme literals. Defaults to open `string`, preserving\n * backwards compatibility. Pass specific literals (e.g. `'sepia' | 'ocean'`)\n * via {@link provideThemeStack} to get a closed, type-safe theme union.\n */\nexport interface NgConfig<T extends string = string & {}> {\n /** The theme to use on first visit or when no preference is saved. Default: 'system'. */\n defaultTheme: NgTheme<T>;\n\n /** Key used to persist theme preference in localStorage. Default: 'ngx-theme-stack'. */\n storageKey: string;\n\n /** \n * How the theme should be applied to the document (via class, attribute or both). \n * Default: 'class'.\n */\n mode: NgMode;\n\n /** \n * Performance strategy for anti-flash.\n * Use 'critters' (default) to inline all theme CSS in <head> โ works for CSR, SSR, and SSG.\n * Use 'blocking' to load themes.css as a render-blocking stylesheet (HTTP-cacheable).\n */\n strategy: NgStrategy;\n\n /**\n * The **resolved** list of supported theme identifiers, always including the\n * built-in themes (`'light'`, `'dark'`, `'system'`).\n *\n * When you pass custom themes to {@link provideThemeStack}, they are **merged**\n * with the built-in defaults โ your custom values are appended after them.\n *\n * @example\n * // Input to provideThemeStack:\n * themes: ['sepia', 'ocean'] as const\n *\n * // Resolved value stored in NgConfig:\n * // ['system', 'light', 'dark', 'sepia', 'ocean']\n *\n * Default (no custom themes): `['system', 'light', 'dark']`.\n */\n themes: NgTheme<T>[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { NgxThemeStackError } from '../errors';\nimport { DEFAULT_THEMES, DefaultNgTheme, NgConfig } from '../types';\n\n/**\n * โ ATTENTION: SHARED CONFIGURATION VALUES\n *\n * These defaults MUST match the schematic defaults in:\n * projects/ngx-theme-stack/schematics/ng-add/constants.ts โ DEFAULTS\n *\n * Schematics compile to CommonJS and cannot import from this ESM file,\n * so the values are intentionally duplicated. Change both at the same time.\n *\n * If you change defaults here, also update:\n * schematics/ng-add/constants.ts โ DEFAULTS + DEFAULT_THEMES\n */\n\nexport const DEFAULT_NG_CONFIG = {\n defaultTheme: 'system',\n storageKey: 'ngx-theme-stack',\n mode: 'class',\n strategy: 'critters',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\n// The token uses NgConfig<string> because Angular DI resolves types at runtime\n// and cannot carry generic parameters. Type-safety is enforced at the\n// provideThemeStack() call site instead.\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig<string>>(\n 'NGX_THEME_STACK_CONFIG',\n {\n factory: () => DEFAULT_NG_CONFIG,\n },\n);\n\n/**\n * Provides Theme Stack configuration to Angular's DI system.\n *\n * The `themes` option accepts **additional** theme identifiers beyond the\n * built-in defaults. They are merged with `'light'`, `'dark'`, and `'system'`\n * so that the resolved {@link NgConfig.themes} array always includes all of\n * them โ your custom values are appended after the built-ins.\n *\n * **Defaults:**\n * - `themes`: `['light', 'dark', 'system']`\n * - `defaultTheme`: `'system'`\n * - `storageKey`: `'ngx-theme-stack'`\n * - `mode`: `'class'`\n * - `strategy`: `'critters'`\n\n *\n * The type parameter `T` is **inferred automatically** from the `themes` array\n * when passed as a `const` โ no need to specify it manually.\n *\n * @typeParam T - Custom theme string literals, inferred from the `themes` option.\n *\n * @param config - Optional partial configuration. Omitted fields fall back to\n * {@link DEFAULT_NG_CONFIG}.\n *\n * @throws {@link NgxThemeStackError}\n * - If any entry in `themes` is an empty or whitespace-only string.\n * - If `defaultTheme` is not present in the resolved (merged) themes array.\n * - If `storageKey` is an empty or whitespace-only string.\n *\n * @example\n * // Default โ uses built-in themes and sensible defaults\n * provideThemeStack()\n *\n * @example\n * // Critters strategy โ inlines all theme CSS in <head> (works for CSR, SSR, and SSG)\n * provideThemeStack({\n * strategy: 'critters',\n * mode: 'class',\n * })\n *\n * @example\n * // Closed union: TypeScript infers 'sepia' | 'ocean' from the array\n * provideThemeStack({\n * themes: ['sepia', 'ocean'] as const,\n * defaultTheme: 'sepia', // โ
in resolved themes\n * // defaultTheme: 'nope', // โ throws NgxThemeStackError at runtime\n * })\n *\n * @example\n * // Custom storage key and mode\n * provideThemeStack({\n * storageKey: 'my-app-theme',\n * mode: 'attribute',\n * })\n */\nexport function provideThemeStack<const T extends string = DefaultNgTheme>(\n config: Partial<NgConfig<T>> = {},\n) {\n config.themes?.forEach((t) => {\n if (t.trim() === '') throw new NgxThemeStackError('Theme cannot be empty or whitespace.');\n });\n\n const themes = config.themes\n ? Array.from(new Set([...DEFAULT_NG_CONFIG.themes, ...config.themes]))\n : DEFAULT_NG_CONFIG.themes;\n\n if (config.defaultTheme && !(themes as string[]).includes(config.defaultTheme as string)) {\n throw new NgxThemeStackError(\n `\"defaultTheme\" must be one of the resolved themes: [${themes.join(', ')}].`,\n );\n }\n\n if (config.storageKey !== undefined && config.storageKey.trim() === '') {\n throw new NgxThemeStackError('\"storageKey\" cannot be empty or whitespace.');\n }\n\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n themes,\n } as NgConfig<string>,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../config';\nimport { NgxThemeStackError } from '../errors';\nimport { NgSystemTheme, NgTheme } from '../types';\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // โโ Dependencies โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // โโ Theme configuration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /** The initial stored theme read from localStorage on startup. */\n readonly #initialStoredTheme = this.readStoredTheme();\n\n /** List of available themes for Select/Cycle services. Defaults to ['system', 'light', 'dark']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n /**\n * The anti-flash class to remove from the host element.\n * Internal mechanism to bridge the gap between the blocking script's\n * initial DOM state and Angular's first effect run.\n */\n #antiFlashClass: string | null = null;\n\n // โโ System preference โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // โโ Theme state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) โ never `'system'`. */\n readonly resolvedTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /**\n * Whether the currently applied theme is dark.\n *\n * Returns `false` when a custom theme (e.g. `'sunset'`) is active โ use\n * `resolvedTheme()` directly if you need to inspect the current custom theme.\n */\n readonly isDark = computed(() => this.resolvedTheme() === 'dark');\n\n /**\n * Whether the currently applied theme is light.\n *\n * Returns `false` when a custom theme (e.g. `'sepia'`) is active โ use\n * `resolvedTheme()` directly if you need to inspect the current custom theme.\n */\n readonly isLight = computed(() => this.resolvedTheme() === 'light');\n\n /** Whether the currently applied theme is system. */\n readonly isSystem = computed(() => this.selectedTheme() === 'system');\n\n /**\n * Whether the service has completed client-side initialization.\n *\n * `false` during SSR and on the very first render pass before the initial theme\n * is resolved from `localStorage`. Becomes `true` immediately after the\n * first browser render pass.\n *\n * **Important:** Guard template elements that display `selectedTheme()` or\n * `resolvedTheme()` behind this signal to prevent hydration-mismatch flashes\n * (e.g. if the server renders the default 'system' but the user has 'dark' stored).\n *\n * @example\n * ```html\n * {{ theme.isHydrated() ? theme.selectedTheme() : '...' }}\n * ```\n */\n readonly isHydrated = signal(false);\n\n // โโ Event handler โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // โโ Lifecycle โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n constructor() {\n this.captureAntiFlashClass();\n\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.resolvedTheme()));\n afterNextRender(() => this.isHydrated.set(true));\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // โโ Public API โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved theme did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#validThemes.has(theme)) {\n throw new NgxThemeStackError(\n `Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n if (!this.#isBrowser) return;\n if (theme === this.#selectedTheme()) return;\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // โโ Private โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n const theme = this.#initialStoredTheme;\n if (theme && this.#validThemes.has(theme as NgTheme)) {\n return theme as NgTheme;\n }\n return this.#config.defaultTheme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n\n if (this.#antiFlashClass) {\n host.classList.remove(this.#antiFlashClass);\n this.#antiFlashClass = null;\n }\n\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, theme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, theme);\n }\n\n this.applyColorSchemeHint(host, theme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n host.classList.remove(...this.availableThemes);\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private captureAntiFlashClass(): void {\n if (!this.#isBrowser || !this.#initialStoredTheme) return;\n\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(this.#initialStoredTheme)) {\n this.#antiFlashClass = null;\n return;\n }\n\n if (this.#initialStoredTheme === 'system') {\n this.#antiFlashClass = this.resolveSystemPreference();\n return;\n }\n\n this.#antiFlashClass = this.#initialStoredTheme;\n }\n\n private readStoredTheme(): string | null {\n if (!this.#isBrowser) return null;\n\n try {\n return localStorage.getItem(this.#config.storageKey);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { computed, inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'system'` โ `'light'` โ `'dark'` โ `'system'` โ ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to `['light', 'dark', 'system']`. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** Index of the currently selected theme in the cycle. */\n readonly cycleIndex = computed(() => {\n return this.availableThemes.indexOf(this.selectedTheme());\n });\n\n /** The theme that comes before the currently selected theme in the cycle. */\n readonly preceding = computed(() => {\n const index = this.cycleIndex();\n const len = this.availableThemes.length;\n return this.availableThemes[(index - 1 + len) % len];\n });\n\n /** The theme that comes after the currently selected theme in the cycle. */\n readonly upcoming = computed(() => {\n const index = this.cycleIndex();\n return this.availableThemes[(index + 1) % this.availableThemes.length];\n });\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n this.#core.setTheme(this.upcoming());\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to `['light', 'dark', 'system']`. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme currently applied to the DOM. Always concrete โ never `'system'`. */\n readonly resolvedTheme = this.#core.resolvedTheme;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Whether the currently applied theme is `'dark'`. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is `'light'`. */\n readonly isLight = this.#core.isLight;\n\n /** Whether the user has explicitly selected `'system'` preference. */\n readonly isSystem = this.#core.isSystem;\n\n /**\n * Whether the service has completed client-side initialization and\n * resolved the real persisted theme. Use to prevent hydration flashes.\n */\n readonly isHydrated = this.#core.isHydrated.asReadonly();\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.resolvedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\nexport * from './lib/errors';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;;;;;;;;;;;;;AAeG;AACG,MAAO,kBAAmB,SAAQ,KAAK,CAAA;IACzB,IAAI,GAAG,oBAAoB;AAE7C,IAAA,WAAA,CAAY,OAAe,EAAA;AACzB,QAAA,KAAK,CAAC,CAAA,kBAAA,EAAqB,OAAO,CAAA,CAAE,CAAC;;QAErC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;IACnD;AACD;;ACxBD;;;;;;;;;;;;AAYG;AACI,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM;;ACTxD;;;;;;;;;;;AAWG;AAEI,MAAM,iBAAiB,GAAG;AAC/B,IAAA,YAAY,EAAE,QAAQ;AACtB,IAAA,UAAU,EAAE,iBAAiB;AAC7B,IAAA,IAAI,EAAE,OAAO;AACb,IAAA,QAAQ,EAAE,UAAU;AACpB,IAAA,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC;;AAG7B;AACA;AACA;MACa,sBAAsB,GAAG,IAAI,cAAc,CACtD,wBAAwB,EACxB;AACE,IAAA,OAAO,EAAE,MAAM,iBAAiB;AACjC,CAAA;AAGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDG;AACG,SAAU,iBAAiB,CAC/B,MAAA,GAA+B,EAAE,EAAA;IAEjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAI;AAC3B,QAAA,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;AAAE,YAAA,MAAM,IAAI,kBAAkB,CAAC,sCAAsC,CAAC;AAC3F,IAAA,CAAC,CAAC;AAEF,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC;UAClB,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AACrE,UAAE,iBAAiB,CAAC,MAAM;AAE5B,IAAA,IAAI,MAAM,CAAC,YAAY,IAAI,CAAE,MAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAsB,CAAC,EAAE;AACxF,QAAA,MAAM,IAAI,kBAAkB,CAC1B,CAAA,oDAAA,EAAuD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,EAAA,CAAI,CAC7E;IACH;AAEA,IAAA,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACtE,QAAA,MAAM,IAAI,kBAAkB,CAAC,6CAA6C,CAAC;IAC7E;IAEA,OAAO;AACL,QAAA,OAAO,EAAE,sBAAsB;AAC/B,QAAA,QAAQ,EAAE;AACR,YAAA,GAAG,iBAAiB;AACpB,YAAA,GAAG,MAAM;YACT,MAAM;AACa,SAAA;KACtB;AACH;;ACvGA;;;;;AAKG;MAEU,gBAAgB,CAAA;;AAGlB,IAAA,OAAO,GAAG,MAAM,CAAC,sBAAsB,CAAC;AACxC,IAAA,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;AAChC,IAAA,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC5B,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;;;AAKnD,IAAA,mBAAmB,GAAG,IAAI,CAAC,eAAe,EAAE;;AAG5C,IAAA,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;;IAGrC,YAAY,GAAG,IAAI,GAAG,CAAU,IAAI,CAAC,eAAe,CAAC;AAE9D;;;;AAIG;IACH,eAAe,GAAkB,IAAI;;;IAK5B,WAAW,GAA0B,IAAI,CAAC;AACjD,WAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,UAAU,CAAC,8BAA8B,CAAC,IAAI,IAAI;UAC/E,IAAI;IAEC,iBAAiB,GAAG,MAAM,CAAgB,IAAI,CAAC,uBAAuB,EAAE,wFAAC;;IAIzE,cAAc,GAAG,MAAM,CAAU,IAAI,CAAC,mBAAmB,EAAE,qFAAC;;AAG5D,IAAA,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE;;AAGhD,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AACrC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE;AACnC,QAAA,OAAO,KAAK,KAAK,QAAQ,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,KAAK;AAC9D,IAAA,CAAC,oFAAC;AAEF;;;;;AAKG;AACM,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,MAAM,6EAAC;AAEjE;;;;;AAKG;AACM,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,OAAO,8EAAC;;AAG1D,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,QAAQ,+EAAC;AAErE;;;;;;;;;;;;;;;AAeG;AACM,IAAA,UAAU,GAAG,MAAM,CAAC,KAAK,iFAAC;;AAI1B,IAAA,yBAAyB,GAAG,MACnC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;;AAI5D,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,qBAAqB,EAAE;QAE5B,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,QAAQ,EAAE;YACzD,IAAI,CAAC,wBAAwB,EAAE;QACjC;AAEA,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;AACxD,QAAA,eAAe,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChD,QAAA,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,uBAAuB,EAAE,CAAC;IAClE;;AAIA;;;;;;;;;AASG;AACI,IAAA,QAAQ,CAAC,KAAc,EAAA;QAC5B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;AACjC,YAAA,MAAM,IAAI,kBAAkB,CAC1B,mBAAmB,KAAK,CAAA,qBAAA,EAAwB,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAA,CAAG,CACrF;QACH;QACA,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE;AACtB,QAAA,IAAI,KAAK,KAAK,IAAI,CAAC,cAAc,EAAE;YAAE;AACrC,QAAA,IAAI,KAAK,KAAK,QAAQ,EAAE;YACtB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC1D,IAAI,CAAC,wBAAwB,EAAE;QACjC;aAAO;YACL,IAAI,CAAC,uBAAuB,EAAE;QAChC;AAEA,QAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;AAC9B,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;IACvB;;IAIQ,uBAAuB,GAAA;AAC7B,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO;IACrD;IAEQ,mBAAmB,GAAA;AACzB,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB;QACtC,IAAI,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAgB,CAAC,EAAE;AACpD,YAAA,OAAO,KAAgB;QACzB;AACA,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY;IAClC;IAEQ,wBAAwB,GAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE;QACvB,IAAI,CAAC,uBAAuB,EAAE;QAC9B,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,yBAAyB,CAAC;IAC7E;IAEQ,uBAAuB,GAAA;QAC7B,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,yBAAyB,CAAC;IACjF;AAEQ,IAAA,eAAe,CAAC,KAAc,EAAA;QACpC,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE;AAEtB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe;AAE3C,QAAA,IAAI,IAAI,CAAC,eAAe,EAAE;YACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC;AAC3C,YAAA,IAAI,CAAC,eAAe,GAAG,IAAI;QAC7B;AAEA,QAAA,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO;QAE7B,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,EAAE;AAC3C,YAAA,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC;QACvC;QAEA,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,MAAM,EAAE;AACvC,YAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC;QACrC;AAEA,QAAA,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC;IACxC;IAEQ,mBAAmB,CAAC,IAAiB,EAAE,KAAc,EAAA;AAC3D,QAAA,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC;IACxC;IAEQ,iBAAiB,CAAC,IAAiB,EAAE,KAAc,EAAA;QACzD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;AAC9C,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;IAC3B;IAEQ,oBAAoB,CAAC,IAAiB,EAAE,KAAc,EAAA;QAC5D,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE;YACzC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,EAAE,KAAK,CAAC;YAC7C;QACF;AAEA,QAAA,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC3C;IAEQ,qBAAqB,GAAA;QAC3B,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAAE;QAEnD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE;AAC9D,YAAA,IAAI,CAAC,eAAe,GAAG,IAAI;YAC3B;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,mBAAmB,KAAK,QAAQ,EAAE;AACzC,YAAA,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,uBAAuB,EAAE;YACrD;QACF;AAEA,QAAA,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,mBAAmB;IACjD;IAEQ,eAAe,GAAA;QACrB,IAAI,CAAC,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO,IAAI;AAEjC,QAAA,IAAI;YACF,OAAO,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACtD;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,OAAO,CAAC,IAAI,CAAC,2DAA2D,EAAE,CAAC,CAAC;AAC5E,YAAA,OAAO,IAAI;QACb;IACF;AAEQ,IAAA,SAAS,CAAC,KAAc,EAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE;AAEtB,QAAA,IAAI;YACF,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;QACtD;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,OAAO,CAAC,IAAI,CAAC,yDAAyD,EAAE,CAAC,CAAC;QAC5E;IACF;uGA3OW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,gBAAgB,cADH,MAAM,EAAA,CAAA;;2FACnB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACnBlC;;;;;;;AAOG;MAEU,iBAAiB,CAAA;AACnB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;;AAGhC,IAAA,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe;;AAG5C,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAClC,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;AAC3D,IAAA,CAAC,iFAAC;;AAGO,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AACjC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE;AAC/B,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM;AACvC,QAAA,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC;AACtD,IAAA,CAAC,gFAAC;;AAGO,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAK;AAChC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE;AAC/B,QAAA,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;AACxE,IAAA,CAAC,+EAAC;;AAGO,IAAA,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;;AAG1B,IAAA,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO;;AAG5B,IAAA,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ;AAEvC;;;AAGG;IACM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE;AAExD;;;;;;;AAOG;IACH,KAAK,GAAA;QACH,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACtC;uGAvDW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,cADJ,MAAM,EAAA,CAAA;;2FACnB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACPlC;;;;;AAKG;MAEU,kBAAkB,CAAA;AACpB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;;AAGhC,IAAA,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe;;AAG5C,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;;AAG1B,IAAA,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO;;AAG5B,IAAA,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ;AAEvC;;;AAGG;IACM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE;AAExD;;;;;AAKG;AACH,IAAA,MAAM,CAAC,KAAc,EAAA;AACnB,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC5B;uGAnCW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACPlC;;;;;AAKG;MAEU,kBAAkB,CAAA;AACpB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;;AAGhC,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa;;AAGxC,IAAA,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;;AAG1B,IAAA,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO;;AAG5B,IAAA,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ;AAEvC;;;AAGG;IACM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE;AAExD;;;;;AAKG;IACH,MAAM,GAAA;AACJ,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,MAAM,GAAG,OAAO,GAAG,MAAM;AACrE,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC3B;uGAjCW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACTlC;;AAEG;;ACFH;;AAEG;;;;"}
|
package/package.json
CHANGED
package/schematics/sync/index.js
CHANGED
|
@@ -37,9 +37,14 @@ const OPTION_DEFAULT_THEME_RE = /defaultTheme\s*:\s*['"]([^'"]+)['"]/;
|
|
|
37
37
|
const OPTION_STRATEGY_RE = /strategy\s*:\s*['"]([^'"]+)['"]/;
|
|
38
38
|
/**
|
|
39
39
|
* Extracts the themes array from the options string.
|
|
40
|
-
*
|
|
40
|
+
* Supports both single-line and multiline array declarations:
|
|
41
|
+
* themes: ['light', 'dark', 'sunset']
|
|
42
|
+
* themes: [
|
|
43
|
+
* 'light',
|
|
44
|
+
* 'dark',
|
|
45
|
+
* ] as const
|
|
41
46
|
*/
|
|
42
|
-
const OPTION_THEMES_RE = /themes\s*:\s*\[([
|
|
47
|
+
const OPTION_THEMES_RE = /themes\s*:\s*\[([\s\S]*?)\]/;
|
|
43
48
|
/** Matches the complete <script> anti-flash block (identified by its marker comment). */
|
|
44
49
|
const SCRIPT_BLOCK_RE = /<!--\s*ngx-theme-stack\s*anti-flash\s*-->\s*<script[^>]*>[\s\S]*?<\/script>/;
|
|
45
50
|
/** Marker injected by ng-add that delimits the Critters Trick zone in <body>. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/sync/index.ts"],"names":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/sync/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -72,7 +72,22 @@ interface NgConfig<T extends string = string & {}> {
|
|
|
72
72
|
* Use 'blocking' to load themes.css as a render-blocking stylesheet (HTTP-cacheable).
|
|
73
73
|
*/
|
|
74
74
|
strategy: NgStrategy;
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* The **resolved** list of supported theme identifiers, always including the
|
|
77
|
+
* built-in themes (`'light'`, `'dark'`, `'system'`).
|
|
78
|
+
*
|
|
79
|
+
* When you pass custom themes to {@link provideThemeStack}, they are **merged**
|
|
80
|
+
* with the built-in defaults โ your custom values are appended after them.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* // Input to provideThemeStack:
|
|
84
|
+
* themes: ['sepia', 'ocean'] as const
|
|
85
|
+
*
|
|
86
|
+
* // Resolved value stored in NgConfig:
|
|
87
|
+
* // ['system', 'light', 'dark', 'sepia', 'ocean']
|
|
88
|
+
*
|
|
89
|
+
* Default (no custom themes): `['system', 'light', 'dark']`.
|
|
90
|
+
*/
|
|
76
91
|
themes: NgTheme<T>[];
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -90,9 +105,19 @@ declare class CoreThemeService {
|
|
|
90
105
|
readonly selectedTheme: _angular_core.Signal<NgTheme>;
|
|
91
106
|
/** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) โ never `'system'`. */
|
|
92
107
|
readonly resolvedTheme: _angular_core.Signal<(string & {}) | NgSystemTheme>;
|
|
93
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Whether the currently applied theme is dark.
|
|
110
|
+
*
|
|
111
|
+
* Returns `false` when a custom theme (e.g. `'sunset'`) is active โ use
|
|
112
|
+
* `resolvedTheme()` directly if you need to inspect the current custom theme.
|
|
113
|
+
*/
|
|
94
114
|
readonly isDark: _angular_core.Signal<boolean>;
|
|
95
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Whether the currently applied theme is light.
|
|
117
|
+
*
|
|
118
|
+
* Returns `false` when a custom theme (e.g. `'sepia'`) is active โ use
|
|
119
|
+
* `resolvedTheme()` directly if you need to inspect the current custom theme.
|
|
120
|
+
*/
|
|
96
121
|
readonly isLight: _angular_core.Signal<boolean>;
|
|
97
122
|
/** Whether the currently applied theme is system. */
|
|
98
123
|
readonly isSystem: _angular_core.Signal<boolean>;
|
|
@@ -163,8 +188,10 @@ declare const NGX_THEME_STACK_CONFIG: InjectionToken<NgConfig<string>>;
|
|
|
163
188
|
/**
|
|
164
189
|
* Provides Theme Stack configuration to Angular's DI system.
|
|
165
190
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
191
|
+
* The `themes` option accepts **additional** theme identifiers beyond the
|
|
192
|
+
* built-in defaults. They are merged with `'light'`, `'dark'`, and `'system'`
|
|
193
|
+
* so that the resolved {@link NgConfig.themes} array always includes all of
|
|
194
|
+
* them โ your custom values are appended after the built-ins.
|
|
168
195
|
*
|
|
169
196
|
* **Defaults:**
|
|
170
197
|
* - `themes`: `['light', 'dark', 'system']`
|