@mhmo91/schmancy 0.10.9 → 0.10.11

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.
@@ -203,7 +203,7 @@ The manifest already has everything needed; the package is just the JSON-RPC wra
203
203
 
204
204
  **Problem.** `handover/agent-runtime-v1.md` had `<PENDING>` placeholders that we manually replaced with `0.9.13` after the first publish. Future handover docs will have the same issue.
205
205
 
206
- **Fix.** A build step that substitutes `0.10.9` in `handover/**/*.md` against `package.json`'s `version` field on every build. `dist/handover/**/*.md` gets the rendered version; the source stays templated.
206
+ **Fix.** A build step that substitutes `0.10.11` in `handover/**/*.md` against `package.json`'s `version` field on every build. `dist/handover/**/*.md` gets the rendered version; the source stays templated.
207
207
 
208
208
  **Effort:** ~30 min (one sed step or a tiny script).
209
209
 
@@ -7,8 +7,8 @@
7
7
  ## The URLs you asked for
8
8
 
9
9
  ```
10
- https://esm.sh/@mhmo91/schmancy/agent@0.10.9
11
- https://esm.sh/@mhmo91/schmancy/agent/manifest@0.10.9
10
+ https://esm.sh/@mhmo91/schmancy/agent@0.10.11
11
+ https://esm.sh/@mhmo91/schmancy/agent/manifest@0.10.11
12
12
  ```
13
13
 
14
14
  `0.9.13` is the first release containing `/agent`; every subsequent publish serves the same subpath. `npm view @mhmo91/schmancy version` always returns the current pin if you want to float forward.
@@ -20,7 +20,7 @@ One script tag. No bundler, no bare specifiers, no npm install.
20
20
  ```html
21
21
  <!doctype html>
22
22
  <script type="module">
23
- import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.10.9';
23
+ import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.10.11';
24
24
  </script>
25
25
  <schmancy-theme root scheme="dark">
26
26
  <schmancy-surface type="solid" fill="all">
@@ -1,88 +1,124 @@
1
1
  # Claude Design brief: building with schmancy
2
2
 
3
- You're designing or redesigning a web page. Use the `@mhmo91/schmancy` component library for every interactive tag, every surface, every color. This file is the operating manual — paste it in alongside the design ask.
3
+ You're designing or redesigning a web page. Use the `@mhmo91/schmancy`
4
+ component library for every interactive tag, every surface, every
5
+ color. This file is the operating manual — paste it in alongside the
6
+ design ask.
4
7
 
5
8
  ## Pin URL
6
9
 
7
10
  ```
8
- https://cdn.jsdelivr.net/npm/@mhmo91/schmancy@0.9.21/dist/agent/schmancy.agent.js
11
+ https://esm.sh/@mhmo91/schmancy/agent
9
12
  ```
10
13
 
11
- One script tag installs 100+ `<schmancy-*>` custom elements plus the `window.schmancy.*` discovery API. No bundler, no npm install, no bare specifiers.
14
+ One module import installs every `<schmancy-*>` custom element. No
15
+ bundler, no npm install, no bare specifiers. The same URL also
16
+ re-exports the full library surface (`theme`, `area`, `state`, `show`,
17
+ `lazy`, every directive, every service, `SchmancyElement`) for in-page
18
+ script code.
19
+
20
+ For introspection — every tag's attributes, events, slots, CSS parts,
21
+ plus the `values` array on every typed attribute — read the static
22
+ manifest at `https://esm.sh/@mhmo91/schmancy/agent/manifest` (JSON,
23
+ shape follows Custom Elements Manifest v1).
12
24
 
13
25
  ## App shell (always this shape)
14
26
 
15
27
  ```html
16
28
  <!doctype html>
17
29
  <script type="module">
18
- import 'https://cdn.jsdelivr.net/npm/@mhmo91/schmancy@0.9.21/dist/agent/schmancy.agent.js';
30
+ import 'https://esm.sh/@mhmo91/schmancy/agent';
19
31
  </script>
20
32
 
21
33
  <schmancy-theme root scheme="dark">
22
34
  <schmancy-surface type="solid" fill="all">
23
35
  <!-- your page content -->
24
- <schmancy-skill></schmancy-skill>
25
36
  </schmancy-surface>
26
37
  </schmancy-theme>
27
38
  ```
28
39
 
29
- - `<schmancy-theme>` generates a Material 3 palette from a seed color + scheme. Attribute `root` publishes tokens onto `document.body`.
30
- - `<schmancy-surface>` picks up theme tokens for background / on-color / elevation. Nest surfaces for hierarchical color stacking.
31
- - `<schmancy-skill>` (drop once on the page) installs `window.schmancy.help()`, `.tokens()`, `.capabilities()`, etc.
40
+ - `<schmancy-theme>` generates a Material 3 palette from a seed color +
41
+ scheme. Attribute `root` publishes tokens onto `document.body`.
42
+ - `<schmancy-surface>` picks up theme tokens for background / on-color
43
+ / elevation. Nest surfaces for hierarchical color stacking.
32
44
 
33
- ## Discovery — ask the runtime what's available
45
+ ## Discovery — read the static manifest
34
46
 
