@wangs-ui/skills 1.3.0-alpha.21 → 1.3.0-alpha.22
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/dist/bin.js +2 -2
- package/dist/index.js +1 -1
- package/dist/skills/wangs-ui/craft-theme/SKILL.md +1 -1
- package/dist/skills/wangs-ui/custom-color-family/SKILL.md +122 -0
- package/dist/{src-BFwMaU8I.js → src-CWGSuYNm.js} +9 -5
- package/package.json +1 -1
- package/skills/wangs-ui/craft-theme/SKILL.md +1 -1
- package/skills/wangs-ui/custom-color-family/SKILL.md +122 -0
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-
|
|
2
|
+
import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-CWGSuYNm.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
//#region bin.ts
|
|
6
|
-
var VERSION = "1.3.0-alpha.
|
|
6
|
+
var VERSION = "1.3.0-alpha.21";
|
|
7
7
|
var HELP_TEXT = `
|
|
8
8
|
\x1b[1m\x1b[36m🚀 Wangs UI Skills & Rules CLI\x1b[0m
|
|
9
9
|
Install, update, and manage modular AI agent skills and rules for Wangs UI React applications.
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as loadAllRules, a as getAgentRuleDirs, c as getInstalledSkills, d as isRuleInstalled, f as isSkillInstalled, g as getSkill, h as getRule, i as listSkills, l as installRule, m as removeSkill, n as updateSkills, o as getAgentSkillDirs, p as removeRule, r as addSkills, s as getInstalledRules, t as removeSkills, u as installSkill, v as loadAllSkills } from "./src-
|
|
1
|
+
import { _ as loadAllRules, a as getAgentRuleDirs, c as getInstalledSkills, d as isRuleInstalled, f as isSkillInstalled, g as getSkill, h as getRule, i as listSkills, l as installRule, m as removeSkill, n as updateSkills, o as getAgentSkillDirs, p as removeRule, r as addSkills, s as getInstalledRules, t as removeSkills, u as installSkill, v as loadAllSkills } from "./src-CWGSuYNm.js";
|
|
2
2
|
export { addSkills, getAgentRuleDirs, getAgentSkillDirs, getInstalledRules, getInstalledSkills, getRule, getSkill, installRule, installSkill, isRuleInstalled, isSkillInstalled, listSkills, loadAllRules, loadAllSkills, removeRule, removeSkill, removeSkills, updateSkills };
|
|
@@ -6,7 +6,7 @@ metadata:
|
|
|
6
6
|
---
|
|
7
7
|
# Skill: Craft Theme
|
|
8
8
|
|
|
9
|
-
Use this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — "make the app's theme orange", "use our brand color #ff6b35", "add a new palette called sunset", etc.
|
|
9
|
+
Use this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — "make the app's theme orange", "use our brand color #ff6b35", "add a new palette called sunset", etc. This only covers the 8 existing families (primary/secondary/tertiary/success/danger/warning/info/general) — for an entirely new semantic color group (e.g. a "premium" tier badge), use the `custom-color-family` skill instead.
|
|
10
10
|
|
|
11
11
|
## The rule this skill exists to enforce
|
|
12
12
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: custom-color-family
|
|
3
|
+
description: Generate a new accessible custom color family (fill/on, container/on-container, emphasized/hover/pressed, fixed) for a Wangs UI app via the `wangs-ui-generate-color-family` CLI — for a semantic group beyond the 8 built-in families (e.g. "premium", "verified", "beta"). Never hand-write or hand-pick these hexes.
|
|
4
|
+
metadata:
|
|
5
|
+
owner: wangs-ui
|
|
6
|
+
---
|
|
7
|
+
# Skill: Custom Color Family
|
|
8
|
+
|
|
9
|
+
Use this skill whenever the app needs a new semantic color group that isn't one of the 8 built-in families (`primary`/`secondary`/`tertiary`/`danger`/`success`/`warning`/`info`/`general`) — a product tier badge ("premium"), a verification status ("verified"), a draft/beta tag, a data-visualization category color, etc. For rebranding the existing 8 families instead, use the `craft-theme` skill.
|
|
10
|
+
|
|
11
|
+
## The rule this skill exists to enforce
|
|
12
|
+
|
|
13
|
+
**Never hand-pick which color goes on top of your fill, how light the container tint should be, or what percentage a hover/pressed state should mix at — and never recompute any of it at render time.** Wangs UI's own built-in families hit the first mistake before: a fixed luminance threshold put white text on a brand color at 2.54:1 contrast (fails AA, looked fine "by eye"). The CLI below runs the same search-based derivation the built-in families use, **once**, and writes the result to a static file — exactly like `successContainer`/`onSuccessContainer` are plain static fields you reuse everywhere, not something recomputed per component.
|
|
14
|
+
|
|
15
|
+
## 1. Generate the family
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm exec wangs-ui-generate-color-family --name=<id> --color=<#hex> [--general=#hex] --out=<path>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- `--name` (required): identifier for the family, e.g. `premium`. Used for the exported const name (`premiumTokens`) and, unless `--out` is given, the output filename (`premium.ts`).
|
|
22
|
+
- `--color` (required): the family's reference hex, e.g. `#7c5cff`.
|
|
23
|
+
- `--general` (optional): your theme's neutral/general base (`Palette.general['500']` — the same hex `craft-theme`'s generator used for your app's `general` family, or one of the built-in palettes' `general['500']`). Improves how the Fill/On pairing reads against your actual neutral surfaces. Defaults to `#808080` if omitted.
|
|
24
|
+
- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the app's own theme directory (e.g. `src/theme/premium.ts`), don't rely on the default.
|
|
25
|
+
|
|
26
|
+
If `@wangs-ui/foundation` isn't already a dependency of the project, install it first — the CLI ships as its `bin`.
|
|
27
|
+
|
|
28
|
+
**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If the color needs to change, re-run the command — never hand-edit a role in the output.
|
|
29
|
+
|
|
30
|
+
This writes **two** files — `<out>.ts` (the computed hex values) and `<out>.css` (a static Tailwind `@theme` registration, same mechanism `theme.css` itself uses for `success`/`danger`/etc.):
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// premium.ts
|
|
34
|
+
export const premiumTokens = {
|
|
35
|
+
light: {
|
|
36
|
+
fill: '#7c5cff',
|
|
37
|
+
onFill: '#000000',
|
|
38
|
+
container: '#dfdfff',
|
|
39
|
+
onContainer: '#471fa9',
|
|
40
|
+
emphasized: '#7959f8',
|
|
41
|
+
hover: '#7959f8',
|
|
42
|
+
pressed: '#7959f8',
|
|
43
|
+
fixed: '#dfdfff',
|
|
44
|
+
fixedDim: '#c2bfff',
|
|
45
|
+
onFixed: '#1b0d48',
|
|
46
|
+
onFixedVariant: '#471fa9',
|
|
47
|
+
},
|
|
48
|
+
dark: {
|
|
49
|
+
/* … same 11 roles, derived for dark mode … */
|
|
50
|
+
},
|
|
51
|
+
} as const;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
/* premium.css */
|
|
56
|
+
@theme {
|
|
57
|
+
--color-premium: var(--color-premium);
|
|
58
|
+
--color-on-premium: var(--color-on-premium);
|
|
59
|
+
--color-premium-container: var(--color-premium-container);
|
|
60
|
+
--color-on-premium-container: var(--color-on-premium-container);
|
|
61
|
+
/* … the rest of the 11 roles … */
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `.css` file only declares **names** (self-referential, no hardcoded value) so Tailwind can generate the matching utilities — it never needs regenerating even when you change the color with a new CLI run; only the `.ts` file does.
|
|
66
|
+
|
|
67
|
+
## 2. Wire it up once (web)
|
|
68
|
+
|
|
69
|
+
Two one-time steps, done once for the whole app — not per component, and no wrapper component to write:
|
|
70
|
+
|
|
71
|
+
**a. Import the generated CSS** anywhere in your app's normal global stylesheet import chain (wherever your `@import "tailwindcss"` already lives):
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
@import './theme/premium.css';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**b. Pass the generated tokens straight to `WangsUiProvider`'s `theme.customColorFamilies`** — this is a real `ThemeProviderProps` field, not a convention you assemble yourself. The provider resolves it to the current mode and injects the CSS variables internally, the exact same pass the built-in families already get (`CssVariablesInjector`) — adding a family is purely additive data, no new component in your tree:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { WangsUiProvider } from '@wangs-ui/react-core/api';
|
|
81
|
+
import { premiumTokens } from './theme/premium';
|
|
82
|
+
|
|
83
|
+
<WangsUiProvider
|
|
84
|
+
configOptions={{ preset }}
|
|
85
|
+
theme={{ palette, mode, customColorFamilies: { premium: premiumTokens } }}
|
|
86
|
+
>
|
|
87
|
+
<App />
|
|
88
|
+
</WangsUiProvider>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A second custom family is one more entry in the same object (`{ premium: premiumTokens, verified: verifiedTokens }`) — never a second wrapper.
|
|
92
|
+
|
|
93
|
+
## 3. Use it — same as any built-in family
|
|
94
|
+
|
|
95
|
+
From here on, every component just uses ordinary Tailwind utility classes, exactly like `bg-success-container`/`text-on-success-container`:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
function PremiumBadge() {
|
|
99
|
+
return <span className="bg-premium-container text-on-premium-container">Premium</span>;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
No import, no hook, no per-component wiring — any component anywhere in the tree can reach for `bg-premium` / `text-on-premium` / `bg-premium-emphasized` / `hover:bg-premium-hover` / etc. the same way it already reaches for the built-in families.
|
|
104
|
+
|
|
105
|
+
If a component needs the plain hex value instead of a class (a canvas draw call, an SVG fill, a chart library prop), `useTheme().customColors.premium` is already resolved to the current mode — no manual `premiumTokens[mode]` indexing:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
const { customColors } = useTheme();
|
|
109
|
+
customColors.premium.fill; // already the right mode's hex
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**React Native** has no `.css`/Tailwind step: skip step **a** in section 2, but still pass `customColorFamilies` to `theme` in step **b** — then read `useTheme().customColors.premium` (etc.) the same way, directly in `style`.
|
|
113
|
+
|
|
114
|
+
## 4. Regenerating / evolving a custom family
|
|
115
|
+
|
|
116
|
+
To change the color, re-run the same command with the new hex:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pnpm exec wangs-ui-generate-color-family --name=premium --color=#9333ea --out=src/theme/premium.ts
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This rewrites `premium.ts` with the new values and rewrites `premium.css` too (harmless — it's the same static names every time, nothing in your app needs to change because of it).
|
|
@@ -4,10 +4,13 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { cancel, intro, isCancel, multiselect, outro } from "@clack/prompts";
|
|
6
6
|
//#region skills/wangs-ui/craft-theme/SKILL.md?raw
|
|
7
|
-
var SKILL_default$
|
|
7
|
+
var SKILL_default$9 = "---\nname: craft-theme\ndescription: Generate a brand-color theme (13-shade tonal palette + semantic tokens) for a Wangs UI app via the `wangs-ui-generate-palette` CLI — never hand-write hex shade ramps.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Craft Theme\n\nUse this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — \"make the app's theme orange\", \"use our brand color #ff6b35\", \"add a new palette called sunset\", etc. This only covers the 8 existing families (primary/secondary/tertiary/success/danger/warning/info/general) — for an entirely new semantic color group (e.g. a \"premium\" tier badge), use the `custom-color-family` skill instead.\n\n## The rule this skill exists to enforce\n\n**Never hand-write a 13-shade tonal ramp, and never hand-pick which shade pairs with which as a text/foreground color.** Wangs UI's own design system shipped with exactly that mistake for a while: hand-tuned palette files drifted from what the perceptual (OKLCH) generator would produce, which caused white text to render at 2.54:1 contrast on one brand's primary color — invisible-adjacent, and only caught by a dedicated audit. The generator and the contrast math it's paired with exist specifically so this class of bug can't happen again. Always reach for the CLI below instead of writing hex values yourself.\n\n## 1. Generate the palette\n\n```bash\npnpm exec wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>\n```\n\n- `--name` (required): identifier for the palette, e.g. `sunset`. Used for the exported const name (`sunsetPalette`) and, unless `--out` is given, the output filename (`sunset.ts`).\n- `--primary` (required): the brand/key hex color, e.g. `#ff6b35`.\n- Optional per-family overrides — omit any of these to let the generator harmoniously auto-derive it from `--primary` (secondary: boosted-lightness variant of primary's hue; tertiary: +60° hue rotation; general: near-neutral variant of primary's hue) or fall back to the generator's calibrated defaults (success/danger/warning/info):\n `--secondary=#hex --tertiary=#hex --general=#hex --success=#hex --danger=#hex --warning=#hex --info=#hex`\n- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the consumer app's own theme directory (e.g. `src/theme/sunset.ts`), don't rely on the default.\n\nIf `@wangs-ui/foundation` isn't already a dependency of the project you're working in, install it first (`pnpm add @wangs-ui/foundation` or the project's equivalent) — the CLI ships as its `bin`.\n\n**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If a color needs to change, re-run the command (it clears the read-only bit, rewrites, and re-locks it) — never hand-edit a shade in the output, and never `chmod` it writable to bypass this. Note the read-only bit is a local filesystem attribute only; it is not preserved by git across clones, so it is a deterrent for the person/agent working in this checkout right now, not a hard guarantee for every future contributor.\n\n## 2. Wire it into the app\n\nThe generated file exports a plain `Palette` object — pass it directly to `WangsUiProvider`'s `theme.palette` (or `theme.defaultPalette` for uncontrolled mode):\n\n```tsx\nimport { WangsUiProvider } from '@wangs-ui/react-core/api';\nimport preset from '@wangs-ui/react-presets/fixedasset'; // or whichever preset the app uses\nimport { sunsetPalette } from './theme/sunset';\n\nconst App = () => (\n <WangsUiProvider configOptions={{ preset }} theme={{ palette: sunsetPalette, mode: 'light' }}>\n <YourApp />\n </WangsUiProvider>\n);\n```\n\n`theme.palette` also accepts a built-in palette name (`'blue' | 'emerald' | 'crimson' | 'carbon' | 'gold'`) or a raw hex string — a generated `Palette` object is the right choice once you have brand-specific secondary/tertiary/status colors to preserve, not just a single key color.\n\n## 3. Regenerating / evolving an existing custom palette\n\nTo change a color, re-run the same command with a new hex for the family that changed — **always pass every family you want to keep**, not just the one changing, since each run is a full regeneration from the anchors you give it:\n\n```bash\npnpm exec wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts\n```\n\n## 4. If you're extending Wangs UI itself (contributing a new official palette)\n\nThis is a different, rarer case than theming a consumer app — only relevant if you're working inside the `wangs-ui-react` monorepo itself and adding a 6th built-in palette alongside blue/emerald/crimson/carbon/gold:\n\n1. Run the generator with `--out` pointing at `packages/foundation/theme/tokens/palettes/<name>.ts`.\n2. Register it: add `'<name>'` to the `PaletteName` union and `paletteLoaders` map in `packages/foundation/theme/context/ThemeContext.tsx`, and re-export it from `packages/foundation/theme/tokens/palettes/index.ts`.\n3. Run the contrast regression suite before considering it done: `pnpm exec vitest run --config packages/foundation/vitest.config.ts` — it checks every semantic token pairing (including the new palette) against WCAG AA across both light and dark mode. See `packages/foundation/theme/tokens/COLOR_TOKEN_CONTRACT.md` for the full rule set this is checked against.\n";
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region skills/wangs-ui/create-form/SKILL.md?raw
|
|
10
|
-
var SKILL_default$
|
|
10
|
+
var SKILL_default$8 = "---\nname: create-form\ndescription: Form architecture, validation workflows, strongly-typed forms (useForm, useDialogForm, useWatchField), initialValues/reset lifecycle, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Form Architecture & Validation Workflows\n\nUse this skill when building forms, data entry panels, modal forms, settings pages, or multipart forms in Wangs UI applications.\n\n---\n\n## 1. MCP Protocol & Component Rules (Mandatory Single Source of Truth)\n\nDo **NOT** hardcode or guess prop names, component options, preset variations, or Storybook patterns in this document. Always retrieve component definitions, active props, and live Storybook implementations directly via MCP:\n\n### Component & Form API Protocol:\n\n```json\nget_component_api({ \"component\": \"form\" })\nget_component_api({ \"component\": \"field\" })\nget_component_api({ \"component\": \"dialogform\" })\nget_component_api({ \"component\": \"input\" })\nget_component_api({ \"component\": \"numberinput\" })\nget_component_api({ \"component\": \"select\" })\nget_component_api({ \"component\": \"multiselect\" })\nget_component_api({ \"component\": \"datepicker\" })\nget_component_api({ \"component\": \"fileupload\" })\n```\n\n### Curated Form Documentation:\n\n```json\nget_documentation({ \"id\": \"form\" })\nget_documentation({ \"id\": \"dialogform\" })\nget_documentation({ \"id\": \"field\" })\n```\n\n### Live Storybook & Interactive Behavior Protocol:\n\n```json\nget_component_examples({ \"component\": \"form\", \"variant\": \"Default\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"AsyncInitialValues\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"ConditionalFields\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"CascadingOptions\" })\nget_component_examples({ \"component\": \"dialogform\", \"variant\": \"Default\" })\n```\n\n### Knowledge Graph & Symbol Usages:\n\n```json\nquery_graph({ \"query\": \"useForm\" })\nquery_graph({ \"query\": \"useDialogForm\" })\nquery_graph({ \"query\": \"useWatchField\" })\n```\n\n---\n\n## 2. Core Form Concepts & Lifecycle Mechanics\n\n### A. Strongly Typed Form Instance (`useForm<TForm>()`)\n\n`useForm<TForm>()` instantiates a `FormControl` natively bound to model type `TForm`.\n\n- `Field`: `name` is strictly typed to `Path<TForm>` dot-paths.\n- `useWatchField`: `name` is strictly typed to `Path<TForm>`.\n- `control`: Provides `setInitialValues`, `setValues`, `setFieldError`, `setErrors`, and `reset`.\n\n### B. Dynamic Initial Values & Baseline Reset (`setInitialValues` vs `setValues`)\n\n1. **Async Initial Values (`control.setInitialValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object (e.g. fetched from an API).\n - Establishes an **immutable baseline** for registered fields. Once set for a field path, subsequent calls to `setInitialValues` for that path are ignored.\n2. **Batch Value Updates (`control.setValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object to update current input values without altering the initial baseline.\n3. **Reset Behavior (`control.reset()`)**:\n - Restores all fields back to their registered initial baseline values (set via `setInitialValues` or field `initialValue`) and clears all field-level validation errors.\n\n### C. Primitive Component Integration Architecture\n\n`Field` serves as the form integration wrapper for primitive UI input components (`Input`, `Select`, `MultiSelect`, `DatePicker`, `NumberInput`, `FileUpload`, `Calendar`, etc.):\n\n- **Children Render Callback**: `Field` yields `{ fieldProps, fieldState }`.\n- **`fieldProps`**: Pass directly to primitive inputs (`<Input {...fieldProps} />`). Contains `name`, `value`, `ref`, `onChange`.\n- **`fieldState`**: Provides `invalid`, `error`, `isDirty`, `isPending`. Pass `invalid={fieldState.invalid}` to primitive components for accessibility and validation styling.\n- **⚠️ Mandatory Destructuring (Never `(field) => ...`)**: `<Field>`'s callback render prop must destructure as `{({ fieldProps, fieldState })}`. Passing a single parameter instead (`{(field) => ...}`) produces `undefined` `value`/`onChange` and permanently locks the input.\n- **Pass `fieldProps.onChange` directly**: `<Field>` is generic and `fieldProps.onChange` is already typed to the field's value type. Do not wrap it in unnecessary `e?.target?.value` extractions.\n\n---\n\n## 3. High-Level Form Architecture & Usage Patterns\n\n### Pattern 1: Page Forms (`useForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useForm } from '@wangs-ui/react-core/primitive/form';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useEffect } from 'react';\n\ninterface UserProfile {\n name: string;\n email: string;\n}\n\nexport function UserProfilePage({ userId }: { userId: string }) {\n const { t } = useI18n();\n const { Form, Field, control } = useForm<UserProfile>();\n\n useEffect(() => {\n async function loadData() {\n const data = await fetchUserData(userId);\n // Establish immutable initial baseline from async response\n control.setInitialValues(data);\n }\n loadData();\n }, [userId, control]);\n\n return (\n <Form control={control} onSubmit={(values) => saveUserData(values)}>\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => (\n <Input {...fieldProps} invalid={fieldState.invalid} placeholder={t('Enter full name')} />\n )}\n </Field>\n\n <div className=\"flex gap-2\">\n <Button\n label={t('Reset')}\n type=\"button\"\n variant=\"outlined\"\n onClick={() => control.reset()}\n />\n <Button label={t('Save')} type=\"submit\" />\n </div>\n </Form>\n );\n}\n```\n\n### Pattern 2: Modal Forms (`useDialogForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useDialogForm } from '@wangs-ui/react-core/primitive/dialogform';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useState } from 'react';\n\ninterface EditUserForm {\n name: string;\n}\n\nexport function EditUserModal() {\n const { t } = useI18n();\n const [open, setOpen] = useState(false);\n const { DialogForm, Field, control } = useDialogForm<EditUserForm>();\n\n return (\n <>\n <Button label={t('Edit')} onClick={() => setOpen(true)} />\n <DialogForm\n closeOnSubmit\n control={control}\n header={t('Edit User')}\n open={open}\n onOpenChange={setOpen}\n onSubmit={(values) => handleSave(values)}\n >\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => <Input {...fieldProps} invalid={fieldState.invalid} />}\n </Field>\n </DialogForm>\n </>\n );\n}\n```\n\n---\n\n## 4. Mandatory Implementation Guidelines\n\n1. **Query MCP First**: Never guess component props or story examples — inspect via MCP tools.\n2. **Granular Primitive Subpaths**: Import primitives via exact subpath modules (`@wangs-ui/react-core/primitive/form`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/input`).\n3. **i18n Localization**: Wrap all user-visible labels, placeholders, and error strings in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Server Error Mapping**: Map HTTP validation errors (e.g. 422 response) into the form using `control.setErrors(apiErrors)`.\n";
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region skills/wangs-ui/custom-color-family/SKILL.md?raw
|
|
13
|
+
var SKILL_default$7 = "---\nname: custom-color-family\ndescription: Generate a new accessible custom color family (fill/on, container/on-container, emphasized/hover/pressed, fixed) for a Wangs UI app via the `wangs-ui-generate-color-family` CLI — for a semantic group beyond the 8 built-in families (e.g. \"premium\", \"verified\", \"beta\"). Never hand-write or hand-pick these hexes.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Custom Color Family\n\nUse this skill whenever the app needs a new semantic color group that isn't one of the 8 built-in families (`primary`/`secondary`/`tertiary`/`danger`/`success`/`warning`/`info`/`general`) — a product tier badge (\"premium\"), a verification status (\"verified\"), a draft/beta tag, a data-visualization category color, etc. For rebranding the existing 8 families instead, use the `craft-theme` skill.\n\n## The rule this skill exists to enforce\n\n**Never hand-pick which color goes on top of your fill, how light the container tint should be, or what percentage a hover/pressed state should mix at — and never recompute any of it at render time.** Wangs UI's own built-in families hit the first mistake before: a fixed luminance threshold put white text on a brand color at 2.54:1 contrast (fails AA, looked fine \"by eye\"). The CLI below runs the same search-based derivation the built-in families use, **once**, and writes the result to a static file — exactly like `successContainer`/`onSuccessContainer` are plain static fields you reuse everywhere, not something recomputed per component.\n\n## 1. Generate the family\n\n```bash\npnpm exec wangs-ui-generate-color-family --name=<id> --color=<#hex> [--general=#hex] --out=<path>\n```\n\n- `--name` (required): identifier for the family, e.g. `premium`. Used for the exported const name (`premiumTokens`) and, unless `--out` is given, the output filename (`premium.ts`).\n- `--color` (required): the family's reference hex, e.g. `#7c5cff`.\n- `--general` (optional): your theme's neutral/general base (`Palette.general['500']` — the same hex `craft-theme`'s generator used for your app's `general` family, or one of the built-in palettes' `general['500']`). Improves how the Fill/On pairing reads against your actual neutral surfaces. Defaults to `#808080` if omitted.\n- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the app's own theme directory (e.g. `src/theme/premium.ts`), don't rely on the default.\n\nIf `@wangs-ui/foundation` isn't already a dependency of the project, install it first — the CLI ships as its `bin`.\n\n**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If the color needs to change, re-run the command — never hand-edit a role in the output.\n\nThis writes **two** files — `<out>.ts` (the computed hex values) and `<out>.css` (a static Tailwind `@theme` registration, same mechanism `theme.css` itself uses for `success`/`danger`/etc.):\n\n```ts\n// premium.ts\nexport const premiumTokens = {\n light: {\n fill: '#7c5cff',\n onFill: '#000000',\n container: '#dfdfff',\n onContainer: '#471fa9',\n emphasized: '#7959f8',\n hover: '#7959f8',\n pressed: '#7959f8',\n fixed: '#dfdfff',\n fixedDim: '#c2bfff',\n onFixed: '#1b0d48',\n onFixedVariant: '#471fa9',\n },\n dark: {\n /* … same 11 roles, derived for dark mode … */\n },\n} as const;\n```\n\n```css\n/* premium.css */\n@theme {\n --color-premium: var(--color-premium);\n --color-on-premium: var(--color-on-premium);\n --color-premium-container: var(--color-premium-container);\n --color-on-premium-container: var(--color-on-premium-container);\n /* … the rest of the 11 roles … */\n}\n```\n\nThe `.css` file only declares **names** (self-referential, no hardcoded value) so Tailwind can generate the matching utilities — it never needs regenerating even when you change the color with a new CLI run; only the `.ts` file does.\n\n## 2. Wire it up once (web)\n\nTwo one-time steps, done once for the whole app — not per component, and no wrapper component to write:\n\n**a. Import the generated CSS** anywhere in your app's normal global stylesheet import chain (wherever your `@import \"tailwindcss\"` already lives):\n\n```css\n@import './theme/premium.css';\n```\n\n**b. Pass the generated tokens straight to `WangsUiProvider`'s `theme.customColorFamilies`** — this is a real `ThemeProviderProps` field, not a convention you assemble yourself. The provider resolves it to the current mode and injects the CSS variables internally, the exact same pass the built-in families already get (`CssVariablesInjector`) — adding a family is purely additive data, no new component in your tree:\n\n```tsx\nimport { WangsUiProvider } from '@wangs-ui/react-core/api';\nimport { premiumTokens } from './theme/premium';\n\n<WangsUiProvider\n configOptions={{ preset }}\n theme={{ palette, mode, customColorFamilies: { premium: premiumTokens } }}\n>\n <App />\n</WangsUiProvider>;\n```\n\nA second custom family is one more entry in the same object (`{ premium: premiumTokens, verified: verifiedTokens }`) — never a second wrapper.\n\n## 3. Use it — same as any built-in family\n\nFrom here on, every component just uses ordinary Tailwind utility classes, exactly like `bg-success-container`/`text-on-success-container`:\n\n```tsx\nfunction PremiumBadge() {\n return <span className=\"bg-premium-container text-on-premium-container\">Premium</span>;\n}\n```\n\nNo import, no hook, no per-component wiring — any component anywhere in the tree can reach for `bg-premium` / `text-on-premium` / `bg-premium-emphasized` / `hover:bg-premium-hover` / etc. the same way it already reaches for the built-in families.\n\nIf a component needs the plain hex value instead of a class (a canvas draw call, an SVG fill, a chart library prop), `useTheme().customColors.premium` is already resolved to the current mode — no manual `premiumTokens[mode]` indexing:\n\n```tsx\nconst { customColors } = useTheme();\ncustomColors.premium.fill; // already the right mode's hex\n```\n\n**React Native** has no `.css`/Tailwind step: skip step **a** in section 2, but still pass `customColorFamilies` to `theme` in step **b** — then read `useTheme().customColors.premium` (etc.) the same way, directly in `style`.\n\n## 4. Regenerating / evolving a custom family\n\nTo change the color, re-run the same command with the new hex:\n\n```bash\npnpm exec wangs-ui-generate-color-family --name=premium --color=#9333ea --out=src/theme/premium.ts\n```\n\nThis rewrites `premium.ts` with the new values and rewrites `premium.css` too (harmless — it's the same static names every time, nothing in your app needs to change because of it).\n";
|
|
11
14
|
//#endregion
|
|
12
15
|
//#region skills/wangs-ui/data-table/SKILL.md?raw
|
|
13
16
|
var SKILL_default$6 = "---\nname: data-table\ndescription: Architecture, workflows, and MCP discovery protocol for building DataTables with sorting, pagination, filtering, selection, and export.\nmetadata:\n owner: wangs-ui\n---\n# Skill: DataTable Architecture & Integration Workflows\n\nUse this skill when implementing data grids, server-paginated tables, filterable listing views, or batch management interfaces with `@wangs-ui/react-core`.\n\n---\n\n## 1. MCP Inspection Protocol (Mandatory Single Source of Truth)\n\nDo **NOT** guess table prop names or hardcode table structures. Query the MCP server dynamically to inspect exact TypeScript signatures, live story implementations, and companion controls:\n\n### Inspect Component Contracts (`component` parameter):\n\n```json\nget_component_api({ \"component\": \"datatable\" })\nget_component_api({ \"component\": \"exportbutton\" })\nget_component_api({ \"component\": \"filtercontainer\" })\nget_component_api({ \"component\": \"bulkactionbutton\" })\n```\n\n### Read Curated Documentation:\n\n```json\nget_documentation({ \"id\": \"datatable\" })\nget_documentation({ \"id\": \"exportbutton\" })\n```\n\n### Inspect Live Story Implementations:\n\n```json\nget_component_examples({ \"component\": \"datatable\", \"variant\": \"Basic\" })\nget_component_examples({ \"component\": \"datatable\", \"variant\": \"CursorPagination\" })\nget_component_examples({ \"component\": \"datatable\", \"variant\": \"Sortable\" })\nget_component_examples({ \"component\": \"datatable\", \"variant\": \"MultipleSelection\" })\nget_component_examples({ \"component\": \"datatable\", \"variant\": \"CustomColumn\" })\nget_component_examples({ \"component\": \"exportbutton\", \"variant\": \"WithTable\" })\n```\n\n### Inspect Knowledge Graph & Usages:\n\n```json\nquery_graph({ \"query\": \"DataTable\" })\nquery_graph({ \"query\": \"useDataTableFetch\" })\n```\n\n---\n\n## 2. Core Architecture & Mental Model\n\nThe Wangs UI `DataTable` is built on a modular, headless-first architecture:\n\n1. **Declarative Column Definitions (`TableColumn<T>[]`)**:\n Columns are configured as typed array objects, not as JSX children. Check `get_component_api({ \"component\": \"datatable\" })` for column field types.\n2. **Table Instance Hook (`useDataTable`)**:\n Coordinates table state (sorting, pagination, selection, column ordering, pinning, visibility).\n3. **Data Fetching Hook (`useDataTableFetch`)**:\n Feeds server-side data, handles loading indicators, manages query parameters (`search`, `filter`, `sort`, `page`, `limit`), and debounces requests automatically.\n4. **Ecosystem Companions**:\n - `FilterContainer` & `FilterToggleButton`: Filter popovers and faceted search.\n - `ExportButton`: Client/server export to Excel, CSV, PDF, or Print.\n - `BulkActionButton`: Contextual batch actions triggered when rows are selected.\n - `CustomColumn`: User-controlled column ordering, visibility toggling, and pinning.\n\n---\n\n## 3. Mandatory Implementation Rules\n\n1. **Query MCP for Current Code Patterns**: Always run `get_component_examples` for `datatable` before drafting code.\n2. **Strict Subpath Imports**: Import via `@wangs-ui/react-core/primitive/datatable` and companion primitive paths.\n3. **Always Translate Visible Copy**: All column header labels, empty state messages, and action button labels must be wrapped in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Stable Row Identity**: Always configure a unique key identifier (`dataKey` / `rowId`) for stable selection and row identity.\n5. **Zero Redundant Table Wrappers**: Never wrap `<DataTable />` in an isolated `<div>` or `<Box>` solely to set width or margin. Wangs UI DataTable manages its own container scroll and layout dimensions out of the box.\n";
|
|
@@ -105,8 +108,9 @@ var typescript_zero_any_default = "---\ntrigger: model_decision\ndescription: \"
|
|
|
105
108
|
//#region src/registry.ts
|
|
106
109
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
107
110
|
var EMBEDDED_SKILLS_RAW = /* #__PURE__ */ Object.assign({
|
|
108
|
-
"../skills/wangs-ui/craft-theme/SKILL.md": SKILL_default$
|
|
109
|
-
"../skills/wangs-ui/create-form/SKILL.md": SKILL_default$
|
|
111
|
+
"../skills/wangs-ui/craft-theme/SKILL.md": SKILL_default$9,
|
|
112
|
+
"../skills/wangs-ui/create-form/SKILL.md": SKILL_default$8,
|
|
113
|
+
"../skills/wangs-ui/custom-color-family/SKILL.md": SKILL_default$7,
|
|
110
114
|
"../skills/wangs-ui/data-table/SKILL.md": SKILL_default$6,
|
|
111
115
|
"../skills/wangs-ui/dialog-modal/SKILL.md": SKILL_default$5,
|
|
112
116
|
"../skills/wangs-ui/i18n-usage/SKILL.md": SKILL_default$4,
|
|
@@ -415,7 +419,7 @@ function removeRule(ruleRelativePath, baseDir = process.cwd()) {
|
|
|
415
419
|
//#endregion
|
|
416
420
|
//#region src/commands/list.ts
|
|
417
421
|
function listSkills(baseDir = process.cwd()) {
|
|
418
|
-
intro(`\x1b[1m\x1b[36m📦 Wangs UI Skills & Rules Registry\x1b[0m [2m(v1.3.0-alpha.
|
|
422
|
+
intro(`\x1b[1m\x1b[36m📦 Wangs UI Skills & Rules Registry\x1b[0m [2m(v1.3.0-alpha.21)[0m`);
|
|
419
423
|
const allSkills = loadAllSkills();
|
|
420
424
|
const allRules = loadAllRules();
|
|
421
425
|
const skillDirs = getAgentSkillDirs(baseDir);
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ metadata:
|
|
|
6
6
|
---
|
|
7
7
|
# Skill: Craft Theme
|
|
8
8
|
|
|
9
|
-
Use this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — "make the app's theme orange", "use our brand color #ff6b35", "add a new palette called sunset", etc.
|
|
9
|
+
Use this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — "make the app's theme orange", "use our brand color #ff6b35", "add a new palette called sunset", etc. This only covers the 8 existing families (primary/secondary/tertiary/success/danger/warning/info/general) — for an entirely new semantic color group (e.g. a "premium" tier badge), use the `custom-color-family` skill instead.
|
|
10
10
|
|
|
11
11
|
## The rule this skill exists to enforce
|
|
12
12
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: custom-color-family
|
|
3
|
+
description: Generate a new accessible custom color family (fill/on, container/on-container, emphasized/hover/pressed, fixed) for a Wangs UI app via the `wangs-ui-generate-color-family` CLI — for a semantic group beyond the 8 built-in families (e.g. "premium", "verified", "beta"). Never hand-write or hand-pick these hexes.
|
|
4
|
+
metadata:
|
|
5
|
+
owner: wangs-ui
|
|
6
|
+
---
|
|
7
|
+
# Skill: Custom Color Family
|
|
8
|
+
|
|
9
|
+
Use this skill whenever the app needs a new semantic color group that isn't one of the 8 built-in families (`primary`/`secondary`/`tertiary`/`danger`/`success`/`warning`/`info`/`general`) — a product tier badge ("premium"), a verification status ("verified"), a draft/beta tag, a data-visualization category color, etc. For rebranding the existing 8 families instead, use the `craft-theme` skill.
|
|
10
|
+
|
|
11
|
+
## The rule this skill exists to enforce
|
|
12
|
+
|
|
13
|
+
**Never hand-pick which color goes on top of your fill, how light the container tint should be, or what percentage a hover/pressed state should mix at — and never recompute any of it at render time.** Wangs UI's own built-in families hit the first mistake before: a fixed luminance threshold put white text on a brand color at 2.54:1 contrast (fails AA, looked fine "by eye"). The CLI below runs the same search-based derivation the built-in families use, **once**, and writes the result to a static file — exactly like `successContainer`/`onSuccessContainer` are plain static fields you reuse everywhere, not something recomputed per component.
|
|
14
|
+
|
|
15
|
+
## 1. Generate the family
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm exec wangs-ui-generate-color-family --name=<id> --color=<#hex> [--general=#hex] --out=<path>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- `--name` (required): identifier for the family, e.g. `premium`. Used for the exported const name (`premiumTokens`) and, unless `--out` is given, the output filename (`premium.ts`).
|
|
22
|
+
- `--color` (required): the family's reference hex, e.g. `#7c5cff`.
|
|
23
|
+
- `--general` (optional): your theme's neutral/general base (`Palette.general['500']` — the same hex `craft-theme`'s generator used for your app's `general` family, or one of the built-in palettes' `general['500']`). Improves how the Fill/On pairing reads against your actual neutral surfaces. Defaults to `#808080` if omitted.
|
|
24
|
+
- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the app's own theme directory (e.g. `src/theme/premium.ts`), don't rely on the default.
|
|
25
|
+
|
|
26
|
+
If `@wangs-ui/foundation` isn't already a dependency of the project, install it first — the CLI ships as its `bin`.
|
|
27
|
+
|
|
28
|
+
**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If the color needs to change, re-run the command — never hand-edit a role in the output.
|
|
29
|
+
|
|
30
|
+
This writes **two** files — `<out>.ts` (the computed hex values) and `<out>.css` (a static Tailwind `@theme` registration, same mechanism `theme.css` itself uses for `success`/`danger`/etc.):
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// premium.ts
|
|
34
|
+
export const premiumTokens = {
|
|
35
|
+
light: {
|
|
36
|
+
fill: '#7c5cff',
|
|
37
|
+
onFill: '#000000',
|
|
38
|
+
container: '#dfdfff',
|
|
39
|
+
onContainer: '#471fa9',
|
|
40
|
+
emphasized: '#7959f8',
|
|
41
|
+
hover: '#7959f8',
|
|
42
|
+
pressed: '#7959f8',
|
|
43
|
+
fixed: '#dfdfff',
|
|
44
|
+
fixedDim: '#c2bfff',
|
|
45
|
+
onFixed: '#1b0d48',
|
|
46
|
+
onFixedVariant: '#471fa9',
|
|
47
|
+
},
|
|
48
|
+
dark: {
|
|
49
|
+
/* … same 11 roles, derived for dark mode … */
|
|
50
|
+
},
|
|
51
|
+
} as const;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
/* premium.css */
|
|
56
|
+
@theme {
|
|
57
|
+
--color-premium: var(--color-premium);
|
|
58
|
+
--color-on-premium: var(--color-on-premium);
|
|
59
|
+
--color-premium-container: var(--color-premium-container);
|
|
60
|
+
--color-on-premium-container: var(--color-on-premium-container);
|
|
61
|
+
/* … the rest of the 11 roles … */
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `.css` file only declares **names** (self-referential, no hardcoded value) so Tailwind can generate the matching utilities — it never needs regenerating even when you change the color with a new CLI run; only the `.ts` file does.
|
|
66
|
+
|
|
67
|
+
## 2. Wire it up once (web)
|
|
68
|
+
|
|
69
|
+
Two one-time steps, done once for the whole app — not per component, and no wrapper component to write:
|
|
70
|
+
|
|
71
|
+
**a. Import the generated CSS** anywhere in your app's normal global stylesheet import chain (wherever your `@import "tailwindcss"` already lives):
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
@import './theme/premium.css';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**b. Pass the generated tokens straight to `WangsUiProvider`'s `theme.customColorFamilies`** — this is a real `ThemeProviderProps` field, not a convention you assemble yourself. The provider resolves it to the current mode and injects the CSS variables internally, the exact same pass the built-in families already get (`CssVariablesInjector`) — adding a family is purely additive data, no new component in your tree:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { WangsUiProvider } from '@wangs-ui/react-core/api';
|
|
81
|
+
import { premiumTokens } from './theme/premium';
|
|
82
|
+
|
|
83
|
+
<WangsUiProvider
|
|
84
|
+
configOptions={{ preset }}
|
|
85
|
+
theme={{ palette, mode, customColorFamilies: { premium: premiumTokens } }}
|
|
86
|
+
>
|
|
87
|
+
<App />
|
|
88
|
+
</WangsUiProvider>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A second custom family is one more entry in the same object (`{ premium: premiumTokens, verified: verifiedTokens }`) — never a second wrapper.
|
|
92
|
+
|
|
93
|
+
## 3. Use it — same as any built-in family
|
|
94
|
+
|
|
95
|
+
From here on, every component just uses ordinary Tailwind utility classes, exactly like `bg-success-container`/`text-on-success-container`:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
function PremiumBadge() {
|
|
99
|
+
return <span className="bg-premium-container text-on-premium-container">Premium</span>;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
No import, no hook, no per-component wiring — any component anywhere in the tree can reach for `bg-premium` / `text-on-premium` / `bg-premium-emphasized` / `hover:bg-premium-hover` / etc. the same way it already reaches for the built-in families.
|
|
104
|
+
|
|
105
|
+
If a component needs the plain hex value instead of a class (a canvas draw call, an SVG fill, a chart library prop), `useTheme().customColors.premium` is already resolved to the current mode — no manual `premiumTokens[mode]` indexing:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
const { customColors } = useTheme();
|
|
109
|
+
customColors.premium.fill; // already the right mode's hex
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**React Native** has no `.css`/Tailwind step: skip step **a** in section 2, but still pass `customColorFamilies` to `theme` in step **b** — then read `useTheme().customColors.premium` (etc.) the same way, directly in `style`.
|
|
113
|
+
|
|
114
|
+
## 4. Regenerating / evolving a custom family
|
|
115
|
+
|
|
116
|
+
To change the color, re-run the same command with the new hex:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pnpm exec wangs-ui-generate-color-family --name=premium --color=#9333ea --out=src/theme/premium.ts
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This rewrites `premium.ts` with the new values and rewrites `premium.css` too (harmless — it's the same static names every time, nothing in your app needs to change because of it).
|