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

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 +76 -0
  2. package/LICENSE +21 -0
  3. package/README.md +417 -2
  4. package/components.json +189 -0
  5. package/package.json +75 -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 +302 -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 +72 -0
  14. package/src/cli/sync-plan.js +163 -0
  15. package/src/config.d.ts +147 -0
  16. package/src/config.js +257 -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 +110 -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 +209 -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 +40 -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 +26 -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,76 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0-rc01] - 2026-10-12 [PUBLISHED]
4
+
5
+ First release of `@keenmate/web-components-devkit`, plus the component catalogue.
6
+
7
+ ### Added
8
+
9
+ - **Catalogue**: `components.json` registry (ids, CSS prefixes, port blocks, links);
10
+ the README tables (components, libraries, showcases/tests — with each repo's
11
+ dev port, no versions/status that would go stale) are generated by
12
+ `npm run readme`. Port blocks of 100 per repo (dev = block, preview = +1,
13
+ examples image = +10), numbered by 100 inside a range per kind with headroom —
14
+ components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900,
15
+ tests 17000–17900 — so repos run side by side without collisions and new
16
+ components land next to the existing ones.
17
+ - **Vite preset** `defineComponentConfig()` (Vite 7): dev server on a strict
18
+ registry port, library build (es + umd + `style.css` + rolled-up d.ts), and
19
+ `--mode examples` multi-page static build with relative asset URLs.
20
+ - **Examples kit**, injected into every root page: `@layer kmwc` design system
21
+ merged from six diverged `examples-shared.css` copies, with `--kmwc-*` tokens
22
+ read through the rulebook's `--base-*` taxonomy (`light-dark()` fallbacks) and
23
+ a light / dark / auto theme switch that drives `data-theme` on `<html>`;
24
+ kit-generated DOM is `kmwc-` prefixed BEM. Chapter nav (bundled Floating UI,
25
+ shared slug logic, last-chapter highlight at page bottom), copy-link, version
26
+ badge with build stamp, build-only Plausible. Helpers:
27
+ `attachControlsPersistence`, `renderExamplesIndex` (its footer links the
28
+ devkit version that built the site), `mountExamplesKit`.
29
+ - **Playwright preset** `definePlaywrightConfig()`.
30
+ - **CLI** `kmwc`: `sync` (Dockerfile, nginx.conf with immutable `/assets/`
31
+ caching, Makefile + `Makefile.local`, .dockerignore, CLAUDE.md block, scripts),
32
+ `doctor` (devkit checks + the BlissFramework rulebook's `*.checks.sh` suites),
33
+ `check-vars` (declared/consumed modes; replaces three script copies),
34
+ `check-release` (dist-tag ⇔ version, not yet on npm — gates `make publish*`),
35
+ `new` (rulebook-shaped scaffold with intake: npm availability, reserved-prefix
36
+ check, next free block in the component port range — a taken or misaligned
37
+ `--port` is refused), `readme`.
38
+ - **Fragment pages**: a page without `<html>` gets the devkit layout (head, entry
39
+ script, header, container) from `<meta name="kmwc:…">` tags; `<kmwc-include>` pulls
40
+ shared partials (parameters, nesting, cycle detection); `<kmwc-demo>` renders live
41
+ markup plus a code block of exactly that markup (and `<script data-kmwc-show>`), so
42
+ snippets can't drift from demos. `kmwc doctor` lists pages still repeating the boilerplate.
43
+ - **Scaffold** (`kmwc new`): Element / Logic / types / logger layers, the
44
+ canonical eight-file CSS set, Strategy-B dark mode, Custom Elements Manifest,
45
+ README + `docs/`, CHANGELOG, LICENSE, `.claude/commands/publish.md`, examples,
46
+ smoke + dark-mode e2e specs — example pages are fragments. `--minimal` gives an
47
+ empty skeleton instead of the sample toggle. A fresh scaffold passes every rulebook suite.
48
+ - **Playground** (`playground/`): a component scaffolded with `kmwc new`, linked to
49
+ the devkit and registered in the catalogue (port 17300) — the e2e test bed for
50
+ the kit, the presets and the scaffold, in dev and in the built site.
51
+
52
+ - **Package-contents check** in `kmwc doctor`: `npm pack --dry-run` must not ship
53
+ `CLAUDE.md` / `.claude/` (at any depth), tests and specs, example pages,
54
+ partials, build/test output or local make config (`--no-pack` skips it).
55
+ `kmwc new` ends `files` with `!**/CLAUDE.md` and `!**/.claude`.
56
+
57
+ - **`/publish` command** (`.claude/commands/publish.md`) for the devkit itself, in
58
+ the rulebook's canonical shape (passes `publish-command.checks.sh`): gates are the
59
+ unit tests, the playground e2e (dev + built), the playground doctor, `sync --check`
60
+ and `readme:check`; "build" is an entry-point smoke test, since `src/` ships as-is.
61
+
62
+ ### Fixed
63
+
64
+ - **Flash of unstyled page on every reload in dev** (FOUC): the kit stylesheet was
65
+ imported from the kit's module script, which Vite dev applies only after the script
66
+ runs. The plugin now loads it render-blocking: a `<link>` in `<head>` in dev, a CSS
67
+ import (extracted to a `<head>` `<link>`) in builds. `auto.js` no longer imports
68
+ it. An e2e spec loads a page with JavaScript off and checks it is styled.
69
+ - **Fragment detection** (`checkIsFragment`): a page is a full document only when it
70
+ *starts* with `<!DOCTYPE` / `<html>` (after a BOM, whitespace or comments). A fragment
71
+ whose script or code sample mentions `<html>` was passed through without the layout
72
+ (found migrating web-commandpalette's playground page).
73
+
74
+ ### Migrated
75
+
76
+ - **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,418 @@
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-rc01
23
+
24
+ - **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.
25
+ - **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.
26
+ - **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.
27
+ - **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.
28
+ - **`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.
29
+ - **`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.
30
+ - **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.
31
+
32
+ ## Catalogue
33
+
34
+ <!-- kmwc:catalogue:start — generated by `npm run readme`, do not edit inside -->
35
+ ### Components
36
+
37
+ | Component | Package | Prefix | Dev port | Links | Devkit | About |
38
+ |---|---|---|---:|---|:---:|---|
39
+ | **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… |
40
+ | **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 |
41
+ | **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… |
42
+ | **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,… |
43
+ | **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. |
44
+ | **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 |
45
+ | **web-editor** | `@keenmate/web-editor` | — | 12700 | [examples](https://examples.web-editor.keenmate.dev) | — | KeenMate Web Editor monorepo — a framework-agnostic WYSIWYG / Markdown editor built on ProseMirror. |
46
+ | **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… |
47
+ | **web-timepicker** | — | — | 12900 | — | — | Vanilla-JS prototypes (spiral / slider time pickers); no package yet. |
48
+ | **web-wysiwyg** | — | — | 13000 | — | — | ProseMirror exploration; superseded by web-editor. |
49
+
50
+ ### Shared libraries
51
+
52
+ | Library | Package | Dev port | About |
53
+ |---|---|---:|---|
54
+ | **web-components-core** | `@keenmate/web-components-core` | 15000 | Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset. |
55
+ | **web-components** | `@keenmate/web-components-devkit` | 15100 | This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI). |
56
+
57
+ ### Showcases, docs & test harnesses
58
+
59
+ | Repo | Kind | For | Dev port | About |
60
+ |---|---|---|---:|---|
61
+ | web-multiselect-showcase | showcase | web-multiselect | 16000 | |
62
+ | web-daterangepicker-showcase | showcase | web-daterangepicker | 16100 | |
63
+ | web-treeview-showcase | showcase | web-treeview | 16200 | |
64
+ | web-grid-showcase | showcase | web-grid | 16300 | [https://web-grid.keenmate.com](https://web-grid.keenmate.com) |
65
+ | web-multiselect-docs | docs | web-multiselect | 16400 | Content-only docs (keendocs). |
66
+ | web-component-framework-test | test | — | 17000 | Smoke + contrast tests inside CSS host frameworks (plain / Bootstrap / …). |
67
+ | web-multiselect-test | test | web-multiselect | 17100 | npm-install vs direct-file consumption checks before publishing. |
68
+ | web-multiselect-positioning-demo | test | web-multiselect | 17200 | Single-page positioning repro. |
69
+ | web-playground | test | web-components | 17300 | Devkit e2e test bed — a component scaffolded with `kmwc new`, linked to this repo. |
70
+
71
+ **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.
72
+ <!-- kmwc:catalogue:end -->
73
+
74
+ To add or change a row, edit [`components.json`](components.json) and run
75
+ `npm run readme`. Descriptions and the Devkit ✅ are read from the
76
+ sibling checkouts next to this repo. `npm run readme:check` fails when the
77
+ table is stale. Prefixes mirror the reserved-prefix table in the rulebook's
78
+ `base-variables.md`, which stays the authority.
79
+
80
+ ---
81
+
82
+ ## The devkit
83
+
84
+ ### Why
85
+
86
+ Before the devkit, every component repo carried its own copy of
87
+ `examples-shared.css` (six copies, six different versions),
88
+ `examples-chapter-nav.js` (three versions), `examples-copy-link.js`,
89
+ `examples-controls-store.js`, `plausible-snippet.html`,
90
+ `check-variable-manifest.mjs`, a `vite.config.ts`, a `Dockerfile`, an
91
+ `nginx.conf` and a `Makefile`. A fix made in one repo never reached the others.
92
+ web-treeview forked the whole 980-line stylesheet just to change purple to
93
+ green. Two components shared a dev port and three shared an image port.
94
+
95
+ The devkit owns all of that. A component states what's specific to it **once**,
96
+ in `package.json#kmwc`, and gets the rest.
97
+
98
+ ### What a component gets
99
+
100
+ | Piece | Import / command | Replaces |
101
+ |---|---|---|
102
+ | Vite preset | `@keenmate/web-components-devkit/vite` → `defineComponentConfig()` | per-repo `vite.config.ts`, `vite.examples.config.ts` |
103
+ | 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` |
104
+ | 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 |
105
+ | Examples helpers | `@keenmate/web-components-devkit/examples` → `attachControlsPersistence`, `renderExamplesIndex`, `mountExamplesKit` | `examples-controls-store.js`, 150 lines of inline index-page CSS |
106
+ | Playwright preset | `@keenmate/web-components-devkit/playwright` → `definePlaywrightConfig()` | per-repo `playwright.config.ts` |
107
+ | Generated files | `kmwc sync` | hand-edited `Dockerfile`, `nginx.conf`, `Makefile`, `.dockerignore` |
108
+ | Standard check | `kmwc doctor` (devkit checks + the rulebook's `*.checks.sh`) | — |
109
+ | Manifest check | `kmwc check-vars` | 3 diverged `check-variable-manifest.mjs` |
110
+ | Release gate | `kmwc check-release` (used by `make publish` / `publish-rc`) | — |
111
+ | Scaffolder | `kmwc new web-<name> --prefix <p>` | copy-pasting the last repo |
112
+
113
+ ### Install
114
+
115
+ ```
116
+ npm i -D @keenmate/web-components-devkit vite@^7
117
+ ```
118
+
119
+ To develop against a local checkout of this repo, keep the semver range in
120
+ `package.json` (Docker builds install from the registry) and link it:
121
+
122
+ ```
123
+ cd ../web-components && npm link
124
+ cd ../web-<component> && npm link @keenmate/web-components-devkit
125
+ ```
126
+
127
+ Peer dependencies (`vite`, `@playwright/test`) are always resolved from the
128
+ **component's** `node_modules`, so a linked devkit doesn't load a second copy.
129
+
130
+ ### `package.json#kmwc`
131
+
132
+ Every field is optional. Defaults are derived from the package.
133
+
134
+ ```jsonc
135
+ "kmwc": {
136
+ "title": "Web MultiSelect", // examples index header
137
+ "tagline": "Feature-rich multiselect …", // default: package.json description
138
+ "tag": "web-multiselect",
139
+ "prefix": "ms", // reserved CSS prefix (rulebook base-variables.md)
140
+ "library": {
141
+ "entry": "src/index.ts", // default
142
+ "globalName": "MultiSelect", // UMD global
143
+ "fileName": "multiselect", // → dist/multiselect.js + .umd.js
144
+ "cssFileName": "style", // default; keeps dist/style.css on Vite 7
145
+ "shouldEmitTypes": true, // rolled-up .d.ts (typesPluginConfig to tune)
146
+ "shouldEmitSourcemap": false,
147
+ "external": []
148
+ },
149
+ "ports": { "dev": 12100, "image": 12110 }, // the repo's components.json port block (+10 = image)
150
+ "theme": { "accentColor": "#667eea", "accentEndColor": "#764ba2" },
151
+ "examples": {
152
+ "canonicalOrigin": "https://examples.web-multiselect.keenmate.dev",
153
+ "staticPaths": ["examples-data", "docs"], // copied verbatim into the built site
154
+ "excludedPages": [], // root *.html that aren't example pages
155
+ "outDir": "dist-examples"
156
+ },
157
+ "analytics": { "plausibleSiteId": "pa-5vUESDVyztHMyzN5gs5A4" },
158
+ "image": {
159
+ "name": "registry.km8.es/web-multiselect-examples:prod",
160
+ "runner": "podman",
161
+ "packageJsonPaths": ["package.json"] // monorepos: list workspace package.jsons too
162
+ },
163
+ "cssVariables": {
164
+ "sources": ["src/css"], // dirs (stylesheets) or explicit files (any type)
165
+ "internalVariables": ["ms-input-current-width"], // runtime internals, not theming knobs
166
+ "checkMode": "declared" // or "consumed" (var() reads)
167
+ },
168
+ "rulebook": {
169
+ "path": "../../BlissFramework/guidelines/web-components", // default; env KMWC_RULEBOOK_PATH wins
170
+ "skippedChecks": [] // e.g. ["responsive-overlay"]
171
+ },
172
+ "sync": { "skippedFiles": [] } // e.g. ["Makefile"] to opt a file out
173
+ }
174
+ ```
175
+
176
+ ### Vite preset
177
+
178
+ ```ts
179
+ // vite.config.ts
180
+ import { defineComponentConfig } from '@keenmate/web-components-devkit/vite';
181
+ export default defineComponentConfig();
182
+
183
+ // with repo-specific bits
184
+ export default defineComponentConfig({
185
+ plugins: [shadowCssPlugin()],
186
+ overrides: { optimizeDeps: { entries: ['index.html', 'examples-*.html'] } },
187
+ getOverridesCallback: (env, config) => ({ /* env-dependent bits */ }),
188
+ });
189
+ ```
190
+
191
+ | Command | Result |
192
+ |---|---|
193
+ | `vite` | dev server on `kmwc.ports.dev` (strict, never hops ports) |
194
+ | `vite build` | library: `dist/<fileName>.js`, `.umd.js`, `style.css`, rolled-up `.d.ts` |
195
+ | `vite build --mode examples` | static examples site → `dist-examples/` (every root `*.html` is a page) |
196
+ | `vite preview --mode examples` | serves the built site on the preview port (`ports.dev + 1`) |
197
+
198
+ The preset defines `__VERSION__`, `__PACKAGE_NAME__`, `__AUTHOR__`,
199
+ `__LICENSE__`, `__REPOSITORY__` and `__HOMEPAGE__`. For a hand-assembled config
200
+ (monorepos, showcases), use the plugin on its own: `createExamplesPlugin()`.
201
+
202
+ ### Examples kit
203
+
204
+ The plugin injects the kit into every **root-level** `*.html` page. Pages under
205
+ `test/` (Playwright fixtures) are left alone, and any page can opt out with
206
+ `<html data-kmwc="off">`.
207
+
208
+ #### Fragment pages — no copied boilerplate
209
+
210
+ A page without `<html>` is a **fragment**: it carries only its own content,
211
+ and the plugin wraps it in the devkit layout (head, entry script, header with
212
+ back link / title / subtitle, container). The whole file:
213
+
214
+ ```html
215
+ <meta name="kmwc:title" content="Tree of Options">
216
+ <meta name="kmwc:subtitle" content="Hierarchical options via a materialized path-member…">
217
+ <link rel="stylesheet" href="examples-local.css"> <!-- moved into <head> -->
218
+
219
+ <kmwc-include src="partials/no-collapse-note.html" title="No collapse"></kmwc-include>
220
+
221
+ <div class="card"> <!-- each h2 = a chapter -->
222
+ <h2>TR01 · Basic tree</h2>
223
+ <kmwc-demo>
224
+ <web-multiselect id="tree" path-member="path"></web-multiselect>
225
+ <script type="module" data-kmwc-show>
226
+ document.getElementById('tree').options = [{ value: 'fruit', label: 'Fruit', path: '1' }];
227
+ </script>
228
+ </kmwc-demo>
229
+ </div>
230
+
231
+ <script type="module">
232
+ import { attachControlsPersistence } from '@keenmate/web-components-devkit/examples';
233
+ attachControlsPersistence('tree');
234
+ </script>
235
+ ```
236
+
237
+ | Construct | Does |
238
+ |---|---|
239
+ | `<meta name="kmwc:title">` (required), `kmwc:subtitle`, `kmwc:body-class` | header + `<title>`, body classes |
240
+ | `<meta name="kmwc:layout" content="bare">` | head only, no header/container (the index page) |
241
+ | `<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) |
242
+ | `<link>` / `<style>` anywhere in a fragment | moved into `<head>` |
243
+ | `<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 |
244
+ | `<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` |
245
+
246
+ Tags inside HTML comments are never expanded. Partials live in the repo's
247
+ `partials/` folder (a change reloads the dev server page); shared demo data
248
+ belongs in `examples-data/*.js` modules imported by pages. Full documents
249
+ (`<html>…`) pass through untouched, so pages migrate one at a time — `kmwc
250
+ doctor` lists the ones still repeating the head/header boilerplate.
251
+
252
+ - **Styles** live in `@layer kmwc`, so any unlayered rule beats them
253
+ regardless of order (and the layered reset can never clobber a component).
254
+ Tokens (`--kmwc-*`) read through the rulebook's `--base-*` taxonomy with
255
+ `light-dark()` fallbacks, so a theme-designer theme restyles the demo page
256
+ together with the component; colour literals live only in `tokens.css`.
257
+ Re-theme a site with `kmwc.theme.accentColor`. The page vocabulary is
258
+ unchanged from the old shared CSS — `.card`, `.example-section`, `.controls`,
259
+ `.demo-area`, `.demo-card`, `.code-block`, `.note(.warning|.info|.success|.error)`,
260
+ `.grid`/`.card-grid`, `.reference-table`, `.log-panel`, `.pill`,
261
+ `.status-dot`, `.control-group`, buttons (`.btn-secondary`, `.btn-outline`,
262
+ `.btn-danger`) — while everything the kit **generates** is prefixed BEM
263
+ (`kmwc-chapter-nav__item--active`, `kmwc-copy-link--copied`, …).
264
+ - **Theme switch** (top right) cycles light → dark → auto and sets
265
+ `data-theme` on `<html>` — the same signal components react to via
266
+ `:host-context([data-theme])`, so every example page is a dark-mode smoke
267
+ test. The saved choice is applied before first paint.
268
+ - **Chapter nav and copy-link** share one section/slug algorithm, so the
269
+ jump-list hashes and copied links always match. Floating UI is bundled (no
270
+ CDN). At the bottom of a short page, the last chapter gets highlighted.
271
+ - **Version badge**: any `[data-kmwc-version]` (or legacy `#version-badge`)
272
+ shows `v1.2.3`, or `v1.2.3 (built …)` in a built site.
273
+ - **Landing page**: `renderExamplesIndex({ pages })` renders the header, cards,
274
+ footer and cross-page nav from the registry plus `kmwc` config.
275
+ - **Late content**: the kit mounts one task after DOM-ready. Pages that render
276
+ sections after a fetch call `mountExamplesKit()`.
277
+ - **Analytics**: the Plausible snippet goes into **built** pages only, and only
278
+ when `kmwc.analytics.plausibleSiteId` is set.
279
+
280
+ ### Playwright preset
281
+
282
+ ```ts
283
+ import { definePlaywrightConfig, devices } from '@keenmate/web-components-devkit/playwright';
284
+ export default definePlaywrightConfig({
285
+ projects: [
286
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, testIgnore: ['**/mobile.spec.ts'] },
287
+ { name: 'mobile', use: { ...devices['Pixel 7'] }, testMatch: ['**/mobile.spec.ts'] },
288
+ ],
289
+ });
290
+ ```
291
+
292
+ baseURL and the `npm run dev` web server come from `kmwc.ports.dev`.
293
+
294
+ ### CLI
295
+
296
+ ```
297
+ kmwc sync [--check] regenerate Dockerfile, nginx.conf, Makefile, .dockerignore, the
298
+ CLAUDE.md devkit block, .gitignore entries, missing standard scripts
299
+ kmwc doctor [--json] devkit checks + package contents + the rulebook's *.checks.sh suites
300
+ (exit 1 on errors). --verbose lists passes, --no-rulebook skips the
301
+ suites, --no-pack skips the package-contents check
302
+ kmwc check-vars [--strict] component-variables.manifest.json ⇔ CSS
303
+ kmwc check-release --tag t latest ⇔ release version, rc ⇔ -rcN, version not yet on npm
304
+ kmwc new web-<name> scaffold a component (--prefix, --title, --tag, --dir, --port, --minimal, --register)
305
+ kmwc readme [--check] regenerate this README's catalogue (devkit repo only)
306
+ ```
307
+
308
+ Generated files start with a `GENERATED by kmwc sync` header. Repo-specific
309
+ make targets go in `Makefile.local`, which the generated Makefile includes at
310
+ the end. Per-developer overrides (`DOCKER_RUNNER`, `IMAGE_PORT`) go in
311
+ `.makefile.env`. `make publish` / `make publish-rc` run `kmwc check-release`
312
+ and the e2e suite before building and publishing.
313
+
314
+ **`kmwc doctor` and the rulebook.** Doctor runs every suite in the rulebook
315
+ (component-structure, naming-conventions, css-structure, theme-container,
316
+ base-variables, color-scheme, readme-structure, custom-elements-manifest,
317
+ responsive-overlay, publish-command) with `bash`, passing `kmwc.prefix`, and
318
+ reports each `FAIL` as an error pointing at the matching `.checks.md` entry.
319
+ When the rulebook isn't checked out (or there's no bash) the suites are
320
+ skipped with a warning — the rulebook stays the single source of truth; the
321
+ devkit never copies its rules.
322
+
323
+ **Package contents.** Doctor runs `npm pack --dry-run` and fails on anything
324
+ dev-only that would reach a consumer's `node_modules`: `CLAUDE.md` at any
325
+ depth and `.claude/` (AI-assistant guidance, which a directory entry such as
326
+ `src/css` in `files` sweeps in), `test/` / `e2e/` / `*.spec.*` / `*.test.*`,
327
+ example pages and `partials/`, build/test output, and local make config. Keep
328
+ `"!**/CLAUDE.md"` and `"!**/.claude"` at the end of `files`; the scaffold adds them.
329
+
330
+ ### Scaffolding a component
331
+
332
+ ```
333
+ npx kmwc new web-colorpicker --prefix cpk --title "Web Color Picker" --register
334
+ ```
335
+
336
+ `kmwc new` runs the rulebook intake: the npm name must be free and the CSS
337
+ prefix must be 2–4 lowercase letters not reserved in `base-variables.md` or
338
+ `components.json` (it's derived only when unambiguous — hyphenated words, or a
339
+ short single word — otherwise pass `--prefix`). The result passes every
340
+ rulebook suite on its first commit:
341
+
342
+ - `src/web-component.ts` (`ColorpickerElement`, Element layer, `ATTRIBUTE_TABLE`,
343
+ `change` event with `onChange` twin) · `src/colorpicker.ts` (`WebColorpicker`,
344
+ logic class) · `src/types.ts` · `src/logger.ts` · `src/index.ts`
345
+ - `src/css/` — the canonical eight files, `@layer variables, component, overrides`,
346
+ `--cpk-*` ⇢ `--base-*` ⇢ `light-dark()` chains, Strategy-B dark mode, FOUC rule
347
+ - Custom Elements Manifest config + package.json `customElements` / `web-types`,
348
+ `component-variables.manifest.json` (exported as `/manifest`)
349
+ - README in the canonical shape, `docs/{usage,theming,examples,accessibility}.md`,
350
+ CHANGELOG (`## [0.1.0] - <date>`), LICENSE, `.claude/commands/publish.md`
351
+ - a `files` allowlist (dist, docs, manifests, editor-integration JSON) ending in
352
+ `!**/CLAUDE.md` and `!**/.claude`, so AI-assistant guidance never ships
353
+ - examples (index, basic, theming), fixtures + smoke and dark-mode e2e specs,
354
+ and the `kmwc sync` files
355
+
356
+ By default the component is a small working sample (a toggle with `label` /
357
+ `active` inputs and a `change` event) to rename or replace. Add `--minimal`
358
+ for an empty skeleton instead: the same files and structure, but an empty
359
+ `ATTRIBUTE_TABLE`, no events, a root that renders its slotted content, and
360
+ stub feature CSS — it passes the same rulebook suites.
361
+
362
+ Reserve the new prefix in the rulebook's `base-variables.md` afterwards — the
363
+ command prints the reminder.
364
+
365
+ ### Migrating a component
366
+
367
+ `kmwc doctor` lists everything that's left, so run it after each step.
368
+
369
+ 1. `npm i -D @keenmate/web-components-devkit vite@^7` (plus `@playwright/test`
370
+ if the repo has e2e tests).
371
+ 2. Add `package.json#kmwc`. Take the port block (`ports.dev` = block,
372
+ `ports.image` = block + 10) and prefix from
373
+ [`components.json`](components.json), the Plausible id from the old
374
+ `plausible-snippet.html` (`analytics.plausibleSiteId`), the image name and
375
+ runner from the old Makefile, the canonical origin from the old
376
+ `examples-copy-link.js`, and internals from the old
377
+ `check-variable-manifest.mjs` (`cssVariables.internalVariables`).
378
+ 3. Replace `vite.config.ts` (and any `vite.examples.config.ts`) with
379
+ `defineComponentConfig()`. Move genuinely specific bits to `plugins` and
380
+ `overrides`. Do the same for `playwright.config.ts`.
381
+ 4. Pages: delete the `<link href="examples-shared.css">` and the
382
+ `examples-chapter-nav.js` / `examples-copy-link.js` script tags. Change
383
+ `import { persistControls } from './examples-controls-store.js'` to
384
+ `import { attachControlsPersistence } from '@keenmate/web-components-devkit/examples'`
385
+ (same storage keys — saved demo state survives). Move component-only CSS into
386
+ `examples-local.css`.
387
+ 5. Swap `index.html` for `renderExamplesIndex({ pages })`, keeping the page
388
+ registry. The inline `<style>` and header markup go away.
389
+ 6. Delete the legacy files: `examples-chapter-nav.js`, `examples-copy-link.js`,
390
+ `examples-controls-store.js`, `examples-shared.css`,
391
+ `plausible-snippet.html`, `scripts/check-variable-manifest.mjs`.
392
+ 7. Set `"check:vars": "kmwc check-vars"` and run it.
393
+ 8. `npx kmwc sync`, then review the diff. Repo-specific Makefile targets move
394
+ to `Makefile.local`.
395
+ 9. Check it runs: `npm run dev`, `npm run build`, `npm run build:examples`,
396
+ `npm run preview:examples`, the e2e tests, and finally `npx kmwc doctor`
397
+ (devkit + rulebook — rulebook failures are real findings to fix or to
398
+ document under `## Known limitations`, per the rulebook).
399
+
400
+ ### Developing the devkit
401
+
402
+ ```
403
+ npm install
404
+ npm test # unit tests (config, registry, templates, sync, checks, intake, plugin, kit)
405
+ npm run test:e2e # playground: the kit + scaffold in a real browser, dev server AND built site
406
+ npm run readme # regenerate the catalogue
407
+ ```
408
+
409
+ [`playground/`](playground) is a real component scaffolded with `kmwc new` and
410
+ linked to this checkout. It's the reference for "what a component looks like",
411
+ the e2e target for every kit feature, and `npx kmwc doctor` inside it must
412
+ report every rulebook suite green. When you change the kit or a template, add
413
+ a spec there.
414
+
415
+ Releases are prepared with the `/publish` Claude command
416
+ (`.claude/commands/publish.md`, the rulebook's canonical `/publish` with devkit
417
+ steps: unit + e2e + playground doctor + catalogue check, entry-point smoke
418
+ test, `npm pack` review). It never publishes or pushes. That's yours.