35
47
  ```js
36
- window.schmancy.help()
37
- // { elements: [{ tag, summary }...], services: [{ name, summary }...] }
38
-
39
- window.schmancy.help('schmancy-button')
40
- // full declaration: attributes (with enum values[]), events, slots,
41
- // cssParts, cssProperties, examples (copy-pastable), platformPrimitive
42
-
43
- window.schmancy.tokens()
44
- // every --schmancy-sys-color-* custom property name, for direct CSS use
45
-
46
- window.schmancy.capabilities()
47
- // → { popover, declarativeShadowDom, scopedRegistries, trustedTypes,
48
- // cssRegisteredProperties, elementInternalsAria, formAssociated,
49
- // adoptedStyleSheets }
48
+ const manifest = await fetch('https://esm.sh/@mhmo91/schmancy/agent/manifest')
49
+ .then(r => r.json())
50
+
51
+ // Every tag, attribute, event, slot, CSS part, CSS property.
52
+ // Typed-attribute enums surface as `values: ['filled', 'tonal', ...]`
53
+ // so you don't parse `"'filled' | 'tonal' | ..."` strings.
54
+ manifest.modules
55
+ .flatMap(m => m.declarations)
56
+ .filter(d => d.kind === 'class' && d.tagName?.startsWith('schmancy-'))
50
57
  ```
51
58
 
52
- `help(tag)` is authoritative. Its `examples[]` array is copy-pastable. Its `platformPrimitive` hint tells you what native element the tag semantically wraps, so you can degrade gracefully if a tag fails to register.
59
+ The manifest is the authoritative source. Pull a tag's declaration
60
+ once at page-build time and use the `attributes`, `events`, `slots`,
61
+ and `cssProperties` arrays to drive the rest of the page.
53
62
 
54
63
  ## Rules
55
64
 
56
- 1. **Every UI tag is `<schmancy-*>`.** Use `<schmancy-button>`, not `<button>`. `<schmancy-input>`, not `<input>`. `<schmancy-list-item>`, not `<li>`. Probe `help()` for the exact tag and its attributes.
57
- 2. **Colors: Tailwind utility classes against schmancy tokens.** Every `--schmancy-sys-color-*` token is exposed as a Tailwind color utility.
65
+ 1. **Every UI tag is `<schmancy-*>`.** Use `<schmancy-button>`, not
66
+ `<button>`. `<schmancy-input>`, not `<input>`. `<schmancy-list-item>`,
67
+ not `<li>`. Look up the exact tag in the manifest.
68
+ 2. **Colors: Tailwind utility classes against schmancy tokens.** Every
69
+ `--schmancy-sys-color-*` token is exposed as a Tailwind color
70
+ utility.
58
71
  - `bg-primary-default`, `bg-primary-container`, `text-primary-on`
59
72
  - `bg-surface-default`, `bg-surface-low`, `bg-surface-high`, `text-surface-on`
60
73
  - `border-outline`, `border-outline-variant`
61
74
  - `bg-secondary-container`, `bg-error-default`, `bg-success-default`, `bg-warning-default`
62
- - **Never hex (`#6200ee`), never arbitrary values (`bg-[#ff0000]`).** Both defeat theming.
63
- 3. **Forms:** wrap form controls in `<schmancy-form>`. Its `submit` event fires with a `FormData` payload — no manual walking of inputs. Every form control (`<schmancy-input>`, `<schmancy-select>`, `<schmancy-checkbox>`, etc.) is form-associated via `ElementInternals`, so `new FormData(form)` just works.
75
+ - **Never hex (`#6200ee`), never arbitrary values (`bg-[#ff0000]`).**
76
+ Both defeat theming.
77
+ 3. **Forms:** wrap form controls in `<schmancy-form>`. Its `submit`
78
+ event fires with a `FormData` payload — no manual walking of inputs.
79
+ Every form control (`<schmancy-input>`, `<schmancy-select>`,
80
+ `<schmancy-checkbox>`, etc.) is form-associated via
81
+ `ElementInternals`, so `new FormData(form)` just works.
64
82
  4. **Layout:**
65
- - `<schmancy-page rows="auto_1fr_auto">` for app shell — fills viewport, suppresses double-tap zoom and pull-to-refresh.
66
- - `<schmancy-nav-drawer>` for responsive sidebar + app-bar + content (persistent on desktop, modal on mobile).
67
- - `<schmancy-scroll>` when you need debounced scroll events or hidden scrollbars.
68
- - Use Tailwind's `grid` / `flex` utilities directly for layout math — no `<schmancy-grid>` (deprecated).
69
- 5. **Overlays use imperative services, not element APIs:**
83
+ - `<schmancy-page rows="auto_1fr_auto">` for app shell — fills
84
+ viewport, suppresses double-tap zoom and pull-to-refresh.
85
+ - `<schmancy-nav-drawer>` for responsive sidebar + app-bar +
86
+ content (persistent on desktop, modal on mobile).
87
+ - `<schmancy-scroll>` when you need debounced scroll events or
88
+ hidden scrollbars.
89
+ - `<schmancy-grid>` and `<schmancy-flex>` for layout primitives
90
+ with design intent; raw Tailwind `grid` / `flex` utilities for
91
+ incidental layout math.
92
+ 5. **Overlays use the imperative service, not element APIs:**
70
93
  ```js
71
- import { $dialog, sheet, $notify, SchmancySheetPosition } from 'https://cdn.jsdelivr.net/npm/@mhmo91/schmancy@0.9.21/dist/agent/schmancy.agent.js';
72
- $dialog.confirm({ title: 'Delete?', message: 'Cannot be undone.', confirmText: 'Delete', cancelText: 'Keep' });
73
- sheet.open({ component: new MyEditor(), position: SchmancySheetPosition.Side });
94
+ import { show, confirm, prompt } from 'https://esm.sh/@mhmo91/schmancy/agent';
95
+ import { $notify } from 'https://esm.sh/@mhmo91/schmancy/agent';
96
+
97
+ show(new MyEditor()); // centered fallback
98
+ show(new QuickPicker(), { anchor: ev }); // anchored at click
99
+ show(new SheetForm()); // narrow viewport → sheet (auto)
100
+ await confirm({ title: 'Delete?', message: 'Cannot be undone.', confirmText: 'Delete' });
74
101
  $notify.success('Saved');
75
102
  ```
