@keenmate/web-components-devkit 0.0.0-stage → 1.0.0-rc02

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/LICENSE +21 -0
  3. package/README.md +434 -2
  4. package/components.json +206 -0
  5. package/package.json +77 -4
  6. package/src/cli/args.js +16 -0
  7. package/src/cli/commands/check-release.js +66 -0
  8. package/src/cli/commands/check-vars.js +116 -0
  9. package/src/cli/commands/doctor.js +289 -0
  10. package/src/cli/commands/new.js +323 -0
  11. package/src/cli/commands/readme.js +111 -0
  12. package/src/cli/commands/sync.js +48 -0
  13. package/src/cli/kmwc.js +73 -0
  14. package/src/cli/sync-plan.js +163 -0
  15. package/src/config.d.ts +149 -0
  16. package/src/config.js +279 -0
  17. package/src/examples/auto.js +20 -0
  18. package/src/examples/chapter-nav.js +246 -0
  19. package/src/examples/constants.js +80 -0
  20. package/src/examples/controls-persistence.js +97 -0
  21. package/src/examples/copy-link.js +89 -0
  22. package/src/examples/css/base.css +215 -0
  23. package/src/examples/css/chapter-nav.css +179 -0
  24. package/src/examples/css/content.css +251 -0
  25. package/src/examples/css/controls.css +205 -0
  26. package/src/examples/css/copy-link.css +82 -0
  27. package/src/examples/css/demo.css +50 -0
  28. package/src/examples/css/index-page.css +118 -0
  29. package/src/examples/css/style.css +30 -0
  30. package/src/examples/css/theme-switch.css +38 -0
  31. package/src/examples/css/tokens.css +124 -0
  32. package/src/examples/css/widgets.css +133 -0
  33. package/src/examples/demo-code.js +30 -0
  34. package/src/examples/index-page.js +78 -0
  35. package/src/examples/index.d.ts +107 -0
  36. package/src/examples/index.js +18 -0
  37. package/src/examples/kit.js +22 -0
  38. package/src/examples/page-config.js +30 -0
  39. package/src/examples/sections.js +53 -0
  40. package/src/examples/theme-switch.js +72 -0
  41. package/src/examples/version-badge.js +23 -0
  42. package/src/playwright/index.d.ts +15 -0
  43. package/src/playwright/index.js +66 -0
  44. package/src/template.js +11 -0
  45. package/src/vite/index.d.ts +28 -0
  46. package/src/vite/index.js +243 -0
  47. package/src/vite/page-composer.js +216 -0
  48. package/templates/Dockerfile.tmpl +39 -0
  49. package/templates/Makefile.tmpl +136 -0
  50. package/templates/claude-block.md +40 -0
  51. package/templates/dockerignore.tmpl +10 -0
  52. package/templates/examples-layout.html +13 -0
  53. package/templates/new/.claude/commands/publish.md +226 -0
  54. package/templates/new/CHANGELOG.md +13 -0
  55. package/templates/new/LICENSE +21 -0
  56. package/templates/new/README.md +71 -0
  57. package/templates/new/_gitignore +7 -0
  58. package/templates/new/component-variables.manifest.json +32 -0
  59. package/templates/new/custom-elements-manifest.config.mjs +23 -0
  60. package/templates/new/docs/accessibility.md +24 -0
  61. package/templates/new/docs/examples.md +33 -0
  62. package/templates/new/docs/theming.md +54 -0
  63. package/templates/new/docs/usage.md +51 -0
  64. package/templates/new/e2e/dark-mode.spec.ts +30 -0
  65. package/templates/new/e2e/smoke.spec.ts +26 -0
  66. package/templates/new/examples-basic.html +57 -0
  67. package/templates/new/examples-theming.html +28 -0
  68. package/templates/new/index.html +16 -0
  69. package/templates/new/partials/note.html +5 -0
  70. package/templates/new/playwright.config.ts +4 -0
  71. package/templates/new/src/__FEATURE__.ts +55 -0
  72. package/templates/new/src/css/animations.css +1 -0
  73. package/templates/new/src/css/base.css +43 -0
  74. package/templates/new/src/css/controls.css +20 -0
  75. package/templates/new/src/css/dark-mode.css +36 -0
  76. package/templates/new/src/css/floating.css +1 -0
  77. package/templates/new/src/css/main.css +27 -0
  78. package/templates/new/src/css/states.css +22 -0
  79. package/templates/new/src/css/variables.css +42 -0
  80. package/templates/new/src/env.d.ts +7 -0
  81. package/templates/new/src/index.ts +17 -0
  82. package/templates/new/src/logger.ts +11 -0
  83. package/templates/new/src/types.ts +25 -0
  84. package/templates/new/src/web-component.ts +73 -0
  85. package/templates/new/test/dark-mode.html +18 -0
  86. package/templates/new/test/fixture.html +11 -0
  87. package/templates/new/tsconfig.json +15 -0
  88. package/templates/new/vite.config.ts +5 -0
  89. package/templates/new-minimal/CHANGELOG.md +13 -0
  90. package/templates/new-minimal/README.md +71 -0
  91. package/templates/new-minimal/component-variables.manifest.json +24 -0
  92. package/templates/new-minimal/docs/accessibility.md +20 -0
  93. package/templates/new-minimal/docs/examples.md +17 -0
  94. package/templates/new-minimal/docs/theming.md +53 -0
  95. package/templates/new-minimal/docs/usage.md +43 -0
  96. package/templates/new-minimal/e2e/smoke.spec.ts +10 -0
  97. package/templates/new-minimal/examples-basic.html +23 -0
  98. package/templates/new-minimal/examples-theming.html +28 -0
  99. package/templates/new-minimal/src/__FEATURE__.ts +37 -0
  100. package/templates/new-minimal/src/css/base.css +48 -0
  101. package/templates/new-minimal/src/css/controls.css +1 -0
  102. package/templates/new-minimal/src/css/states.css +1 -0
  103. package/templates/new-minimal/src/css/variables.css +28 -0
  104. package/templates/new-minimal/src/index.ts +17 -0
  105. package/templates/new-minimal/src/types.ts +10 -0
  106. package/templates/new-minimal/src/web-component.ts +48 -0
  107. package/templates/new-minimal/test/dark-mode.html +18 -0
  108. package/templates/new-minimal/test/fixture.html +11 -0
  109. package/templates/nginx.conf.tmpl +43 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,126 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0-rc02] - 2026-10-31 [PUBLISHED]
