ngx-theme-stack 3.8.2 → 3.8.4
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 +73 -16
- package/package.json +1 -1
- package/schematics/ng-add/index.js +48 -10
- package/schematics/ng-add/index.js.map +1 -1
- package/schematics/skill/index.js +97 -229
- package/schematics/skill/index.js.map +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Built for performance and SSR support.
|
|
|
27
27
|
- [🏗️ Architecture & Extensibility](#️-architecture--extensibility)
|
|
28
28
|
- [📐 Supported Versions](#-supported-versions)
|
|
29
29
|
- [⚙️ Configuration](#️-configuration)
|
|
30
|
+
- [🔄 Theme Synchronization](#-theme-synchronization)
|
|
30
31
|
- [🛠️ Usage](#️-usage)
|
|
31
32
|
- [🛡️ CoreThemeService API](#️-advanced-corethemeservice-api)
|
|
32
33
|
- [🎨 Styling](#-styling)
|
|
@@ -102,7 +103,7 @@ The installation command automates the following:
|
|
|
102
103
|
| --------------- | ----------------------------------------------------------------------- |
|
|
103
104
|
| `app.config.ts` | Injects `provideThemeStack()` using AST — follows imports automatically |
|
|
104
105
|
| `index.html` | Injects the blocking anti-flash script into `<head>` |
|
|
105
|
-
| `package.json` | Adds a `"
|
|
106
|
+
| `package.json` | Adds a central `"ngx-theme-stack:sync"` script and configures `"prestart"` / `"prebuild"` hooks to run it automatically |
|
|
106
107
|
| `angular.json` | Registers `themes.css` and optimizes build config |
|
|
107
108
|
| `themes.css` | Scaffolds base theme tokens if they don't exist |
|
|
108
109
|
| `SKILL.md` | Generates an AI Agent Skill under `.agents/skills/` (optional) |
|
|
@@ -163,12 +164,68 @@ export const appConfig: ApplicationConfig = {
|
|
|
163
164
|
| `strategy` | `NgStrategy` | `'critters'` | Anti-flash strategy |
|
|
164
165
|
| `storageKey` | `string` | `'ngx-theme-stack'` | Persistence key |
|
|
165
166
|
|
|
166
|
-
> [!
|
|
167
|
-
> Custom themes are **merged** with built-ins. Passing `['sepia', 'ocean']` resolves to `['system', 'light', 'dark', 'sepia', 'ocean']`.
|
|
168
|
-
>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
> [!IMPORTANT]
|
|
168
|
+
> Custom themes are **merged** with built-ins. Passing `['sepia', 'ocean']` resolves to `['system', 'light', 'dark', 'sepia', 'ocean']`.
|
|
169
|
+
> After making any configuration changes, you **must** synchronize the workspace. See [🔄 Theme Synchronization](#-theme-synchronization) below for instructions and when to run it.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 🔄 Theme Synchronization
|
|
174
|
+
|
|
175
|
+
The synchronization schematic validates and compiles your theme configuration in `app.config.ts` into static assets (like the zero-flash anti-flash script in `index.html` and critters placeholders).
|
|
176
|
+
|
|
177
|
+
### Centralized Script
|
|
178
|
+
|
|
179
|
+
The installation schematic registers a centralized `"ngx-theme-stack:sync"` script in your `package.json` to compile configurations:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Run manually using your package manager of choice:
|
|
183
|
+
npm run ngx-theme-stack:sync
|
|
184
|
+
pnpm run ngx-theme-stack:sync
|
|
185
|
+
yarn run ngx-theme-stack:sync
|
|
186
|
+
bun run ngx-theme-stack:sync
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### When should you run sync?
|
|
190
|
+
|
|
191
|
+
You should run this command after:
|
|
192
|
+
- ➕ **Adding or removing** themes in `provideThemeStack`
|
|
193
|
+
- 🏷️ **Renaming** a theme identifier
|
|
194
|
+
- ⚙️ **Changing** configuration settings like `storageKey`, `mode`, or `strategy`
|
|
195
|
+
- 📝 **Manually editing** the anti-flash script in your `index.html` file
|
|
196
|
+
|
|
197
|
+
### Development vs. Production (Serving vs. Building)
|
|
198
|
+
|
|
199
|
+
To prevent you from having to run this command manually, the `ng add` command automatically detects your package manager and registers hooks in your `package.json` (appending/prepending safely to any existing scripts):
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
"scripts": {
|
|
203
|
+
"ngx-theme-stack:sync": "ng generate ngx-theme-stack:sync --project YOUR_PROJECT_NAME",
|
|
204
|
+
"prestart": "npm run ngx-theme-stack:sync",
|
|
205
|
+
"start": "ng serve",
|
|
206
|
+
"prebuild": "npm run ngx-theme-stack:sync",
|
|
207
|
+
"build": "ng build"
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
This ensures that the theme configuration is synchronized automatically before running your local development server (`npm start` / `pnpm start` / `yarn start` / `bun start`) and before production builds (`ng build`).
|
|
212
|
+
|
|
213
|
+
> [!WARNING]
|
|
214
|
+
> If you run `ng serve` or `ng build` directly from the CLI (bypassing npm/pnpm/yarn/bun scripts), the package manager hooks (`prestart` and `prebuild`) will not run. In this case, you must run the synchronization command manually after making configuration changes.
|
|
215
|
+
|
|
216
|
+
### Troubleshooting: Theme reverts to Default/System on reload
|
|
217
|
+
|
|
218
|
+
If you select a newly added custom theme (e.g. `'sunset'`) and reload the page, but the page reverts to the default theme (e.g. `'system'`), the theme is being rejected by the anti-flash script.
|
|
219
|
+
|
|
220
|
+
**Diagnostic steps:**
|
|
221
|
+
1. Open your `src/index.html` file.
|
|
222
|
+
2. Locate the `<script>` tag inside `<head>` marked with `<!-- ngx-theme-stack anti-flash -->`.
|
|
223
|
+
3. Check the valid themes array (`v`) defined in that script (e.g., `v=["system","light","dark"]`).
|
|
224
|
+
4. **Verify if your custom theme is missing from this array.** To prevent layout flicker and XSS injections, the blocking script rejects any theme not explicitly registered in this array and falls back to the default.
|
|
225
|
+
5. If it is missing, run the synchronization script:
|
|
226
|
+
```bash
|
|
227
|
+
npm run ngx-theme-stack:sync
|
|
228
|
+
```
|
|
172
229
|
|
|
173
230
|
---
|
|
174
231
|
|
|
@@ -303,18 +360,18 @@ export class MyAdvancedComponent {
|
|
|
303
360
|
|
|
304
361
|
:root,
|
|
305
362
|
.light {
|
|
306
|
-
--
|
|
307
|
-
--
|
|
363
|
+
--background: #ffffff;
|
|
364
|
+
--foreground: #333333;
|
|
308
365
|
}
|
|
309
366
|
|
|
310
367
|
.dark {
|
|
311
|
-
--
|
|
312
|
-
--
|
|
368
|
+
--background: #121212;
|
|
369
|
+
--foreground: #ffffff;
|
|
313
370
|
}
|
|
314
371
|
|
|
315
372
|
.sunset {
|
|
316
|
-
--
|
|
317
|
-
--
|
|
373
|
+
--background: #ff5f6d;
|
|
374
|
+
--foreground: #ffffff;
|
|
318
375
|
}
|
|
319
376
|
```
|
|
320
377
|
|
|
@@ -329,15 +386,15 @@ export class MyAdvancedComponent {
|
|
|
329
386
|
@import 'tailwindcss';
|
|
330
387
|
|
|
331
388
|
@theme {
|
|
332
|
-
--color-
|
|
333
|
-
--color-
|
|
389
|
+
--color-background: var(--background);
|
|
390
|
+
--color-foreground: var(--foreground);
|
|
334
391
|
}
|
|
335
392
|
```
|
|
336
393
|
|
|
337
394
|
### Use in components — no `dark:` prefix needed
|
|
338
395
|
|
|
339
396
|
```html
|
|
340
|
-
<div class="bg-
|
|
397
|
+
<div class="bg-background text-foreground shadow-xl">
|
|
341
398
|
<!-- automatically reflects the active theme -->
|
|
342
399
|
</div>
|
|
343
400
|
```
|
package/package.json
CHANGED
|
@@ -112,7 +112,16 @@ function ngAdd(options) {
|
|
|
112
112
|
t.create(themesPath, content);
|
|
113
113
|
changeset.push(` \u001b[36mA\u001b[0m ${themesPath} (theme tokens)`);
|
|
114
114
|
}
|
|
115
|
-
function
|
|
115
|
+
function detectPackageManager(t) {
|
|
116
|
+
if (t.exists('/pnpm-lock.yaml'))
|
|
117
|
+
return 'pnpm';
|
|
118
|
+
if (t.exists('/yarn.lock'))
|
|
119
|
+
return 'yarn';
|
|
120
|
+
if (t.exists('/bun.lockb') || t.exists('/bun.lock'))
|
|
121
|
+
return 'bun';
|
|
122
|
+
return 'npm';
|
|
123
|
+
}
|
|
124
|
+
function patchPackageJsonScripts(t) {
|
|
116
125
|
const pkgPath = '/package.json';
|
|
117
126
|
const buffer = t.read(pkgPath);
|
|
118
127
|
if (!buffer)
|
|
@@ -120,17 +129,46 @@ function ngAdd(options) {
|
|
|
120
129
|
const pkg = JSON.parse(buffer.toString());
|
|
121
130
|
pkg.scripts = pkg.scripts || {};
|
|
122
131
|
const syncCmd = `ng generate ngx-theme-stack:sync --project ${projectName}`;
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
const pm = detectPackageManager(t);
|
|
133
|
+
const pmRunCmd = `${pm} run ngx-theme-stack:sync`;
|
|
134
|
+
// 1. Add/Update main sync script
|
|
135
|
+
const existingSync = pkg.scripts['ngx-theme-stack:sync'];
|
|
136
|
+
if (!existingSync) {
|
|
137
|
+
pkg.scripts['ngx-theme-stack:sync'] = syncCmd;
|
|
138
|
+
changeset.push(' \u001b[33mM\u001b[0m package.json (ngx-theme-stack:sync script added)');
|
|
139
|
+
}
|
|
140
|
+
else if (existingSync !== syncCmd) {
|
|
141
|
+
pkg.scripts['ngx-theme-stack:sync'] = syncCmd;
|
|
142
|
+
changeset.push(' \u001b[33mM\u001b[0m package.json (ngx-theme-stack:sync script updated)');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
changeset.push(' \u001b[90mℹ\u001b[0m package.json (ngx-theme-stack:sync script already correct — skipped)');
|
|
146
|
+
}
|
|
147
|
+
// 2. Patch prebuild (runs before production builds)
|
|
148
|
+
const existingPrebuild = pkg.scripts.prebuild;
|
|
149
|
+
if (!existingPrebuild) {
|
|
150
|
+
pkg.scripts.prebuild = pmRunCmd;
|
|
151
|
+
changeset.push(` \u001b[33mM\u001b[0m package.json (prebuild script added with ${pmRunCmd})`);
|
|
152
|
+
}
|
|
153
|
+
else if (!existingPrebuild.includes('ngx-theme-stack:sync')) {
|
|
154
|
+
pkg.scripts.prebuild = `${existingPrebuild} && ${pmRunCmd}`;
|
|
155
|
+
changeset.push(' \u001b[33mM\u001b[0m package.json (sync run appended to existing prebuild)');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
changeset.push(' \u001b[90mℹ\u001b[0m package.json (prebuild already contains sync run — skipped)');
|
|
159
|
+
}
|
|
160
|
+
// 3. Patch prestart (runs before local development server)
|
|
161
|
+
const existingPrestart = pkg.scripts.prestart;
|
|
162
|
+
if (!existingPrestart) {
|
|
163
|
+
pkg.scripts.prestart = pmRunCmd;
|
|
164
|
+
changeset.push(` \u001b[33mM\u001b[0m package.json (prestart script added with ${pmRunCmd})`);
|
|
127
165
|
}
|
|
128
|
-
else if (!
|
|
129
|
-
pkg.scripts.
|
|
130
|
-
changeset.push(' \u001b[33mM\u001b[0m package.json (sync
|
|
166
|
+
else if (!existingPrestart.includes('ngx-theme-stack:sync')) {
|
|
167
|
+
pkg.scripts.prestart = `${pmRunCmd} && ${existingPrestart}`;
|
|
168
|
+
changeset.push(' \u001b[33mM\u001b[0m package.json (sync run prepended to existing prestart)');
|
|
131
169
|
}
|
|
132
170
|
else {
|
|
133
|
-
changeset.push(' \u001b[90mℹ\u001b[0m package.json (
|
|
171
|
+
changeset.push(' \u001b[90mℹ\u001b[0m package.json (prestart already contains sync run — skipped)');
|
|
134
172
|
}
|
|
135
173
|
t.overwrite(pkgPath, JSON.stringify(pkg, null, 2));
|
|
136
174
|
}
|
|
@@ -177,7 +215,7 @@ function ngAdd(options) {
|
|
|
177
215
|
}
|
|
178
216
|
return (0, schematics_1.chain)([
|
|
179
217
|
scaffoldThemesCss,
|
|
180
|
-
|
|
218
|
+
patchPackageJsonScripts,
|
|
181
219
|
patchAngularJson,
|
|
182
220
|
scaffoldAgentSkill,
|
|
183
221
|
patchProviderAndIndexHtml,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":";;;;;;;;;;;AAmEA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,306 +2,174 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateSkill = generateSkill;
|
|
4
4
|
exports.skill = skill;
|
|
5
|
-
// ── SKILL.md (Tier 2 — loaded on activation)
|
|
5
|
+
// ── SKILL.md (Tier 2 — loaded on activation) ─────────
|
|
6
6
|
const SKILL_CONTENT = `---
|
|
7
7
|
name: ngx-theme-stack
|
|
8
|
-
description:
|
|
9
|
-
Use when installing, configuring, or building theme-switching UI with
|
|
10
|
-
ngx-theme-stack in Angular 20+. Covers provideThemeStack setup,
|
|
11
|
-
ThemeToggle/Cycle/Select services, SSR hydration guards, CSS variable
|
|
12
|
-
theming, and Tailwind CSS v4 integration. Do not use for generic Angular
|
|
13
|
-
styling, standalone dark-mode CSS, or projects not using ngx-theme-stack.
|
|
8
|
+
description: Signal-based theme manager for Angular 20+. Covers setup, services, SSR guards, and Tailwind v4.
|
|
14
9
|
compatibility: Angular 20+ with TypeScript. Optional Tailwind CSS v4.
|
|
15
10
|
metadata:
|
|
16
11
|
author: WanderleeDev
|
|
17
|
-
version:
|
|
12
|
+
version: '1.1.0'
|
|
18
13
|
---
|
|
19
14
|
|
|
20
15
|
# ngx-theme-stack
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
Supports dark/light/system/custom themes, SSR, and zero-flash rendering.
|
|
24
|
-
|
|
25
|
-
## Architecture
|
|
26
|
-
|
|
27
|
-
\`\`\`
|
|
28
|
-
provideThemeStack() ← DI configuration (app.config.ts)
|
|
29
|
-
│
|
|
30
|
-
CoreThemeService ← Foundation: state, persistence, DOM updates
|
|
31
|
-
│
|
|
32
|
-
┌────┼────────────┐
|
|
33
|
-
│ │ │
|
|
34
|
-
Toggle Cycle Select ← Convenience services (pick ONE per component)
|
|
35
|
-
\`\`\`
|
|
17
|
+
Headless, signal-based theme manager for Angular 20+.
|
|
36
18
|
|
|
37
19
|
## Interaction Rules
|
|
38
20
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
> 2. **Cycle** (\`ThemeCycleService\`) — Rotates through all configured themes (best for 3+ themes with a single button).
|
|
46
|
-
> 3. **Select** (\`ThemeSelectService\`) — Full dropdown/radio list of all available themes (best for settings pages or explicit pickers).
|
|
47
|
-
>
|
|
48
|
-
> **Custom Themes Inquiry**: In addition to the service choice, you **MUST** ask the user if they want to configure or need any **custom themes** (e.g. \`sunset\`, \`ocean\`, \`sepia\`) beyond the default \`light\`, \`dark\`, and \`system\`. Prompt the user for details on what they are looking for or need (such as specific custom theme names, colors, or CSS variables) to help them scaffold these customizations.
|
|
49
|
-
>
|
|
50
|
-
> **DO NOT** generate any component code or configurations until the user has explicitly responded to these questions.
|
|
51
|
-
|
|
52
|
-
## Constraints
|
|
53
|
-
|
|
54
|
-
- Always import from \`'ngx-theme-stack'\` — never deep-import internal paths.
|
|
55
|
-
- Call \`provideThemeStack()\` exactly once, in the root \`app.config.ts\` providers.
|
|
56
|
-
- Custom themes are **merged** with built-ins. Passing \`['sepia']\` resolves to \`['system', 'light', 'dark', 'sepia']\`.
|
|
57
|
-
- **Mandatory Theme Synchronization**: After configuring, adding, or modifying themes in \`provideThemeStack()\`, you **MUST** run the synchronization schematic command: \`ng generate ngx-theme-stack:sync --project PROJECT_NAME\` (or explicitly instruct the user to run it if you cannot execute commands). Failing to synchronize themes after modification is a critical violation that breaks theme compilation.
|
|
58
|
-
- \`isDark()\` and \`isLight()\` return \`false\` for custom themes — use \`resolvedTheme()\` directly.
|
|
59
|
-
- **Mandatory SSR Guard**: You MUST guard all theme-dependent template elements (e.g. text, icons, images, styling classes based on theme signals) using \`@if (theme.isHydrated())\`. Using theme signals (\`isDark()\`, \`resolvedTheme()\`, etc.) directly in templates without checking \`isHydrated()\` causes content flickering and critical Angular hydration mismatch errors in SSR.
|
|
60
|
-
- Never call \`setTheme()\` with a theme name not registered in the \`themes\` array — it throws \`NgxThemeStackError\`.
|
|
61
|
-
- Pick ONE convenience service per component — do not mix Toggle, Cycle, and Select in the same component.
|
|
62
|
-
|
|
63
|
-
## Installation
|
|
64
|
-
|
|
65
|
-
\`\`\`bash
|
|
66
|
-
ng add ngx-theme-stack
|
|
67
|
-
\`\`\`
|
|
68
|
-
|
|
69
|
-
For Bun environments (where \`ng add\` is unsupported):
|
|
70
|
-
\`\`\`bash
|
|
71
|
-
bun add ngx-theme-stack
|
|
72
|
-
ng generate ngx-theme-stack:ng-add
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
## Configuration
|
|
76
|
-
|
|
77
|
-
Configure in \`app.config.ts\` via \`provideThemeStack()\`:
|
|
78
|
-
|
|
79
|
-
\`\`\`typescript
|
|
80
|
-
import { provideThemeStack } from 'ngx-theme-stack';
|
|
81
|
-
|
|
82
|
-
export const appConfig: ApplicationConfig = {
|
|
83
|
-
providers: [
|
|
84
|
-
provideThemeStack({
|
|
85
|
-
themes: ['sunset', 'ocean'] as const,
|
|
86
|
-
defaultTheme: 'system',
|
|
87
|
-
mode: 'class',
|
|
88
|
-
strategy: 'critters',
|
|
89
|
-
storageKey: 'ngx-theme-stack',
|
|
90
|
-
}),
|
|
91
|
-
],
|
|
92
|
-
};
|
|
93
|
-
\`\`\`
|
|
94
|
-
|
|
95
|
-
| Option | Type | Default | Description |
|
|
96
|
-
| -------------- | ---------- | ----------------------------- | ---------------------------- |
|
|
97
|
-
| \`themes\` | \`string[]\` | \`['light', 'dark', 'system']\` | Merged with built-ins |
|
|
98
|
-
| \`defaultTheme\` | \`string\` | \`'system'\` | Theme on first visit |
|
|
99
|
-
| \`mode\` | \`NgMode\` | \`'class'\` | How the theme is applied |
|
|
100
|
-
| \`strategy\` | \`NgStrategy\`| \`'critters'\` | Anti-flash strategy |
|
|
101
|
-
| \`storageKey\` | \`string\` | \`'ngx-theme-stack'\` | localStorage persistence key |
|
|
102
|
-
|
|
103
|
-
## Services Quick Reference
|
|
104
|
-
|
|
105
|
-
| Service | Method | Use case |
|
|
106
|
-
| -------------------- | ------------ | --------------------------------- |
|
|
107
|
-
| \`ThemeToggleService\` | \`toggle()\` | Binary dark/light switch |
|
|
108
|
-
| \`ThemeCycleService\` | \`cycle()\` | Rotate through all themes |
|
|
109
|
-
| \`ThemeSelectService\` | \`select(t)\` | Dropdown / radio / tab selection |
|
|
110
|
-
| \`CoreThemeService\` | \`setTheme(t)\`| Advanced / low-level access |
|
|
111
|
-
|
|
112
|
-
All services expose these signals: \`selectedTheme()\`, \`resolvedTheme()\`, \`isDark()\`, \`isLight()\`, \`isSystem()\`, \`isHydrated()\`.
|
|
21
|
+
- **MANDATORY RULE**: If the user asks for theme components/switching, you **MUST** ask them to choose:
|
|
22
|
+
- **Toggle** (\`ThemeToggleService\`) - Binary dark/light toggle.
|
|
23
|
+
- **Cycle** (\`ThemeCycleService\`) - Rotate through all themes.
|
|
24
|
+
- **Select** (\`ThemeSelectService\`) - Full picker dropdown/radio selection.
|
|
25
|
+
- **Custom Themes Inquiry**: Ask if they want custom themes (e.g. \`sunset\`, colors, or CSS variables).
|
|
26
|
+
- **DO NOT** generate code or configs until the user responds to these questions.
|
|
113
27
|
|
|
114
|
-
|
|
28
|
+
## Constraints & Rules
|
|
115
29
|
|
|
116
|
-
|
|
30
|
+
- Call \`provideThemeStack()\` once in root \`app.config.ts\`. Custom themes merge with defaults.
|
|
31
|
+
- **Theme Synchronization**: Syncs theme configuration in \`app.config.ts\` with \`index.html\` assets.
|
|
32
|
+
- **Manual execution**: Run \`<package-manager> run ngx-theme-stack:sync\` (e.g., \`npm run ngx-theme-stack:sync\`).
|
|
33
|
+
- **Auto-Sync**: Runs automatically before serving or building via \`"prestart"\` and \`"prebuild"\` hooks in \`package.json\`.
|
|
34
|
+
- **When to sync**: Run after adding/removing themes, renaming themes, changing configuration settings (storageKey, mode, strategy), or manually editing index.html.
|
|
35
|
+
- **Debugging**: If a theme reverts to default/system on reload, check if the theme identifier is missing in the valid themes array (\`v\`) in \`index.html\`. If missing, run synchronization.
|
|
36
|
+
- \`isDark()\` / \`isLight()\` return false for custom themes (use \`resolvedTheme()\`).
|
|
37
|
+
- Pick ONE convenience service per component. Do not write custom localStorage or direct DOM logic.
|
|
117
38
|
|
|
118
|
-
## SSR Hydration
|
|
39
|
+
## SSR Hydration & Layout Stability
|
|
119
40
|
|
|
120
|
-
|
|
41
|
+
Wrap theme-dependent elements in \`@if (theme.isHydrated())\` to prevent layout shift and SSR mismatches. Fallback placeholders in \`@else\` must match the exact hydrated dimensions.
|
|
121
42
|
|
|
122
43
|
\`\`\`html
|
|
123
44
|
@if (theme.isHydrated()) {
|
|
124
|
-
|
|
45
|
+
<img [src]="theme.isDark() ? darkLogo : lightLogo" />
|
|
125
46
|
} @else {
|
|
126
|
-
|
|
127
|
-
<div class="logo-skeleton" style="width: 150px; height: 40px; display: inline-block;"></div>
|
|
47
|
+
<div class="logo-skeleton"></div>
|
|
128
48
|
}
|
|
129
49
|
\`\`\`
|
|
130
50
|
|
|
131
|
-
|
|
51
|
+
## Configuration & API
|
|
132
52
|
|
|
133
|
-
|
|
134
|
-
1. **Dimension & Spacing Match**: The fallback element (e.g., inside the \`@else\` block) MUST occupy the exact same space, size, margins, padding, positioning, and responsive constraints as the hydrated element. This ensures zero layout shift (preventing CLS issues) and keeps the Largest Contentful Paint (LCP) optimized.
|
|
135
|
-
2. **Granular Skeletons**: NEVER wrap an entire complex component or large layout block in an \`isHydrated()\` guard if only a single word, label, icon, or sub-element changes dynamically based on the theme. Instead, place the \`isHydrated()\` guard at the most granular level possible (e.g., directly wrapping just the dynamic text or icon inside the button), leaving the surrounding button wrapper and layout static. A full-element skeleton should only be used if the entire component's structure/layout is completely dynamic.
|
|
53
|
+
See [references/api-reference.md](references/api-reference.md) for APIs. Examples: [Toggle](assets/theme-toggle.ts) · [Cycle](assets/theme-cycle.ts) · [Select](assets/theme-select.ts).
|
|
136
54
|
|
|
137
|
-
|
|
55
|
+
\`\`\`typescript
|
|
56
|
+
import { provideThemeStack } from 'ngx-theme-stack';
|
|
57
|
+
export const appConfig = {
|
|
58
|
+
providers: [provideThemeStack({ themes: ['sunset'] as const, strategy: 'critters' })],
|
|
59
|
+
};
|
|
60
|
+
\`\`\`
|
|
138
61
|
|
|
139
|
-
|
|
62
|
+
| Service | Method | Signals |
|
|
63
|
+
| -------------------- | ----------- | ------------------------------------------------------------------------------------------- |
|
|
64
|
+
| \`ThemeToggleService\` | \`toggle()\` | \`selectedTheme()\`, \`resolvedTheme()\`, \`isDark()\`, \`isLight()\`, \`isSystem()\`, \`isHydrated()\` |
|
|
65
|
+
| \`ThemeCycleService\` | \`cycle()\` | |
|
|
66
|
+
| \`ThemeSelectService\` | \`select(t)\` | |
|
|
140
67
|
|
|
141
|
-
|
|
142
|
-
:root, .light { --bg: #fff; --text: #1a1a1a; }
|
|
143
|
-
.dark { --bg: #0a0a0a; --text: #f5f5f5; }
|
|
144
|
-
.sunset { --bg: #ff5f6d; --text: #fff; }
|
|
145
|
-
\`\`\`
|
|
68
|
+
## Styling: CSS Variables & Tailwind Separation
|
|
146
69
|
|
|
147
|
-
|
|
70
|
+
Define CSS variables in \`src/themes.css\` and map them to Tailwind in \`src/styles.css\` (use semantic classes, not \`dark:\`):
|
|
148
71
|
|
|
149
|
-
|
|
72
|
+
\`\`\`css
|
|
73
|
+
/* src/themes.css */
|
|
74
|
+
:root,
|
|
75
|
+
.light {
|
|
76
|
+
--background: #fff;
|
|
77
|
+
--foreground: #1a1a1a;
|
|
78
|
+
}
|
|
79
|
+
.dark {
|
|
80
|
+
--background: #0a0a0a;
|
|
81
|
+
--foreground: #f5f5f5;
|
|
82
|
+
}
|
|
83
|
+
.sunset {
|
|
84
|
+
--background: #ff5f6d;
|
|
85
|
+
--foreground: #fff;
|
|
86
|
+
}
|
|
87
|
+
\`\`\`
|
|
150
88
|
|
|
151
89
|
\`\`\`css
|
|
90
|
+
/* src/styles.css */
|
|
152
91
|
@import 'tailwindcss';
|
|
153
92
|
@theme {
|
|
154
|
-
--color-
|
|
155
|
-
--color-
|
|
93
|
+
--color-background: var(--background);
|
|
94
|
+
--color-foreground: var(--foreground);
|
|
156
95
|
}
|
|
157
96
|
\`\`\`
|
|
158
97
|
|
|
159
|
-
Use semantic classes — no \`dark:\` prefix needed: \`bg-bg text-text\`.
|
|
160
|
-
|
|
161
98
|
## Anti-patterns
|
|
162
99
|
|
|
163
|
-
- Do NOT
|
|
164
|
-
- Do NOT use
|
|
165
|
-
- Do NOT
|
|
166
|
-
- Do NOT use
|
|
167
|
-
- Do NOT skip \`ngx-theme-stack:sync\` after changing \`provideThemeStack()\` config.
|
|
100
|
+
- Do NOT mix Toggle, Cycle, and Select in the same component.
|
|
101
|
+
- Do NOT use Tailwind's \`dark:\` utility modifier (use semantic classes mapped from themes).
|
|
102
|
+
- Do NOT skip \`ngx-theme-stack:sync\` schematic after updating providers.
|
|
103
|
+
- Do NOT use theme signals in templates without an \`@if (theme.isHydrated())\` guard.
|
|
168
104
|
`;
|
|
169
|
-
// ── references/api-reference.md (Tier 3 — loaded on demand)
|
|
105
|
+
// ── references/api-reference.md (Tier 3 — loaded on demand) ──────────────────
|
|
170
106
|
const API_REFERENCE = `# ngx-theme-stack API Reference
|
|
171
107
|
|
|
172
108
|
## provideThemeStack(config?)
|
|
173
109
|
|
|
174
|
-
|
|
110
|
+
Configures the Theme Stack in \`app.config.ts\`. Custom themes merge with built-ins (\`system\`, \`light\`, \`dark\`).
|
|
175
111
|
|
|
176
112
|
\`\`\`typescript
|
|
177
113
|
provideThemeStack({
|
|
178
114
|
themes: ['sunset', 'ocean'] as const,
|
|
179
115
|
defaultTheme: 'system',
|
|
180
116
|
storageKey: 'ngx-theme-stack',
|
|
181
|
-
mode: 'class',
|
|
182
|
-
strategy: 'critters',
|
|
117
|
+
mode: 'class', // 'class' | 'attribute' | 'both'
|
|
118
|
+
strategy: 'critters', // 'critters' | 'blocking'
|
|
183
119
|
})
|
|
184
120
|
\`\`\`
|
|
185
121
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
### Throws \`NgxThemeStackError\` when:
|
|
190
|
-
- A theme entry is empty or whitespace-only.
|
|
191
|
-
- \`defaultTheme\` is not in the resolved themes array.
|
|
192
|
-
- \`storageKey\` is empty or whitespace-only.
|
|
122
|
+
**Throws \`NgxThemeStackError\` when:**
|
|
123
|
+
- A theme entry is empty, or \`defaultTheme\` is not in themes, or \`storageKey\` is empty.
|
|
193
124
|
|
|
194
125
|
---
|
|
195
126
|
|
|
196
127
|
## CoreThemeService
|
|
197
128
|
|
|
198
|
-
Foundation service
|
|
199
|
-
system preference detection (matchMedia), and safe DOM manipulation (SSR compatible).
|
|
200
|
-
|
|
201
|
-
### Signals
|
|
202
|
-
|
|
203
|
-
| Signal | Type | Description |
|
|
204
|
-
| ------------------ | ------------------- | --------------------------------------------------- |
|
|
205
|
-
| \`selectedTheme()\` | \`Signal<string>\` | Theme chosen by the user. May be \`'system'\`. |
|
|
206
|
-
| \`resolvedTheme()\` | \`Signal<string>\` | Theme applied to DOM. Never \`'system'\`. |
|
|
207
|
-
| \`isDark()\` | \`Signal<boolean>\` | \`true\` when resolved is \`'dark'\`. \`false\` for custom. |
|
|
208
|
-
| \`isLight()\` | \`Signal<boolean>\` | \`true\` when resolved is \`'light'\`. \`false\` for custom.|
|
|
209
|
-
| \`isSystem()\` | \`Signal<boolean>\` | \`true\` when user selected \`'system'\`. |
|
|
210
|
-
| \`isHydrated()\` | \`Signal<boolean>\` | \`true\` after first browser render. Guard SSR content.|
|
|
129
|
+
Foundation service managing state (signals), persistence, system preference, and DOM manipulation (SSR safe).
|
|
211
130
|
|
|
212
|
-
### Methods
|
|
131
|
+
### Signals, Methods & Properties
|
|
213
132
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
| \`
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
|
221
|
-
|
|
|
222
|
-
| \`availableThemes\` | \`string[]\` | Resolved list including built-ins. |
|
|
133
|
+
| Name | Type | Description |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| \`selectedTheme()\` | \`Signal<string>\` | Chosen theme (can be \`'system'\`). |
|
|
136
|
+
| \`resolvedTheme()\` | \`Signal<string>\` | Active theme applied to DOM (never \`'system'\`). |
|
|
137
|
+
| \`isDark()\` / \`isLight()\` | \`Signal<boolean>\` | \`true\` for dark/light (returns \`false\` for custom themes). |
|
|
138
|
+
| \`isSystem()\` / \`isHydrated()\` | \`Signal<boolean>\` | System choice active / SSR hydration finished. |
|
|
139
|
+
| \`availableThemes\` | \`string[]\` | All configured themes including built-ins. |
|
|
140
|
+
| \`setTheme(theme)\` | \`(theme: string) => void\` | Validates, persists, and applies the theme to DOM. |
|
|
223
141
|
|
|
224
142
|
---
|
|
225
143
|
|
|
226
|
-
##
|
|
227
|
-
|
|
228
|
-
Binary switch between \`'dark'\` and \`'light'\`.
|
|
229
|
-
|
|
230
|
-
### Signals
|
|
231
|
-
Inherits: \`selectedTheme()\`, \`resolvedTheme()\`, \`isDark()\`, \`isLight()\`, \`isSystem()\`, \`isHydrated()\`.
|
|
232
|
-
|
|
233
|
-
### Methods
|
|
234
|
-
|
|
235
|
-
| Method | Description |
|
|
236
|
-
| ---------- | ---------------------------------------------- |
|
|
237
|
-
| \`toggle()\` | If resolved is dark → light. Otherwise → dark. |
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## ThemeCycleService
|
|
242
|
-
|
|
243
|
-
Rotates through all configured themes in order.
|
|
144
|
+
## Convenience Services
|
|
244
145
|
|
|
245
|
-
|
|
246
|
-
Inherits all from CoreThemeService, plus:
|
|
146
|
+
Specialized services implementing different theme selection behaviors.
|
|
247
147
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
| \`upcoming()\` | \`Signal<string>\` | Next theme in the cycle. |
|
|
252
|
-
| \`preceding()\` | \`Signal<string>\` | Previous theme in the cycle. |
|
|
253
|
-
|
|
254
|
-
### Methods
|
|
255
|
-
|
|
256
|
-
| Method | Description |
|
|
257
|
-
| --------- | ---------------------------- |
|
|
258
|
-
| \`cycle()\` | Advances to the next theme. |
|
|
259
|
-
|
|
260
|
-
### Properties
|
|
261
|
-
|
|
262
|
-
| Property | Type | Description |
|
|
263
|
-
| ----------------- | ----------- | ---------------------------------- |
|
|
264
|
-
| \`availableThemes\` | \`string[]\` | Full list of themes in cycle order.|
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## ThemeSelectService
|
|
269
|
-
|
|
270
|
-
Exposes the full theme list for dropdowns, radios, or tab selection.
|
|
271
|
-
|
|
272
|
-
### Signals
|
|
273
|
-
Inherits: \`selectedTheme()\`, \`resolvedTheme()\`, \`isDark()\`, \`isLight()\`, \`isSystem()\`, \`isHydrated()\`.
|
|
274
|
-
|
|
275
|
-
### Methods
|
|
276
|
-
|
|
277
|
-
| Method | Signature | Description |
|
|
278
|
-
| ----------------- | ---------------------- | --------------------------- |
|
|
279
|
-
| \`select()\` | \`(theme: string): void\`| Applies the given theme. |
|
|
148
|
+
### ThemeToggleService
|
|
149
|
+
Binary switch between \`'dark'\` and \`'light'\`.
|
|
150
|
+
- \`toggle()\`: Toggles the theme.
|
|
280
151
|
|
|
281
|
-
###
|
|
152
|
+
### ThemeCycleService
|
|
153
|
+
Rotates through all themes in configuration order.
|
|
154
|
+
- \`cycle()\`: Moves to the next theme.
|
|
155
|
+
- \`cycleIndex()\`: \`Signal<number>\` - Current theme index.
|
|
156
|
+
- \`upcoming()\` / \`preceding()\`: \`Signal<string>\` - Next / previous theme in cycle.
|
|
282
157
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
158
|
+
### ThemeSelectService
|
|
159
|
+
Full list control for select dropdowns, radio buttons, or lists.
|
|
160
|
+
- \`select(theme)\`: Sets the chosen theme.
|
|
286
161
|
|
|
287
162
|
---
|
|
288
163
|
|
|
289
|
-
## Types
|
|
290
|
-
|
|
291
|
-
| Type | Definition | Description |
|
|
292
|
-
| ------------- | ----------------------------------- | --------------------------------- |
|
|
293
|
-
| \`NgTheme<T>\` | \`'system' \\| 'light' \\| 'dark' \\| T\`| Theme identifier union |
|
|
294
|
-
| \`NgSystemTheme\`| \`'light' \\| 'dark'\` | Resolved system theme |
|
|
295
|
-
| \`NgMode\` | \`'class' \\| 'attribute' \\| 'both'\` | How theme is applied to DOM |
|
|
296
|
-
| \`NgStrategy\` | \`'critters' \\| 'blocking'\` | Anti-flash rendering strategy |
|
|
297
|
-
| \`NgConfig<T>\` | \`interface\` | Full library configuration |
|
|
298
|
-
|
|
299
|
-
## Errors
|
|
164
|
+
## Types & Errors
|
|
300
165
|
|
|
301
|
-
|
|
302
|
-
|
|
|
303
|
-
|
|
166
|
+
### Core Types
|
|
167
|
+
- \`NgTheme<T>\`: \`'system' | 'light' | 'dark' | T\`
|
|
168
|
+
- \`NgMode\`: \`'class' | 'attribute' | 'both'\`
|
|
169
|
+
- \`NgStrategy\`: \`'critters' | 'blocking'\`
|
|
304
170
|
|
|
171
|
+
### Errors
|
|
172
|
+
- \`NgxThemeStackError\`: Thrown for invalid configurations, storage keys, or theme names.
|
|
305
173
|
Catch with: \`if (e instanceof NgxThemeStackError) { ... }\`
|
|
306
174
|
`;
|
|
307
175
|
// ── assets/ component examples (Tier 3 — pure TypeScript, read on demand) ───
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/skill/index.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/skill/index.ts"],"names":[],"mappings":";;AAoQA,sCAUC;AAED,sBAMC;AAnRD,wDAAwD;AACxD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkGrB,CAAC;AAEF,gFAAgF;AAEhF,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoErB,CAAC;AAEF,+EAA+E;AAE/E,MAAM,eAAe,GACrB;;;;;;;;;;;;;;;;;;CAkBC,CAAC;AAEF,MAAM,cAAc,GACpB;;;;;;;;;;;;;;;;CAgBC,CAAC;AAEF,MAAM,eAAe,GACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,CAAC;AAEF,+EAA+E;AAE/E,MAAM,UAAU,GAAG,gCAAgC,CAAC;AAEpD,MAAM,KAAK,GAAwC;IACjD,EAAE,IAAI,EAAE,GAAG,UAAU,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE;IAC1D,EAAE,IAAI,EAAE,GAAG,UAAU,8BAA8B,EAAE,OAAO,EAAE,aAAa,EAAE;IAC7E,EAAE,IAAI,EAAE,GAAG,UAAU,yBAAyB,EAAE,OAAO,EAAE,eAAe,EAAE;IAC1E,EAAE,IAAI,EAAE,GAAG,UAAU,wBAAwB,EAAE,OAAO,EAAE,cAAc,EAAE;IACxE,EAAE,IAAI,EAAE,GAAG,UAAU,yBAAyB,EAAE,OAAO,EAAE,eAAe,EAAE;CAC3E,CAAC;AAEF,SAAgB,aAAa,CAAC,IAAU,EAAE,OAAyB;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACxC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAgB,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;QAC/C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACjF,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
|