76
- 6. **Accessibility is built in.** Components handle ARIA roles, focus management, keyboard navigation, form validation messages. Don't re-implement. Do provide `aria-label` on icon-only buttons (`<schmancy-icon-button aria-label="Close">`).
77
- 7. **Typography:** use `<schmancy-typography type="..." token="...">` for text. Type = `display` / `headline` / `title` / `body` / `label`. Token = `lg` / `md` / `sm`.
78
- 8. **Icons:** `<schmancy-icon>close</schmancy-icon>` renders a Material Symbols glyph. Pass the icon name as text content.
103
+ `show()` is the single overlay primitive layout (centered /
104
+ anchored / sheet) is chosen automatically by viewport + anchor
105
+ presence. There is no `$dialog` and no `sheet` service.
106
+ 6. **Accessibility is built in.** Components handle ARIA roles, focus
107
+ management, keyboard navigation, form validation messages. Don't
108
+ re-implement. Do provide `aria-label` on icon-only buttons
109
+ (`<schmancy-icon-button aria-label="Close">`).
110
+ 7. **Typography:** use `<schmancy-typography type="..." token="...">`
111
+ for text. Type = `display` / `headline` / `title` / `body` / `label`.
112
+ Token = `lg` / `md` / `sm`.
113
+ 8. **Icons:** `<schmancy-icon>close</schmancy-icon>` renders a Material
114
+ Symbols glyph. Pass the icon name as text content.
79
115
 
80
116
  ## Minimum working page (copy, paste, it runs)
81
117
 
82
118
  ```html
83
119
  <!doctype html>
84
120
  <script type="module">
85
- import 'https://cdn.jsdelivr.net/npm/@mhmo91/schmancy@0.9.21/dist/agent/schmancy.agent.js';
121
+ import 'https://esm.sh/@mhmo91/schmancy/agent';
86
122
  </script>
87
123
 
88
124
  <schmancy-theme root scheme="auto" color="#6200ee">
@@ -114,18 +150,22 @@ window.schmancy.capabilities()
114
150
  <schmancy-navigation-bar-item icon="search" label="Search"></schmancy-navigation-bar-item>
115
151
  <schmancy-navigation-bar-item icon="settings" label="Settings"></schmancy-navigation-bar-item>
116
152
  </schmancy-navigation-bar>
117
-
118
- <schmancy-skill></schmancy-skill>
119
153
  </schmancy-page>
120
154
  </schmancy-theme>
121
155
  ```
122
156
 
123
157
  ## When the design asks for something you don't recognize
124
158
 
125
- 1. Call `window.schmancy.help()` and scan the `elements[]` summaries for a tag that fits.
126
- 2. Call `window.schmancy.help('schmancy-<tag>')` for exact attributes + copy-pastable examples.
127
- 3. If no tag fits, compose from primitives (`<schmancy-surface>` + `<schmancy-button>` + Tailwind layout) before reaching for a custom element.
159
+ 1. Fetch the manifest and filter `declarations` by `tagName` to find a
160
+ tag that fits the role.
161
+ 2. Read the matching declaration's `attributes`, `slots`, `events`,
162
+ and `cssProperties` arrays — they're the contract.
163
+ 3. If no tag fits, compose from primitives (`<schmancy-surface>` +
164
+ `<schmancy-button>` + `<schmancy-grid>` / `<schmancy-flex>` for
165
+ layout) before reaching for a one-off custom element.
128
166
 
129
167
  ## Report bugs
130
168
 
131
- github.com/mhmo91/schmancy — include `window.schmancy.capabilities()`, the minimum failing HTML, and `window.schmancy.manifest.schemaVersion`.
169
+ `github.com/mhmo91/schmancy` — include the schmancy version
170
+ (`https://esm.sh/@mhmo91/schmancy/package.json`), the minimum failing
171
+ HTML, and the manifest's `schemaVersion`.
@@ -13,9 +13,11 @@ Claude Design (Anthropic Labs) extracts a design system from a codebase at org-o
13
13
 
14
14
  ```
15
15
  schmancy — Material 3 web-component library (100+ <schmancy-*> Lit elements,
16
- Tailwind 4 theme tokens, RxJS). Single-URL ESM runtime ships a discovery API
17
- via `window.schmancy.help()`. Designs look like Material You: tonal surfaces,
18
- rounded corners, dynamic color palette generated from a seed color.
16
+ Tailwind 4 theme tokens, RxJS). Single-URL ESM runtime registers every tag
17
+ from one import. Component contracts are introspectable via the static
18
+ Custom Elements Manifest at @mhmo91/schmancy/agent/manifest. Designs look
19
+ like Material You: tonal surfaces, rounded corners, dynamic color palette
20
+ generated from a seed color.
19
21
  ```
20
22
 
21
23
  **Link code on GitHub:**