4
+
5
+ ### Added
6
+
7
+ - **`kmwc new --adopt`** for migrating a component that is already in the registry:
8
+ the same scaffold, with the entry's port block and prefix (or `--prefix` when the
9
+ entry has none), no npm-name check (the package may exist) and no registry edit.
10
+ Scaffold into a scratch `--dir`, then move the component's code into it — how
11
+ web-editor's one-package monorepo was flattened.
12
+
13
+ - **`npm run check:contract`** — `base-contract-check` (from `@keenmate/base-css-variables`,
14
+ a devDependency) over the kit, the scaffold templates and the playground: every `--base-*`
15
+ read or set exists in the contract, input fallbacks equal its defaults, derived ones are its
16
+ formulas. Deliberate differences go in `contract-check.json` (the scaffolds' `inherit` font).
17
+
18
+ ### Fixed
19
+
20
+ - **Kit and scaffolds read the 2.0 `--base-*` names.** `--base-accent-color*`,
21
+ `--base-text-color-on-accent` and `--base-input-color` were removed in base-css-variables
22
+ 2.0.0, so a theme's primary never reached the example pages or a new component. Now
23
+ `--base-primary-color` / `-hover` / `-active`, `--base-primary-bg-subtle(-hover)`,
24
+ `--base-on-primary-text-color` and `--base-input-text-color`; the theming pages and docs set
25
+ `--base-primary-color`. Fallbacks are the contract's: example pages default to the blue-500
26
+ primary (was purple `#667eea`; the header gradient end now follows the accent), slate surfaces
27
+ (dark main `#1e293b`, page `#f8f9fa` / `#0f172a`) and the contract text, border, hover, shadow
28
+ and font values. The kit's status colours read `--base-<role>-text-color`; the danger button
29
+ fills with `--base-danger-color` and its text is `--base-on-danger-text-color`. The secondary
30
+ button and the index "new" badge compute their text from their own fill, so a light primary
31
+ no longer turns them black.
32
+ - **`kmwc doctor` skipped every rulebook suite once the devkit came from npm.** The
33
+ rulebook was looked up at a fixed path from the devkit (`<devkit>/../../BlissFramework`),
34
+ which lands inside `node_modules` for an installed package. It is now the nearest
35
+ `BlissFramework/guidelines/web-components` above the component (then above the
36
+ devkit); `kmwc.rulebook.path` resolves against the component root.
37
+ - **Fragment pages: a `<style>` or `<link>` inside a `<script>` stays put.** Shadow-DOM
38
+ styles and an SVG's own `<style>` written into a script were moved into `<head>`; they
39
+ are strings there, not page elements.
40
+
41
+ ### Migrated
42
+
43
+ - **web-editor** — flattened from a one-package npm-workspaces monorepo; registry entry
44
+ now carries its prefix (`km`) and no longer says monorepo.
45
+ - **pure-css** (library, 15200) and **base-css-variables** (library, 15300) added to the
46
+ registry.
47
+
48
+ ### Internal
49
+
50
+ - **Root `Makefile`** for the devkit's own tasks: test, readme / contract checks, and
51
+ `publish` / `publish-rc` gated on `kmwc check-release`.
52
+
53
+ ## [1.0.0-rc01] - 2026-10-12 [PUBLISHED]
54
+
55
+ First release of `@keenmate/web-components-devkit`, plus the component catalogue.
56
+
57
+ ### Added
58
+
59
+ - **Catalogue**: `components.json` registry (ids, CSS prefixes, port blocks, links);
60
+ the README tables (components, libraries, showcases/tests — with each repo's
61
+ dev port, no versions/status that would go stale) are generated by
62
+ `npm run readme`. Port blocks of 100 per repo (dev = block, preview = +1,
63
+ examples image = +10), numbered by 100 inside a range per kind with headroom —
64
+ components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900,
65
+ tests 17000–17900 — so repos run side by side without collisions and new
66
+ components land next to the existing ones.
67
+ - **Vite preset** `defineComponentConfig()` (Vite 7): dev server on a strict
68
+ registry port, library build (es + umd + `style.css` + rolled-up d.ts), and
69
+ `--mode examples` multi-page static build with relative asset URLs.
70
+ - **Examples kit**, injected into every root page: `@layer kmwc` design system
71
+ merged from six diverged `examples-shared.css` copies, with `--kmwc-*` tokens
72
+ read through the rulebook's `--base-*` taxonomy (`light-dark()` fallbacks) and
73
+ a light / dark / auto theme switch that drives `data-theme` on `<html>`;
74
+ kit-generated DOM is `kmwc-` prefixed BEM. Chapter nav (bundled Floating UI,
75
+ shared slug logic, last-chapter highlight at page bottom), copy-link, version
76
+ badge with build stamp, build-only Plausible. Helpers:
77
+ `attachControlsPersistence`, `renderExamplesIndex` (its footer links the
78
+ devkit version that built the site), `mountExamplesKit`.
79
+ - **Playwright preset** `definePlaywrightConfig()`.
80
+ - **CLI** `kmwc`: `sync` (Dockerfile, nginx.conf with immutable `/assets/`
81
+ caching, Makefile + `Makefile.local`, .dockerignore, CLAUDE.md block, scripts),
82
+ `doctor` (devkit checks + the BlissFramework rulebook's `*.checks.sh` suites),
83
+ `check-vars` (declared/consumed modes; replaces three script copies),
84
+ `check-release` (dist-tag ⇔ version, not yet on npm — gates `make publish*`),
85
+ `new` (rulebook-shaped scaffold with intake: npm availability, reserved-prefix
86
+ check, next free block in the component port range — a taken or misaligned
87
+ `--port` is refused), `readme`.
88
+ - **Fragment pages**: a page without `<html>` gets the devkit layout (head, entry
89
+ script, header, container) from `<meta name="kmwc:…">` tags; `<kmwc-include>` pulls
90
+ shared partials (parameters, nesting, cycle detection); `<kmwc-demo>` renders live
91
+ markup plus a code block of exactly that markup (and `<script data-kmwc-show>`), so
92
+ snippets can't drift from demos. `kmwc doctor` lists pages still repeating the boilerplate.
93
+ - **Scaffold** (`kmwc new`): Element / Logic / types / logger layers, the
94
+ canonical eight-file CSS set, Strategy-B dark mode, Custom Elements Manifest,
95
+ README + `docs/`, CHANGELOG, LICENSE, `.claude/commands/publish.md`, examples,
96
+ smoke + dark-mode e2e specs — example pages are fragments. `--minimal` gives an
97
+ empty skeleton instead of the sample toggle. A fresh scaffold passes every rulebook suite.
98
+ - **Playground** (`playground/`): a component scaffolded with `kmwc new`, linked to
99
+ the devkit and registered in the catalogue (port 17300) — the e2e test bed for
100
+ the kit, the presets and the scaffold, in dev and in the built site.
101
+
102
+ - **Package-contents check** in `kmwc doctor`: `npm pack --dry-run` must not ship
103
+ `CLAUDE.md` / `.claude/` (at any depth), tests and specs, example pages,
104
+ partials, build/test output or local make config (`--no-pack` skips it).
105
+ `kmwc new` ends `files` with `!**/CLAUDE.md` and `!**/.claude`.
106
+
107
+ - **`/publish` command** (`.claude/commands/publish.md`) for the devkit itself, in
108
+ the rulebook's canonical shape (passes `publish-command.checks.sh`): gates are the
109
+ unit tests, the playground e2e (dev + built), the playground doctor, `sync --check`
110
+ and `readme:check`; "build" is an entry-point smoke test, since `src/` ships as-is.
111
+
112
+ ### Fixed
113
+
114
+ - **Flash of unstyled page on every reload in dev** (FOUC): the kit stylesheet was
115
+ imported from the kit's module script, which Vite dev applies only after the script
116
+ runs. The plugin now loads it render-blocking: a `<link>` in `<head>` in dev, a CSS
117
+ import (extracted to a `<head>` `<link>`) in builds. `auto.js` no longer imports
118
+ it. An e2e spec loads a page with JavaScript off and checks it is styled.
119
+ - **Fragment detection** (`checkIsFragment`): a page is a full document only when it
120
+ *starts* with `<!DOCTYPE` / `<html>` (after a BOM, whitespace or comments). A fragment
121
+ whose script or code sample mentions `<html>` was passed through without the layout
122
+ (found migrating web-commandpalette's playground page).
123
+
124
+ ### Migrated
125
+
126
+ - **web-commandpalette** (1.0.0-rc02) — the first component on the devkit.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Keenmate s.r.o.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,435 @@
1
- # Temporary Holding Version
1
+ # Keenmate Web Components
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The index of Keenmate's web components **and** the shared tooling that keeps
4
+ them in the same shape: `@keenmate/web-components-devkit`.
5
+
6
+ - **[Catalogue](#catalogue)**: every component, library, showcase and test
7
+ harness, with CSS prefixes, dev ports and links. It's
8
+ generated from [`components.json`](components.json).
9
+ - **[Devkit](#the-devkit)**: one Vite preset, one examples kit, one Playwright
10
+ preset and the `kmwc` CLI, instead of a diverged copy of each in every repo.
11
+
12
+ > **The rules** — component structure, naming, CSS, theming, README, Custom
13
+ > Elements Manifest, publishing — live in the BlissFramework rulebook
14
+ > (`BlissFramework/guidelines/web-components/`, plus the JS coding guidelines
15
+ > in `BlissFramework/web/docs/coding-guidelines-javascript/`). The devkit
16
+ > **generates** what the rulebook asks for (`kmwc new`) and **checks** it
17
+ > (`kmwc doctor` runs the rulebook's `*.checks.sh` suites). Runtime code shared
18
+ > between components (BlissElement, the input table, converters, logging,
19
+ > positioning) lives in [`@keenmate/web-components-core`](../web-components-core).
20
+ > Nothing here ships inside a component's bundle.
21
+
22
+ ## What's New in v1.0.0-rc02
23
+
24
+ - **Theming — kit and scaffolds follow base-css-variables 2.0** — base-css-variables 2.0 dropped `--base-accent-color*`, `--base-text-color-on-accent` and `--base-input-color`, so a theme's primary colour silently stopped reaching the example pages and every newly scaffolded component. The `--kmwc-*` tokens in `src/examples/css/tokens.css` and the `templates/new*` `variables.css` now read `--base-primary-color` (with `-hover` / `-active` / `-bg-subtle`), `--base-on-primary-text-color` and `--base-input-text-color`, and the theming pages, docs and manifests set the new names. Standalone fallbacks are now the contract's own values, so a page looks the same with or without the contract loaded: a blue-500 primary instead of the old purple, slate surfaces, and on-colours computed from their fill. Re-run `kmwc new` output or diff your `variables.css` if you scaffolded on rc01.
25
+ - **`npm run check:contract` — fallbacks can't drift from the contract** — `base-contract-check` from `@keenmate/base-css-variables` now runs over the kit, the scaffold templates and the playground. It fails when a `--base-*` read doesn't exist in the contract, when an input's fallback differs from the contract default, or when a derived token's fallback isn't the contract formula. Deliberate differences, such as the scaffolds inheriting the page font, are listed with a reason in `contract-check.json`. It's part of the gate before every kit or template change.
26
+ - **`kmwc new --adopt` — migrate a component that's already registered** — Brings an existing repo onto the devkit with the same rulebook-shaped scaffold, using the registry entry's port block and CSS prefix, skipping the npm-name check (the package already exists) and leaving `components.json` untouched. Scaffold into a scratch `--dir` and move the component's code in. web-editor's one-package monorepo was flattened this way.
27
+ - **`kmwc doctor` — rulebook suites run from an npm install** — With the devkit installed from npm instead of linked, doctor looked for the BlissFramework rulebook at a path relative to the devkit, which ended up inside `node_modules`, and silently skipped every suite. It now uses the nearest `BlissFramework/guidelines/web-components` above the component (then above the devkit), and `kmwc.rulebook.path` resolves against the component root.
28
+
29
+ ## What's New in v1.0.0-rc01
30
+
31
+ - **Devkit — one shared toolchain for every Keenmate web component** — Each `web-*` repo used to carry its own copy of the example helpers, the shared stylesheet, the Vite/Playwright configs and the deploy files, and every session "fixed" its own copy until they drifted apart. `@keenmate/web-components-devkit` owns all of it now: a component describes itself once in `package.json#kmwc` (title, prefix, ports, accent colours, analytics, image) and `defineComponentConfig()`, `definePlaywrightConfig()` and the `kmwc` CLI read it from there. A shared concern is fixed once in the devkit and every component picks it up on the next install.
32
+ - **Vite preset — dev, library and examples-site builds from one config** — `defineComponentConfig()` (Vite 7) runs the dev server on the repo's registry port with `strictPort`, builds the library (`es` + `umd`, `style.css` kept under its historical name, rolled-up `.d.ts`) and, with `--mode examples`, builds every root `*.html` into a static multi-page site in `dist-examples/` with relative asset URLs. The examples plugin injects the kit into each page and loads its stylesheet render-blocking, so pages don't flash unstyled on reload.
33
+ - **Examples kit — one design system and set of page helpers instead of six forks** — The `@layer kmwc` stylesheet merges six diverged `examples-shared.css` copies; its `--kmwc-*` tokens read the rulebook's `--base-*` taxonomy with `light-dark()` fallbacks, so a theme-designer theme restyles the demo page together with the component. Every page gets a light / dark / auto theme switch (it sets `data-theme` on `<html>`, the signal components react to), a chapter nav and copy-link buttons that share one slug algorithm, a version badge with the build stamp, and build-only Plausible. `attachControlsPersistence()` and `renderExamplesIndex()` replace the per-repo controls store and the 150-line inline index page.
34
+ - **Fragment pages — no more copied page boilerplate** — A page without `<html>` is a fragment: `<meta name="kmwc:title">` and friends drive the shared layout (head, entry script, header with back link, container), `<kmwc-include>` pulls parameterised partials (nesting, cycle detection), and `<kmwc-demo>` renders live markup plus a code block of exactly that markup, so a snippet can no longer drift from its demo. Full documents pass through untouched, so pages migrate one at a time.
35
+ - **`kmwc` CLI — generate, check and gate instead of hand-edit** — `kmwc sync` writes the Dockerfile, nginx.conf, Makefile (repo targets in `Makefile.local`), .dockerignore and a managed CLAUDE.md block from templates. `kmwc doctor` runs the devkit checks, an `npm pack` contents check (no CLAUDE.md, tests or example pages in the package) and every BlissFramework rulebook `*.checks.sh` suite. `kmwc check-vars` replaces three diverged manifest scripts (declared or consumed mode), and `kmwc check-release` gates `make publish*` on the dist-tag ⇔ version rule.
36
+ - **`kmwc new` — a rulebook-shaped component on the first commit** — The scaffold runs the rulebook intake (npm name free, CSS prefix 2–4 letters and not reserved, next free port block) and generates the Element / Logic / types / logger layers, the canonical eight-file CSS set with Strategy-B dark mode, a Custom Elements Manifest, README + `docs/`, CHANGELOG, `/publish`, fragment example pages and smoke + dark-mode e2e specs. It passes every rulebook suite as generated; `--minimal` gives an empty skeleton instead of the sample toggle.
37
+ - **Catalogue and port registry — every component listed, no port collisions** — `components.json` registers every Keenmate repo with its CSS prefix and a 100-port block (dev = block, preview = +1, image = +10), numbered inside a range per kind (components 12100–14900, libraries 15000–, showcases 16000–, tests 17000–), so any number of repos run side by side. The README catalogue is generated from it, with a ✅ for each repo on the devkit; web-commandpalette is the first.
38
+
39
+ ## Catalogue
40
+
41
+ <!-- kmwc:catalogue:start — generated by `npm run readme`, do not edit inside -->
42
+ ### Components
43
+
44
+ | Component | Package | Prefix | Dev port | Links | Devkit | About |
45
+ |---|---|---|---:|---|:---:|---|
46
+ | **web-multiselect** | `@keenmate/web-multiselect` | `ms` | 12100 | [repo](https://github.com/keenmate/web-multiselect) · [examples](https://examples.web-multiselect.keenmate.dev) · [docs](https://web-multiselect.keenmate.dev) · `web-multiselect-showcase` · `web-multiselect-docs` | — | Lightweight multiselect web component with typeahead search, rich content support, and excellent keyboard… |
47
+ | **web-daterangepicker** | `@keenmate/web-daterangepicker` | `drp` | 12200 | [repo](https://github.com/keenmate/web-daterangepicker) · [docs](https://web-daterangepicker.keenmate.dev) · `web-daterangepicker-showcase` | — | Lightweight date picker web component with excellent keyboard navigation and range selection |
48
+ | **web-treeview** | `@keenmate/web-treeview` | `wtv` | 12300 | [repo](https://github.com/keenmate/web-treeview) · `web-treeview-showcase` | — | Lightweight treeview web component with hierarchical data display, search, expand/collapse, drag-drop, and… |
49
+ | **web-commandpalette** | `@keenmate/web-commandpalette` | `cp` | 12400 | [repo](https://github.com/keenmate/web-commandpalette) · [examples](https://examples.web-commandpalette.keenmate.dev) | ✅ | Framework-agnostic command-palette web component — modes, multi-step commands, scoped + global search,… |
50
+ | **web-dropzone** | `@keenmate/web-dropzone` | `dz` | 12500 | [repo](https://github.com/keenmate/web-dropzone) | — | Lightweight file dropzone web component with drag-drop, previews, validation, and multiple display modes. |
51
+ | **web-grid** | `@keenmate/web-grid` | `wg` | 12600 | [repo](https://github.com/keenmate/web-grid) · [docs](https://web-grid.keenmate.com) · [showcase](https://web-grid.keenmate.com) | — | Framework-agnostic data grid web component with sorting, filtering, inline editing, and keyboard navigation |
52
+ | **web-editor** | `@keenmate/web-editor` | `km` | 12700 | [examples](https://examples.web-editor.keenmate.dev) | ✅ | Keenmate Web Editor — a framework-agnostic WYSIWYG / Markdown editor built on ProseMirror. |
53
+ | **web-switch** | `@keenmate/web-switch` | — | 12800 | [repo](https://github.com/keenmate/web-switch) | — | Framework-agnostic web component wrapper for @keenmate/svelte-switch. Works with React, Vue, Angular, or… |
54
+ | **web-timepicker** | — | — | 12900 | — | — | Vanilla-JS prototypes (spiral / slider time pickers); no package yet. |
55
+ | **web-wysiwyg** | — | — | 13000 | — | — | ProseMirror exploration; superseded by web-editor. |
56
+
57
+ ### Shared libraries
58
+
59
+ | Library | Package | Dev port | About |
60
+ |---|---|---:|---|
61
+ | **web-components-core** | `@keenmate/web-components-core` | 15000 | Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset. |
62
+ | **web-components** | `@keenmate/web-components-devkit` | 15100 | This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI). |
63
+ | **pure-css** | `@keenmate/pure-css` | 15200 | CSS framework mirroring the base-css-variables token contract (demo site + e2e on the dev port). |
64
+ | **base-css-variables** | `@keenmate/base-css-variables` | 15300 | The shared --base-* token contract (base-variables.css) + base-contract-check; examples site + e2e on the dev port. |
65
+
66
+ ### Showcases, docs & test harnesses
67
+
68
+ | Repo | Kind | For | Dev port | About |
69
+ |---|---|---|---:|---|
70
+ | web-multiselect-showcase | showcase | web-multiselect | 16000 | |
71
+ | web-daterangepicker-showcase | showcase | web-daterangepicker | 16100 | |
72
+ | web-treeview-showcase | showcase | web-treeview | 16200 | |
73
+ | web-grid-showcase | showcase | web-grid | 16300 | [https://web-grid.keenmate.com](https://web-grid.keenmate.com) |
74
+ | web-multiselect-docs | docs | web-multiselect | 16400 | Content-only docs (keendocs). |
75
+ | web-component-framework-test | test | — | 17000 | Smoke + contrast tests inside CSS host frameworks (plain / Bootstrap / …). |
76
+ | web-multiselect-test | test | web-multiselect | 17100 | npm-install vs direct-file consumption checks before publishing. |
77
+ | web-multiselect-positioning-demo | test | web-multiselect | 17200 | Single-page positioning repro. |
78
+ | web-playground | test | web-components | 17300 | Devkit e2e test bed — a component scaffolded with `kmwc new`, linked to this repo. |
79
+
80
+ **Ports** — a block of 100 per repo: dev server = **Dev port**, preview = +1, examples image = +10. Each kind numbers its blocks by 100 inside its own range (component 12100–14900 · library 15000–15900 · showcase & docs 16000–16900 · test 17000–17900), so any number of repos run side by side without collisions and a new component lands next to the others. `kmwc doctor` checks a repo against its block; `kmwc new` takes the next free one.
81
+ <!-- kmwc:catalogue:end -->
82
+
83
+ To add or change a row, edit [`components.json`](components.json) and run
84
+ `npm run readme`. Descriptions and the Devkit ✅ are read from the
85
+ sibling checkouts next to this repo. `npm run readme:check` fails when the
86
+ table is stale. Prefixes mirror the reserved-prefix table in the rulebook's
87
+ `base-variables.md`, which stays the authority.
88
+
89
+ ---
90
+
91
+ ## The devkit
92
+
93
+ ### Why
94
+
95
+ Before the devkit, every component repo carried its own copy of
96
+ `examples-shared.css` (six copies, six different versions),
97
+ `examples-chapter-nav.js` (three versions), `examples-copy-link.js`,
98
+ `examples-controls-store.js`, `plausible-snippet.html`,
99
+ `check-variable-manifest.mjs`, a `vite.config.ts`, a `Dockerfile`, an
100
+ `nginx.conf` and a `Makefile`. A fix made in one repo never reached the others.
101
+ web-treeview forked the whole 980-line stylesheet just to change purple to
102
+ green. Two components shared a dev port and three shared an image port.
103
+
104
+ The devkit owns all of that. A component states what's specific to it **once**,
105
+ in `package.json#kmwc`, and gets the rest.
106
+
107
+ ### What a component gets
108
+
109
+ | Piece | Import / command | Replaces |
110
+ |---|---|---|
111
+ | Vite preset | `@keenmate/web-components-devkit/vite` → `defineComponentConfig()` | per-repo `vite.config.ts`, `vite.examples.config.ts` |
112
+ | Examples kit (auto-injected) | shared styles, chapter nav, copy-link buttons, theme switch, version badge, analytics | `examples-shared.css`, `examples-chapter-nav.js`, `examples-copy-link.js`, `plausible-snippet.html`, Dockerfile `sed` |
113
+ | Fragment pages | `<meta name="kmwc:title">` pages + `<kmwc-include>` partials + `<kmwc-demo>` (live markup = snippet) | the head / header / container boilerplate copied into every page, hand-copied `.code-block` snippets |
114
+ | Examples helpers | `@keenmate/web-components-devkit/examples` → `attachControlsPersistence`, `renderExamplesIndex`, `mountExamplesKit` | `examples-controls-store.js`, 150 lines of inline index-page CSS |
115
+ | Playwright preset | `@keenmate/web-components-devkit/playwright` → `definePlaywrightConfig()` | per-repo `playwright.config.ts` |
116
+ | Generated files | `kmwc sync` | hand-edited `Dockerfile`, `nginx.conf`, `Makefile`, `.dockerignore` |
117
+ | Standard check | `kmwc doctor` (devkit checks + the rulebook's `*.checks.sh`) | — |
118
+ | Manifest check | `kmwc check-vars` | 3 diverged `check-variable-manifest.mjs` |
119
+ | Release gate | `kmwc check-release` (used by `make publish` / `publish-rc`) | — |
120
+ | Scaffolder | `kmwc new web-<name> --prefix <p>` | copy-pasting the last repo |
121
+
122
+ ### Install
123
+
124
+ ```
125
+ npm i -D @keenmate/web-components-devkit vite@^7
126
+ ```
127
+
128
+ To develop against a local checkout of this repo, keep the semver range in
129
+ `package.json` (Docker builds install from the registry) and link it:
130
+
131
+ ```
132
+ cd ../web-components && npm link
133
+ cd ../web-<component> && npm link @keenmate/web-components-devkit
134
+ ```
135
+
136
+ Peer dependencies (`vite`, `@playwright/test`) are always resolved from the
137
+ **component's** `node_modules`, so a linked devkit doesn't load a second copy.
138
+
139
+ ### `package.json#kmwc`
140
+
141
+ Every field is optional. Defaults are derived from the package.
142
+
143
+ ```jsonc
144
+ "kmwc": {
145
+ "title": "Web MultiSelect", // examples index header
146
+ "tagline": "Feature-rich multiselect …", // default: package.json description
147
+ "tag": "web-multiselect",
148
+ "prefix": "ms", // reserved CSS prefix (rulebook base-variables.md)
149
+ "library": {
150
+ "entry": "src/index.ts", // default
151
+ "globalName": "MultiSelect", // UMD global
152
+ "fileName": "multiselect", // → dist/multiselect.js + .umd.js
153
+ "cssFileName": "style", // default; keeps dist/style.css on Vite 7
154
+ "shouldEmitTypes": true, // rolled-up .d.ts (typesPluginConfig to tune)
155
+ "shouldEmitSourcemap": false,
156
+ "external": []
157
+ },
158
+ "ports": { "dev": 12100, "image": 12110 }, // the repo's components.json port block (+10 = image)
159
+ "theme": { "accentColor": "#667eea", "accentEndColor": "#764ba2" },
160
+ "examples": {
161
+ "canonicalOrigin": "https://examples.web-multiselect.keenmate.dev",
162
+ "staticPaths": ["examples-data", "docs"], // copied verbatim into the built site
163
+ "excludedPages": [], // root *.html that aren't example pages
164
+ "outDir": "dist-examples"
165
+ },
166
+ "analytics": { "plausibleSiteId": "pa-5vUESDVyztHMyzN5gs5A4" },
167
+ "image": {
168
+ "name": "registry.km8.es/web-multiselect-examples:prod",
169
+ "runner": "podman",
170
+ "packageJsonPaths": ["package.json"] // monorepos: list workspace package.jsons too
171
+ },
172
+ "cssVariables": {
173
+ "sources": ["src/css"], // dirs (stylesheets) or explicit files (any type)
174
+ "internalVariables": ["ms-input-current-width"], // runtime internals, not theming knobs
175
+ "checkMode": "declared" // or "consumed" (var() reads)
176
+ },
177
+ "rulebook": {
178
+ "path": "../BlissFramework/guidelines/web-components", // relative to the component; default: the nearest
179
+ // BlissFramework checkout above it. env KMWC_RULEBOOK_PATH wins
180
+ "skippedChecks": [] // e.g. ["responsive-overlay"]
181
+ },
182
+ "sync": { "skippedFiles": [] } // e.g. ["Makefile"] to opt a file out
183
+ }
184
+ ```
185
+
186
+ ### Vite preset
187
+
188
+ ```ts
189
+ // vite.config.ts
190
+ import { defineComponentConfig } from '@keenmate/web-components-devkit/vite';
191
+ export default defineComponentConfig();
192
+
193
+ // with repo-specific bits
194
+ export default defineComponentConfig({
195
+ plugins: [shadowCssPlugin()],
196
+ overrides: { optimizeDeps: { entries: ['index.html', 'examples-*.html'] } },
197
+ getOverridesCallback: (env, config) => ({ /* env-dependent bits */ }),
198
+ });
199
+ ```
200
+
201
+ | Command | Result |
202
+ |---|---|
203
+ | `vite` | dev server on `kmwc.ports.dev` (strict, never hops ports) |
204
+ | `vite build` | library: `dist/<fileName>.js`, `.umd.js`, `style.css`, rolled-up `.d.ts` |
205
+ | `vite build --mode examples` | static examples site → `dist-examples/` (every root `*.html` is a page) |
206
+ | `vite preview --mode examples` | serves the built site on the preview port (`ports.dev + 1`) |
207
+
208
+ The preset defines `__VERSION__`, `__PACKAGE_NAME__`, `__AUTHOR__`,
209
+ `__LICENSE__`, `__REPOSITORY__` and `__HOMEPAGE__`. For a hand-assembled config
210
+ (monorepos, showcases), use the plugin on its own: `createExamplesPlugin()`.
211
+
212
+ ### Examples kit
213
+
214
+ The plugin injects the kit into every **root-level** `*.html` page. Pages under
215
+ `test/` (Playwright fixtures) are left alone, and any page can opt out with
216
+ `<html data-kmwc="off">`.
217
+
218
+ #### Fragment pages — no copied boilerplate
219
+
220
+ A page without `<html>` is a **fragment**: it carries only its own content,
221
+ and the plugin wraps it in the devkit layout (head, entry script, header with
222
+ back link / title / subtitle, container). The whole file:
223
+
224
+ ```html
225
+ <meta name="kmwc:title" content="Tree of Options">
226
+ <meta name="kmwc:subtitle" content="Hierarchical options via a materialized path-member…">
227
+ <link rel="stylesheet" href="examples-local.css"> <!-- moved into <head> -->
228
+
229
+ <kmwc-include src="partials/no-collapse-note.html" title="No collapse"></kmwc-include>
230
+
231
+ <div class="card"> <!-- each h2 = a chapter -->
232
+ <h2>TR01 · Basic tree</h2>
233
+ <kmwc-demo>
234
+ <web-multiselect id="tree" path-member="path"></web-multiselect>
235
+ <script type="module" data-kmwc-show>
236
+ document.getElementById('tree').options = [{ value: 'fruit', label: 'Fruit', path: '1' }];
237
+ </script>
238
+ </kmwc-demo>
239
+ </div>
240
+
241
+ <script type="module">
242
+ import { attachControlsPersistence } from '@keenmate/web-components-devkit/examples';
243
+ attachControlsPersistence('tree');
244
+ </script>
245
+ ```
246
+
247
+ | Construct | Does |
248
+ |---|---|
249
+ | `<meta name="kmwc:title">` (required), `kmwc:subtitle`, `kmwc:body-class` | header + `<title>`, body classes |
250
+ | `<meta name="kmwc:layout" content="bare">` | head only, no header/container (the index page) |
251
+ | `<meta name="kmwc:entry" content="none">` | skip the component entry script (default `/<kmwc.library.entry>`, loaded in `<head>` so the element is defined before page scripts run) |
252
+ | `<link>` / `<style>` anywhere in a fragment | moved into `<head>` |
253
+ | `<kmwc-include src="partials/x.html" some-param="v">inner</kmwc-include>` | the partial with `{{someParam}}` and `{{content}}` filled in; nests; cycles are an error; unknown `{{…}}` stay as written |
254
+ | `<kmwc-demo>markup</kmwc-demo>` | the markup live **and** as a code block of exactly that markup (copy button included) — a snippet can't drift from its demo. `<script data-kmwc-show>` inside runs and is shown too. Attributes: `preview-class`, `markup-hidden`, `code-hidden` |
255
+
256
+ Tags inside HTML comments are never expanded. Partials live in the repo's
257
+ `partials/` folder (a change reloads the dev server page); shared demo data
258
+ belongs in `examples-data/*.js` modules imported by pages. Full documents
259
+ (`<html>…`) pass through untouched, so pages migrate one at a time — `kmwc
260
+ doctor` lists the ones still repeating the head/header boilerplate.
261
+
262
+ - **Styles** live in `@layer kmwc`, so any unlayered rule beats them
263
+ regardless of order (and the layered reset can never clobber a component).
264
+ Tokens (`--kmwc-*`) read through the rulebook's `--base-*` taxonomy with
265
+ `light-dark()` fallbacks, so a theme-designer theme restyles the demo page
266
+ together with the component; colour literals live only in `tokens.css`.
267
+ Re-theme a site with `kmwc.theme.accentColor`. The page vocabulary is
268
+ unchanged from the old shared CSS — `.card`, `.example-section`, `.controls`,
269
+ `.demo-area`, `.demo-card`, `.code-block`, `.note(.warning|.info|.success|.error)`,
270
+ `.grid`/`.card-grid`, `.reference-table`, `.log-panel`, `.pill`,
271
+ `.status-dot`, `.control-group`, buttons (`.btn-secondary`, `.btn-outline`,
272
+ `.btn-danger`) — while everything the kit **generates** is prefixed BEM
273
+ (`kmwc-chapter-nav__item--active`, `kmwc-copy-link--copied`, …).
274
+ - **Theme switch** (top right) cycles light → dark → auto and sets
275
+ `data-theme` on `<html>` — the same signal components react to via
276
+ `:host-context([data-theme])`, so every example page is a dark-mode smoke
277
+ test. The saved choice is applied before first paint.
278
+ - **Chapter nav and copy-link** share one section/slug algorithm, so the
279
+ jump-list hashes and copied links always match. Floating UI is bundled (no
280
+ CDN). At the bottom of a short page, the last chapter gets highlighted.
281
+ - **Version badge**: any `[data-kmwc-version]` (or legacy `#version-badge`)
282
+ shows `v1.2.3`, or `v1.2.3 (built …)` in a built site.
283
+ - **Landing page**: `renderExamplesIndex({ pages })` renders the header, cards,
284
+ footer and cross-page nav from the registry plus `kmwc` config.
285
+ - **Late content**: the kit mounts one task after DOM-ready. Pages that render
286
+ sections after a fetch call `mountExamplesKit()`.
287
+ - **Analytics**: the Plausible snippet goes into **built** pages only, and only
288
+ when `kmwc.analytics.plausibleSiteId` is set.
289
+
290
+ ### Playwright preset
291
+
292
+ ```ts
293
+ import { definePlaywrightConfig, devices } from '@keenmate/web-components-devkit/playwright';
294
+ export default definePlaywrightConfig({
295
+ projects: [
296
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, testIgnore: ['**/mobile.spec.ts'] },
297
+ { name: 'mobile', use: { ...devices['Pixel 7'] }, testMatch: ['**/mobile.spec.ts'] },
298
+ ],
299
+ });
300
+ ```
301
+
302
+ baseURL and the `npm run dev` web server come from `kmwc.ports.dev`.
303
+
304
+ ### CLI
305
+
306
+ ```
307
+ kmwc sync [--check] regenerate Dockerfile, nginx.conf, Makefile, .dockerignore, the
308
+ CLAUDE.md devkit block, .gitignore entries, missing standard scripts
309
+ kmwc doctor [--json] devkit checks + package contents + the rulebook's *.checks.sh suites
310
+ (exit 1 on errors). --verbose lists passes, --no-rulebook skips the
311
+ suites, --no-pack skips the package-contents check
312
+ kmwc check-vars [--strict] component-variables.manifest.json ⇔ CSS
313
+ kmwc check-release --tag t latest ⇔ release version, rc ⇔ -rcN, version not yet on npm
314
+ kmwc new web-<name> scaffold a component (--prefix, --title, --tag, --dir, --port, --minimal, --register)
315
+ --adopt: scaffold an ALREADY registered component (its port/prefix) to migrate into
316
+ kmwc readme [--check] regenerate this README's catalogue (devkit repo only)
317
+ ```
318
+
319
+ Generated files start with a `GENERATED by kmwc sync` header. Repo-specific
320
+ make targets go in `Makefile.local`, which the generated Makefile includes at
321
+ the end. Per-developer overrides (`DOCKER_RUNNER`, `IMAGE_PORT`) go in
322
+ `.makefile.env`. `make publish` / `make publish-rc` run `kmwc check-release`
323
+ and the e2e suite before building and publishing.
324
+
325
+ **`kmwc doctor` and the rulebook.** Doctor runs every suite in the rulebook
326
+ (component-structure, naming-conventions, css-structure, theme-container,
327
+ base-variables, color-scheme, readme-structure, custom-elements-manifest,
328
+ responsive-overlay, publish-command) with `bash`, passing `kmwc.prefix`, and
329
+ reports each `FAIL` as an error pointing at the matching `.checks.md` entry.
330
+ When the rulebook isn't checked out (or there's no bash) the suites are
331
+ skipped with a warning — the rulebook stays the single source of truth; the
332
+ devkit never copies its rules.
333
+
334
+ **Package contents.** Doctor runs `npm pack --dry-run` and fails on anything
335
+ dev-only that would reach a consumer's `node_modules`: `CLAUDE.md` at any
336
+ depth and `.claude/` (AI-assistant guidance, which a directory entry such as
337
+ `src/css` in `files` sweeps in), `test/` / `e2e/` / `*.spec.*` / `*.test.*`,
338
+ example pages and `partials/`, build/test output, and local make config. Keep
339
+ `"!**/CLAUDE.md"` and `"!**/.claude"` at the end of `files`; the scaffold adds them.
340
+
341
+ ### Scaffolding a component
342
+
343
+ ```
344
+ npx kmwc new web-colorpicker --prefix cpk --title "Web Color Picker" --register
345
+ ```
346
+
347
+ `kmwc new` runs the rulebook intake: the npm name must be free and the CSS
348
+ prefix must be 2–4 lowercase letters not reserved in `base-variables.md` or
349
+ `components.json` (it's derived only when unambiguous — hyphenated words, or a
350
+ short single word — otherwise pass `--prefix`). The result passes every
351
+ rulebook suite on its first commit:
352
+
353
+ - `src/web-component.ts` (`ColorpickerElement`, Element layer, `ATTRIBUTE_TABLE`,
354
+ `change` event with `onChange` twin) · `src/colorpicker.ts` (`WebColorpicker`,
355
+ logic class) · `src/types.ts` · `src/logger.ts` · `src/index.ts`
356
+ - `src/css/` — the canonical eight files, `@layer variables, component, overrides`,
357
+ `--cpk-*` ⇢ `--base-*` ⇢ `light-dark()` chains, Strategy-B dark mode, FOUC rule
358
+ - Custom Elements Manifest config + package.json `customElements` / `web-types`,
359
+ `component-variables.manifest.json` (exported as `/manifest`)
360
+ - README in the canonical shape, `docs/{usage,theming,examples,accessibility}.md`,
361
+ CHANGELOG (`## [0.1.0] - <date>`), LICENSE, `.claude/commands/publish.md`
362
+ - a `files` allowlist (dist, docs, manifests, editor-integration JSON) ending in
363
+ `!**/CLAUDE.md` and `!**/.claude`, so AI-assistant guidance never ships
364
+ - examples (index, basic, theming), fixtures + smoke and dark-mode e2e specs,
365
+ and the `kmwc sync` files
366
+
367
+ By default the component is a small working sample (a toggle with `label` /
368
+ `active` inputs and a `change` event) to rename or replace. Add `--minimal`
369
+ for an empty skeleton instead: the same files and structure, but an empty
370
+ `ATTRIBUTE_TABLE`, no events, a root that renders its slotted content, and
371
+ stub feature CSS — it passes the same rulebook suites.
372
+
373
+ Reserve the new prefix in the rulebook's `base-variables.md` afterwards — the
374
+ command prints the reminder.
375
+
376
+ ### Migrating a component
377
+
378
+ `kmwc doctor` lists everything that's left, so run it after each step.
379
+
380
+ **Repo in a different shape** (a one-package monorepo, a non-standard layout)?
381
+ Don't teach the devkit the old shape: back the repo up, run `kmwc new web-<name>
382
+ --adopt --dir <scratch>` (same files, with the registered port and prefix, no
383
+ registry edits), and move the component's own code into that layout with
384
+ `git mv`. web-editor was flattened this way.
385
+
386
+ 1. `npm i -D @keenmate/web-components-devkit vite@^7` (plus `@playwright/test`
387
+ if the repo has e2e tests).
388
+ 2. Add `package.json#kmwc`. Take the port block (`ports.dev` = block,
389
+ `ports.image` = block + 10) and prefix from
390
+ [`components.json`](components.json), the Plausible id from the old
391
+ `plausible-snippet.html` (`analytics.plausibleSiteId`), the image name and
392
+ runner from the old Makefile, the canonical origin from the old
393
+ `examples-copy-link.js`, and internals from the old
394
+ `check-variable-manifest.mjs` (`cssVariables.internalVariables`).
395
+ 3. Replace `vite.config.ts` (and any `vite.examples.config.ts`) with
396
+ `defineComponentConfig()`. Move genuinely specific bits to `plugins` and
397
+ `overrides`. Do the same for `playwright.config.ts`.
398
+ 4. Pages: delete the `<link href="examples-shared.css">` and the
399
+ `examples-chapter-nav.js` / `examples-copy-link.js` script tags. Change
400
+ `import { persistControls } from './examples-controls-store.js'` to
401
+ `import { attachControlsPersistence } from '@keenmate/web-components-devkit/examples'`
402
+ (same storage keys — saved demo state survives). Move component-only CSS into
403
+ `examples-local.css`.
404
+ 5. Swap `index.html` for `renderExamplesIndex({ pages })`, keeping the page
405
+ registry. The inline `<style>` and header markup go away.
406
+ 6. Delete the legacy files: `examples-chapter-nav.js`, `examples-copy-link.js`,
407
+ `examples-controls-store.js`, `examples-shared.css`,
408
+ `plausible-snippet.html`, `scripts/check-variable-manifest.mjs`.
409
+ 7. Set `"check:vars": "kmwc check-vars"` and run it.
410
+ 8. `npx kmwc sync`, then review the diff. Repo-specific Makefile targets move
411
+ to `Makefile.local`.
412
+ 9. Check it runs: `npm run dev`, `npm run build`, `npm run build:examples`,
413
+ `npm run preview:examples`, the e2e tests, and finally `npx kmwc doctor`
414
+ (devkit + rulebook — rulebook failures are real findings to fix or to
415
+ document under `## Known limitations`, per the rulebook).
416
+
417
+ ### Developing the devkit
418
+
419
+ ```
420
+ npm install
421
+ npm test # unit tests (config, registry, templates, sync, checks, intake, plugin, kit)
422
+ npm run test:e2e # playground: the kit + scaffold in a real browser, dev server AND built site
423
+ npm run readme # regenerate the catalogue
424
+ ```
425
+
426
+ [`playground/`](playground) is a real component scaffolded with `kmwc new` and
427
+ linked to this checkout. It's the reference for "what a component looks like",
428
+ the e2e target for every kit feature, and `npx kmwc doctor` inside it must
429
+ report every rulebook suite green. When you change the kit or a template, add
430
+ a spec there.
431
+
432
+ Releases are prepared with the `/publish` Claude command
433
+ (`.claude/commands/publish.md`, the rulebook's canonical `/publish` with devkit
434
+ steps: unit + e2e + playground doctor + catalogue check, entry-point smoke
435
+ test, `npm pack` review). It never publishes or pushes. That's yours.