@@ -37,10 +39,12 @@ Use <schmancy-*> custom elements for every UI tag — never raw <button>, <input
37
39
  Colors: Tailwind utility classes against schmancy theme tokens (bg-primary-default,
38
40
  text-surface-on, border-outline-variant). Never hex. Never arbitrary values like bg-[#xxx].
39
41
  App shell: wrap in <schmancy-theme root scheme="auto" color="#seed"> then
40
- <schmancy-surface type="solid" fill="all">. Include one <schmancy-skill></schmancy-skill>
41
- so window.schmancy.help() / tokens() / capabilities() are live for introspection.
42
- Full operating manual + copy-pastable minimum page: handover/claude-design-brief.md
43
- in the repo.
42
+ <schmancy-surface type="solid" fill="all">. Overlays use the imperative service
43
+ `show()` / `confirm()` / `prompt()` from @mhmo91/schmancy/overlay there is no
44
+ $dialog or sheet service. Component contracts (attributes, events, slots, CSS
45
+ parts, typed-attribute enums) live in the static manifest at
46
+ @mhmo91/schmancy/agent/manifest. Full operating manual + copy-pastable minimum
47
+ page: handover/claude-design-brief.md in the repo.
44
48
  ```
45
49
 
46
50
  ## After onboarding
@@ -2,7 +2,7 @@
2
2
 
3
3
  Every token your design can lean on, grouped by what it's for. Use the **Tailwind class** column when styling — every system token is exposed as a Tailwind utility, so reach for `bg-primary-default` or `text-surface-on` before the raw CSS variable. The CSS variable column is the fallback for properties Tailwind doesn't cover (e.g. `box-shadow`, `transition-duration`).
4
4
 
5
- Source of truth: `src/theme/theme.interface.ts` in this repo. Live introspection: `window.schmancy.tokens()` returns the full flat list of CSS custom property names.
5
+ Source of truth: `src/theme/theme.interface.ts` in this repo. The same flat list of CSS custom property names is enumerated in the static manifest at `@mhmo91/schmancy/agent/manifest` under each tag's `cssProperties` array.
6
6
 
7
7
  > **Never use raw hex** (`#6200ee`) **or arbitrary values** (`bg-[#ff0000]`). Both defeat theming — the whole palette is regenerated from `<schmancy-theme color="…">` and your hardcoded color won't follow scheme switches.
8
8
 
@@ -188,11 +188,17 @@ Opacity multipliers for interactive states.
188
188
 
189
189
  Both bypass theming and break when `<schmancy-theme color="…" scheme="…">` regenerates the palette.
190
190
 
191
- ## Live introspection
191
+ ## Introspection at build time
192
192
 
193
193
  ```js
194
- window.schmancy.tokens()
195
- // string[] — every --schmancy-sys-* property name available at runtime,
196
- // in the order they were registered. Use this to verify a token exists
197
- // before referencing it in CSS.
194
+ const manifest = await fetch('https://esm.sh/@mhmo91/schmancy/agent/manifest')
195
+ .then(r => r.json())
196
+
197
+ const tokens = new Set(
198
+ manifest.modules
199
+ .flatMap(m => m.declarations)
200
+ .flatMap(d => (d.cssProperties ?? []).map(p => p.name))
201
+ )
202
+ // → Set<string> — every --schmancy-sys-* property name surfaced by any tag.
203
+ // Use this to verify a token exists before referencing it in CSS.
198
204
  ```
@@ -8,7 +8,7 @@ The framework pieces — touch before components.
8
8
 
9
9
  - [Area](./area.md) — `<schmancy-area>`, `<schmancy-route>`, `area.push()`, `lazy()` for routing.
10
10
  - [State](./state.md) — `state()` factory (memory / session / local / idb), variant write APIs (`Object` / `Map` / `Set` / `Array` / `Scalar`), `bindState`, `computed`, `stateFromObservable`.
11
- - [Mixins](./mixins.md) — `$LitElement` base class.
11
+ - [Mixins](./mixins.md) — `SchmancyElement` base class.
12
12
  - [Theme](./theme.md) — `<schmancy-theme>`, color scheme, CSS variables.
13
13
  - [Directives](./directives.md) — Lit directives for physics, effects, text, visibility, interaction.
14
14
  - [Animation](./animation.md) — Spring presets (`SPRING_SMOOTH`, etc.), `createAnimation`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: schmancy
3
- description: UI patterns, component APIs, and conventions for the @mhmo91/schmancy web-component library (Lit + RxJS + Tailwind) — the exclusive UI stack in this repo's `web/` workspace. Fire this skill on ANY web-UI work, even when the user doesn't name schmancy explicitly — including adding or editing a component, building a form, showing a dialog / toast / side drawer / bottom sheet, wiring routing, reading or writing a context, styling with theme tokens, adding a drop zone / file input / date picker / autocomplete, working with `$LitElement`, or touching any `<schmancy-*>` tag. Also fire on prompts like "build a page", "add a modal", "wire a route", "save user prefs in storage", "animate this", "style with our theme", "make a notification", "how do I do X in Lit", "my drag-and-drop", "dark mode toggle".
3
+ description: UI patterns, component APIs, and conventions for the @mhmo91/schmancy web-component library (Lit + RxJS + Tailwind) — the exclusive UI stack in this repo's `web/` workspace. Fire this skill on ANY web-UI work, even when the user doesn't name schmancy explicitly — including adding or editing a component, building a form, showing a dialog / toast / side drawer / bottom sheet, wiring routing, reading or writing a state, styling with theme tokens, adding a drop zone / file input / date picker / autocomplete, working with `SchmancyElement`, or touching any `<schmancy-*>` tag. Also fire on prompts like "build a page", "add a modal", "wire a route", "save user prefs in storage", "animate this", "style with our theme", "make a notification", "how do I do X in Lit", "my drag-and-drop", "dark mode toggle".
4
4
  ---
5
5
 
6
6
  # Schmancy
@@ -56,19 +56,20 @@ Use component tags (`<schmancy-menu>`, `<schmancy-dropdown>`, `<schmancy-tooltip
56
56
  Remediation: before reporting any file compliant, run a grep against each forbidden pattern named by the rule under audit (e.g. `grep -nE '\[(?:[^]]+)\]' <file>` for `TOKEN_FIRST_NO_ARBITRARY`; `grep -nE 'class="[^"]*\b(text-|bg-|border-|rounded-|shadow-|tracking-|leading-|p[xy]?-)' <file>` plus a manual scan for raw `<div>`/`<span>` carrying those classes for `PRIMITIVE_FIRST`). The compliance report's body is the concatenation of those grep outputs annotated with their rule name; an empty body across every pattern under every rule is the only form of "compliant". A claim of compliance unaccompanied by the grep evidence is not a compliance report and is treated as an unverified assertion that the audit loop rejects.
57
57
  - **Schmancy primitive first** (`PRIMITIVE_FIRST`). Within `web/**`, every visible UI element is a custom element exported from `packages/schmancy/src/**`, and an element absent from that export set is added there before being imported into `web/**`. The rule sits above the styling rules: a `<div class="text-xs text-surface-on-variant">…</div>` whose role is typography is a violation even when every utility resolves to a registered token, because `<schmancy-typography>` already covers that role; the styling rules apply only to whatever class strings remain after the right primitive has been selected.
58
58
  Sources: [packages/schmancy/skills/schmancy/INDEX.md](../INDEX.md) catalogues the export set by job (foundations / atoms / forms / navigation / overlays / interaction / feedback / display); each role's reference file (`typography.md`, `surface.md`, `button.md`, `overlay.md`, …) names the props, slots, and events that displace the equivalent `<div>` + utility-class pattern. The export set is the single source — a primitive that is not exported from `packages/schmancy/src/**` does not satisfy this rule even if it lives in a private file inside the schmancy tree.
59
- Remediation: walk every `.ts` and `.html` file under `web/**` and list every raw HTML element whose class string carries design-system styling (typography, color, spacing-as-design-decision, surface, layout-as-design-decision, motion, overlay) — those are the violations. For each, look up the matching schmancy primitive in `INDEX.md` and rewrite the element through that primitive (`<schmancy-typography type=… token=…>` for type-scale text, `<schmancy-surface type=… fill=…>` for elevated/bounded surfaces, `<schmancy-grid>`/`<schmancy-flex>` for layout primitives with design intent, the imperative `show`/`$notify`/`schmancyContentDrawer.push` services for overlays, `<schmancy-scroll>` for scroll containers). When a needed primitive is absent from the export set, design and implement it as a new component under `packages/schmancy/src/<role>/` — extending `$LitElement(style?)`, registered in `HTMLElementTagNameMap`, exported through the package barrel, and documented with a sibling `.md` in the skill's reference set — and only then introduce the first call site in `web/**`. The audit subagent iterates the whole `web/**` tree, surfaces the violation list, applies the rewrites, runs `yarn workspace @momo/web tsc --noEmit` plus the colocated `*-view.test.ts` suites, and reports pre-existing violations that require a new schmancy primitive as a separate punch list for designer/architect approval before the implementation lands. The loop exits when every `web/**` file's visible UI elements are schmancy primitives and the typecheck plus the test suites pass.
59
+ Remediation: walk every `.ts` and `.html` file under `web/**` and list every raw HTML element whose class string carries design-system styling (typography, color, spacing-as-design-decision, surface, layout-as-design-decision, motion, overlay) — those are the violations. For each, look up the matching schmancy primitive in `INDEX.md` and rewrite the element through that primitive (`<schmancy-typography type=… token=…>` for type-scale text, `<schmancy-surface type=… fill=…>` for elevated/bounded surfaces, `<schmancy-grid>`/`<schmancy-flex>` for layout primitives with design intent, the imperative `show`/`$notify`/`schmancyContentDrawer.push` services for overlays, `<schmancy-scroll>` for scroll containers). When a needed primitive is absent from the export set, design and implement it as a new component under `packages/schmancy/src/<role>/` — extending `SchmancyElement` with `static styles = [css\`...\`]`, registered in `HTMLElementTagNameMap`, exported through the package barrel, and documented with a sibling `.md` in the skill's reference set — and only then introduce the first call site in `web/**`. The audit subagent iterates the whole `web/**` tree, surfaces the violation list, applies the rewrites, runs `yarn workspace @momo/web tsc --noEmit` plus the colocated `*-view.test.ts` suites, and reports pre-existing violations that require a new schmancy primitive as a separate punch list for designer/architect approval before the implementation lands. The loop exits when every `web/**` file's visible UI elements are schmancy primitives and the typecheck plus the test suites pass.
60
60
 
61
61
  ## Non-negotiable conventions
62
62
 
63
63
  **Component authoring**
64
- - Every component extends `SchmancyElement` and declares its component-local CSS via `static styles = [css\`...\`]`. Never raw `LitElement`. Never wrap with `SignalWatcher` — the base already includes it; double-wrapping creates two nested Computeds and panics with "Detected cycle in computations" at runtime. The deprecated `$LitElement(style?)` factory still works (it now just delegates to SchmancyElement) but should not appear in new code.
64
+ - Every component extends `SchmancyElement` and declares its component-local CSS via `static styles = [css\`...\`]`. Never raw `LitElement`. Never wrap with `SignalWatcher` — the base already includes it; double-wrapping creates two nested Computeds and panics with "Detected cycle in computations" at runtime.
65
65
  - Every RxJS subscription ends with `.pipe(takeUntil(this.disconnecting))`.
66
66
  - Register the tag in `HTMLElementTagNameMap` for TypeScript.
67
67
 
68
68
  **State**
69
- - Contexts live at module scope. Many small contexts beat one monolith.
70
- - Gate subscriptions with `filter(() => ctx.ready)` when reading persisted contexts.
71
- - Storage tiers: `'memory'` (regenerable) · `'session'` (per-tab) · `'local'` (user prefs) · `'indexeddb'` (>100-entry collections).
69
+ - States live at module scope. Many small states beat one monolith. Use `state('feature/name').{memory,session,local,idb}(initial)` from `@mhmo91/schmancy/state`.
70
+ - Reading `state.value` inside `render()` auto-tracks via the base class's `SignalWatcher` no decorator or binding needed for the default case.
71
+ - `await state.ready` (or `if (state.loaded)`) before reading persisted-backend values that hydrate asynchronously.
72
+ - Storage tiers: `.memory()` (regenerable) · `.session()` (per-tab) · `.local()` (user prefs) · `.idb()` (>100-entry collections).
72
73
 
73
74
  **Routing**
74
75
  - Route guards are `Observable<boolean>`, never cached booleans.
@@ -36,7 +36,7 @@ sound.muted$.subscribe(m => {})
36
36
  ```
37
37
 
38
38
  ## Settings Persistence
39
- Volume, mute, and custom theme persist to `localStorage` under key `schmancy-sound-settings` (via `createContext`).
39
+ Volume, mute, and custom theme persist to `localStorage` under key `schmancy-sound-settings` (via `state(...).local(...)` from `@mhmo91/schmancy/state`).
40
40
 
41
41
  ## AI-Generated Themes
42
42
  ```typescript
@@ -38,7 +38,7 @@ discoverAnyComponent('schmancy-navigation-rail', 'schmancy-navigation-bar')
38
38
  ```
39
39
 
40
40
  ### `discoverElement(selector, timeout = 150)`
41
- Finds any element by CSS selector across shadow DOM. Uses a request ID + universal `schmancy-discover` event. Every `$LitElement` responds if it finds a match in its shadow root.
41
+ Finds any element by CSS selector across shadow DOM. Uses a request ID + universal `schmancy-discover` event. Every `SchmancyElement` responds if it finds a match in its shadow root.
42
42
  ```typescript
43
43
  discoverElement('[data-section="pricing"]').subscribe(section => section?.scrollIntoView())
44
44
  ```
@@ -51,12 +51,12 @@ discoverAllElements('.flagged').subscribe(all => console.log(all.length))
51
51
 
52
52
  ## How the Handshake Works
53
53
  1. Caller creates a unique `requestId` and broadcasts `schmancy-discover` on `window` with `{ selector, requestId }`.
54
- 2. Every `$LitElement` listens for this event (wired up in the base class).
54
+ 2. Every `SchmancyElement` listens for this event (wired up in the base class).
55
55
  3. Any matching element dispatches `schmancy-discover-response` with `{ requestId, element }`.
56
56
  4. Caller collects responses for the timeout window and emits via RxJS.
57
57
 
58
58
  ## Pattern in Base Class
59
- Every `$LitElement` inherits auto-response: `discover<T>(tag)` (method on the component) and `{tagName}-where-are-you`/`{tagName}-here-i-am` events. See [mixins.md](./mixins.md).
59
+ Every `SchmancyElement` inherits auto-response: `discover<T>(tag)` (method on the component) and `{tagName}-where-are-you`/`{tagName}-here-i-am` events. See [mixins.md](./mixins.md).
60
60
 
61
61
  ## When to Use
62
62
  - Cross-shadow coordination between unrelated components.
@@ -36,4 +36,4 @@ Auto-dismisses the dialog when clicked. Renders as a `schmancy-list-item` intern
36
36
  </schmancy-menu>
37
37
  ```
38
38
 
39
- Uses `$dialog.component()` internally. Menu items auto-dismiss the dialog. Custom components in the default slot must call `$dialog.dismiss()` manually.
39
+ Uses `show(overlay, { anchor })` from `@mhmo91/schmancy/overlay` internally. `<schmancy-menu-item>` auto-closes the menu by dispatching a `'close'` event on click. Custom components in the default slot dismiss the menu the same way: `this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))`.
@@ -40,7 +40,7 @@ Centered is the fallback (no anchor given). Sheet is the responsive adaptation (
40
40
 
41
41
  ## Subscription IS the overlay lifecycle
42
42
 
43
- Inside any `$LitElement`, pipe `takeUntil(this.disconnecting)`. When the caller unmounts, the overlay auto-dismisses — no handles to track, no leaks.
43
+ Inside any `SchmancyElement`, pipe `takeUntil(this.disconnecting)`. When the caller unmounts, the overlay auto-dismisses — no handles to track, no leaks.
44
44
 
45
45
  ```ts
46
46
  show(MyForm, { props: { id }, anchor: ev })
@@ -8,7 +8,7 @@ The framework pieces — touch before components.
8
8
 
9
9
  - [Area](./area.md) — `<schmancy-area>`, `<schmancy-route>`, `area.push()`, `lazy()` for routing.
10
10
  - [State](./state.md) — `state()` factory (memory / session / local / idb), variant write APIs (`Object` / `Map` / `Set` / `Array` / `Scalar`), `bindState`, `computed`, `stateFromObservable`.
11
- - [Mixins](./mixins.md) — `$LitElement` base class.
11
+ - [Mixins](./mixins.md) — `SchmancyElement` base class.
12
12
  - [Theme](./theme.md) — `<schmancy-theme>`, color scheme, CSS variables.
13
13
  - [Directives](./directives.md) — Lit directives for physics, effects, text, visibility, interaction.
14
14
  - [Animation](./animation.md) — Spring presets (`SPRING_SMOOTH`, etc.), `createAnimation`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: schmancy
3
- description: UI patterns, component APIs, and conventions for the @mhmo91/schmancy web-component library (Lit + RxJS + Tailwind) — the exclusive UI stack in this repo's `web/` workspace. Fire this skill on ANY web-UI work, even when the user doesn't name schmancy explicitly — including adding or editing a component, building a form, showing a dialog / toast / side drawer / bottom sheet, wiring routing, reading or writing a context, styling with theme tokens, adding a drop zone / file input / date picker / autocomplete, working with `$LitElement`, or touching any `<schmancy-*>` tag. Also fire on prompts like "build a page", "add a modal", "wire a route", "save user prefs in storage", "animate this", "style with our theme", "make a notification", "how do I do X in Lit", "my drag-and-drop", "dark mode toggle".
3
+ description: UI patterns, component APIs, and conventions for the @mhmo91/schmancy web-component library (Lit + RxJS + Tailwind) — the exclusive UI stack in this repo's `web/` workspace. Fire this skill on ANY web-UI work, even when the user doesn't name schmancy explicitly — including adding or editing a component, building a form, showing a dialog / toast / side drawer / bottom sheet, wiring routing, reading or writing a state, styling with theme tokens, adding a drop zone / file input / date picker / autocomplete, working with `SchmancyElement`, or touching any `<schmancy-*>` tag. Also fire on prompts like "build a page", "add a modal", "wire a route", "save user prefs in storage", "animate this", "style with our theme", "make a notification", "how do I do X in Lit", "my drag-and-drop", "dark mode toggle".
4
4
  ---
5
5
 
6
6
  # Schmancy
@@ -56,19 +56,20 @@ Use component tags (`<schmancy-menu>`, `<schmancy-dropdown>`, `<schmancy-tooltip
56
56
  Remediation: before reporting any file compliant, run a grep against each forbidden pattern named by the rule under audit (e.g. `grep -nE '\[(?:[^]]+)\]' <file>` for `TOKEN_FIRST_NO_ARBITRARY`; `grep -nE 'class="[^"]*\b(text-|bg-|border-|rounded-|shadow-|tracking-|leading-|p[xy]?-)' <file>` plus a manual scan for raw `<div>`/`<span>` carrying those classes for `PRIMITIVE_FIRST`). The compliance report's body is the concatenation of those grep outputs annotated with their rule name; an empty body across every pattern under every rule is the only form of "compliant". A claim of compliance unaccompanied by the grep evidence is not a compliance report and is treated as an unverified assertion that the audit loop rejects.
57
57
  - **Schmancy primitive first** (`PRIMITIVE_FIRST`). Within `web/**`, every visible UI element is a custom element exported from `packages/schmancy/src/**`, and an element absent from that export set is added there before being imported into `web/**`. The rule sits above the styling rules: a `<div class="text-xs text-surface-on-variant">…</div>` whose role is typography is a violation even when every utility resolves to a registered token, because `<schmancy-typography>` already covers that role; the styling rules apply only to whatever class strings remain after the right primitive has been selected.
58
58
  Sources: [packages/schmancy/skills/schmancy/INDEX.md](../INDEX.md) catalogues the export set by job (foundations / atoms / forms / navigation / overlays / interaction / feedback / display); each role's reference file (`typography.md`, `surface.md`, `button.md`, `overlay.md`, …) names the props, slots, and events that displace the equivalent `<div>` + utility-class pattern. The export set is the single source — a primitive that is not exported from `packages/schmancy/src/**` does not satisfy this rule even if it lives in a private file inside the schmancy tree.
59
- Remediation: walk every `.ts` and `.html` file under `web/**` and list every raw HTML element whose class string carries design-system styling (typography, color, spacing-as-design-decision, surface, layout-as-design-decision, motion, overlay) — those are the violations. For each, look up the matching schmancy primitive in `INDEX.md` and rewrite the element through that primitive (`<schmancy-typography type=… token=…>` for type-scale text, `<schmancy-surface type=… fill=…>` for elevated/bounded surfaces, `<schmancy-grid>`/`<schmancy-flex>` for layout primitives with design intent, the imperative `show`/`$notify`/`schmancyContentDrawer.push` services for overlays, `<schmancy-scroll>` for scroll containers). When a needed primitive is absent from the export set, design and implement it as a new component under `packages/schmancy/src/<role>/` — extending `$LitElement(style?)`, registered in `HTMLElementTagNameMap`, exported through the package barrel, and documented with a sibling `.md` in the skill's reference set — and only then introduce the first call site in `web/**`. The audit subagent iterates the whole `web/**` tree, surfaces the violation list, applies the rewrites, runs `yarn workspace @momo/web tsc --noEmit` plus the colocated `*-view.test.ts` suites, and reports pre-existing violations that require a new schmancy primitive as a separate punch list for designer/architect approval before the implementation lands. The loop exits when every `web/**` file's visible UI elements are schmancy primitives and the typecheck plus the test suites pass.
59
+ Remediation: walk every `.ts` and `.html` file under `web/**` and list every raw HTML element whose class string carries design-system styling (typography, color, spacing-as-design-decision, surface, layout-as-design-decision, motion, overlay) — those are the violations. For each, look up the matching schmancy primitive in `INDEX.md` and rewrite the element through that primitive (`<schmancy-typography type=… token=…>` for type-scale text, `<schmancy-surface type=… fill=…>` for elevated/bounded surfaces, `<schmancy-grid>`/`<schmancy-flex>` for layout primitives with design intent, the imperative `show`/`$notify`/`schmancyContentDrawer.push` services for overlays, `<schmancy-scroll>` for scroll containers). When a needed primitive is absent from the export set, design and implement it as a new component under `packages/schmancy/src/<role>/` — extending `SchmancyElement` with `static styles = [css\`...\`]`, registered in `HTMLElementTagNameMap`, exported through the package barrel, and documented with a sibling `.md` in the skill's reference set — and only then introduce the first call site in `web/**`. The audit subagent iterates the whole `web/**` tree, surfaces the violation list, applies the rewrites, runs `yarn workspace @momo/web tsc --noEmit` plus the colocated `*-view.test.ts` suites, and reports pre-existing violations that require a new schmancy primitive as a separate punch list for designer/architect approval before the implementation lands. The loop exits when every `web/**` file's visible UI elements are schmancy primitives and the typecheck plus the test suites pass.
60
60
 
61
61
  ## Non-negotiable conventions
62
62
 
63
63
  **Component authoring**
64
- - Every component extends `SchmancyElement` and declares its component-local CSS via `static styles = [css\`...\`]`. Never raw `LitElement`. Never wrap with `SignalWatcher` — the base already includes it; double-wrapping creates two nested Computeds and panics with "Detected cycle in computations" at runtime. The deprecated `$LitElement(style?)` factory still works (it now just delegates to SchmancyElement) but should not appear in new code.
64
+ - Every component extends `SchmancyElement` and declares its component-local CSS via `static styles = [css\`...\`]`. Never raw `LitElement`. Never wrap with `SignalWatcher` — the base already includes it; double-wrapping creates two nested Computeds and panics with "Detected cycle in computations" at runtime.
65
65
  - Every RxJS subscription ends with `.pipe(takeUntil(this.disconnecting))`.
66
66
  - Register the tag in `HTMLElementTagNameMap` for TypeScript.
67
67
 
68
68
  **State**
69
- - Contexts live at module scope. Many small contexts beat one monolith.
70
- - Gate subscriptions with `filter(() => ctx.ready)` when reading persisted contexts.
71
- - Storage tiers: `'memory'` (regenerable) · `'session'` (per-tab) · `'local'` (user prefs) · `'indexeddb'` (>100-entry collections).
69
+ - States live at module scope. Many small states beat one monolith. Use `state('feature/name').{memory,session,local,idb}(initial)` from `@mhmo91/schmancy/state`.
70
+ - Reading `state.value` inside `render()` auto-tracks via the base class's `SignalWatcher` no decorator or binding needed for the default case.
71
+ - `await state.ready` (or `if (state.loaded)`) before reading persisted-backend values that hydrate asynchronously.
72
+ - Storage tiers: `.memory()` (regenerable) · `.session()` (per-tab) · `.local()` (user prefs) · `.idb()` (>100-entry collections).
72
73
 
73
74
  **Routing**
74
75
  - Route guards are `Observable<boolean>`, never cached booleans.
@@ -36,7 +36,7 @@ sound.muted$.subscribe(m => {})
36
36
  ```
37
37
 
38
38
  ## Settings Persistence
39
- Volume, mute, and custom theme persist to `localStorage` under key `schmancy-sound-settings` (via `createContext`).
39
+ Volume, mute, and custom theme persist to `localStorage` under key `schmancy-sound-settings` (via `state(...).local(...)` from `@mhmo91/schmancy/state`).
40
40
 
41
41
  ## AI-Generated Themes
42
42
  ```typescript
@@ -38,7 +38,7 @@ discoverAnyComponent('schmancy-navigation-rail', 'schmancy-navigation-bar')
38
38
  ```
39
39
 
40
40
  ### `discoverElement(selector, timeout = 150)`
41
- Finds any element by CSS selector across shadow DOM. Uses a request ID + universal `schmancy-discover` event. Every `$LitElement` responds if it finds a match in its shadow root.
41
+ Finds any element by CSS selector across shadow DOM. Uses a request ID + universal `schmancy-discover` event. Every `SchmancyElement` responds if it finds a match in its shadow root.
42
42
  ```typescript
43
43
  discoverElement('[data-section="pricing"]').subscribe(section => section?.scrollIntoView())
44
44
  ```
@@ -51,12 +51,12 @@ discoverAllElements('.flagged').subscribe(all => console.log(all.length))
51
51
 
52
52
  ## How the Handshake Works
53
53
  1. Caller creates a unique `requestId` and broadcasts `schmancy-discover` on `window` with `{ selector, requestId }`.
54
- 2. Every `$LitElement` listens for this event (wired up in the base class).
54
+ 2. Every `SchmancyElement` listens for this event (wired up in the base class).
55
55
  3. Any matching element dispatches `schmancy-discover-response` with `{ requestId, element }`.
56
56
  4. Caller collects responses for the timeout window and emits via RxJS.
57
57
 
58
58
  ## Pattern in Base Class
59
- Every `$LitElement` inherits auto-response: `discover<T>(tag)` (method on the component) and `{tagName}-where-are-you`/`{tagName}-here-i-am` events. See [mixins.md](./mixins.md).
59
+ Every `SchmancyElement` inherits auto-response: `discover<T>(tag)` (method on the component) and `{tagName}-where-are-you`/`{tagName}-here-i-am` events. See [mixins.md](./mixins.md).
60
60
 
61
61
  ## When to Use
62
62
  - Cross-shadow coordination between unrelated components.
@@ -36,4 +36,4 @@ Auto-dismisses the dialog when clicked. Renders as a `schmancy-list-item` intern
36
36
  </schmancy-menu>
37
37
  ```
38
38
 
39
- Uses `$dialog.component()` internally. Menu items auto-dismiss the dialog. Custom components in the default slot must call `$dialog.dismiss()` manually.
39
+ Uses `show(overlay, { anchor })` from `@mhmo91/schmancy/overlay` internally. `<schmancy-menu-item>` auto-closes the menu by dispatching a `'close'` event on click. Custom components in the default slot dismiss the menu the same way: `this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))`.
@@ -40,7 +40,7 @@ Centered is the fallback (no anchor given). Sheet is the responsive adaptation (
40
40
 
41
41
  ## Subscription IS the overlay lifecycle
42
42
 
43
- Inside any `$LitElement`, pipe `takeUntil(this.disconnecting)`. When the caller unmounts, the overlay auto-dismisses — no handles to track, no leaks.
43
+ Inside any `SchmancyElement`, pipe `takeUntil(this.disconnecting)`. When the caller unmounts, the overlay auto-dismisses — no handles to track, no leaks.
44
44
 
45
45
  ```ts
46
46
  show(MyForm, { props: { id }, anchor: ev })