@keenmate/web-components-devkit 1.0.0-rc01 → 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.
- package/CHANGELOG.md +50 -0
- package/README.md +19 -2
- package/components.json +206 -189
- package/package.json +3 -1
- package/src/cli/commands/new.js +29 -8
- package/src/cli/kmwc.js +1 -0
- package/src/config.d.ts +2 -0
- package/src/config.js +25 -3
- package/src/examples/css/controls.css +4 -4
- package/src/examples/css/index-page.css +1 -1
- package/src/examples/css/tokens.css +41 -27
- package/src/vite/page-composer.js +17 -10
- package/templates/new/component-variables.manifest.json +3 -3
- package/templates/new/docs/examples.md +1 -1
- package/templates/new/docs/theming.md +1 -1
- package/templates/new/e2e/dark-mode.spec.ts +3 -3
- package/templates/new/examples-theming.html +3 -3
- package/templates/new/src/css/variables.css +10 -8
- package/templates/new-minimal/src/css/variables.css +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
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
|
+
|
|
3
53
|
## [1.0.0-rc01] - 2026-10-12 [PUBLISHED]
|
|
4
54
|
|
|
5
55
|
First release of `@keenmate/web-components-devkit`, plus the component catalogue.
|
package/README.md
CHANGED
|
@@ -19,6 +19,13 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
19
19
|
> positioning) lives in [`@keenmate/web-components-core`](../web-components-core).
|
|
20
20
|
> Nothing here ships inside a component's bundle.
|
|
21
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
|
+
|
|
22
29
|
## What's New in v1.0.0-rc01
|
|
23
30
|
|
|
24
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.
|
|
@@ -42,7 +49,7 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
42
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,… |
|
|
43
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. |
|
|
44
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 |
|
|
45
|
-
| **web-editor** | `@keenmate/web-editor` |
|
|
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. |
|
|
46
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… |
|
|
47
54
|
| **web-timepicker** | — | — | 12900 | — | — | Vanilla-JS prototypes (spiral / slider time pickers); no package yet. |
|
|
48
55
|
| **web-wysiwyg** | — | — | 13000 | — | — | ProseMirror exploration; superseded by web-editor. |
|
|
@@ -53,6 +60,8 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
53
60
|
|---|---|---:|---|
|
|
54
61
|
| **web-components-core** | `@keenmate/web-components-core` | 15000 | Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset. |
|
|
55
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. |
|
|
56
65
|
|
|
57
66
|
### Showcases, docs & test harnesses
|
|
58
67
|
|
|
@@ -166,7 +175,8 @@ Every field is optional. Defaults are derived from the package.
|
|
|
166
175
|
"checkMode": "declared" // or "consumed" (var() reads)
|
|
167
176
|
},
|
|
168
177
|
"rulebook": {
|
|
169
|
-
"path": "
|
|
178
|
+
"path": "../BlissFramework/guidelines/web-components", // relative to the component; default: the nearest
|
|
179
|
+
// BlissFramework checkout above it. env KMWC_RULEBOOK_PATH wins
|
|
170
180
|
"skippedChecks": [] // e.g. ["responsive-overlay"]
|
|
171
181
|
},
|
|
172
182
|
"sync": { "skippedFiles": [] } // e.g. ["Makefile"] to opt a file out
|
|
@@ -302,6 +312,7 @@ kmwc doctor [--json] devkit checks + package contents + the rulebook's
|
|
|
302
312
|
kmwc check-vars [--strict] component-variables.manifest.json ⇔ CSS
|
|
303
313
|
kmwc check-release --tag t latest ⇔ release version, rc ⇔ -rcN, version not yet on npm
|
|
304
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
|
|
305
316
|
kmwc readme [--check] regenerate this README's catalogue (devkit repo only)
|
|
306
317
|
```
|
|
307
318
|
|
|
@@ -366,6 +377,12 @@ command prints the reminder.
|
|
|
366
377
|
|
|
367
378
|
`kmwc doctor` lists everything that's left, so run it after each step.
|
|
368
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
|
+
|
|
369
386
|
1. `npm i -D @keenmate/web-components-devkit vite@^7` (plus `@playwright/test`
|
|
370
387
|
if the repo has e2e tests).
|
|
371
388
|
2. Add `package.json#kmwc`. Take the port block (`ports.dev` = block,
|
package/components.json
CHANGED
|
@@ -1,189 +1,206 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "Registry of Keenmate web components — source for the README catalogue (`npm run readme`). `port` is the repo's block: dev server = port, preview = port + 1, examples image = port + 10. Each kind has its own range with headroom (components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900, tests 17000–17900), numbered by 100 in registry order; `kmwc doctor` checks package.json#kmwc.ports against it and `kmwc new` takes the next block. `prefix` mirrors the reserved-prefix table in BlissFramework guidelines/web-components/base-variables.md (the authority).",
|
|
3
|
-
"components": [
|
|
4
|
-
{
|
|
5
|
-
"id": "web-multiselect",
|
|
6
|
-
"kind": "component",
|
|
7
|
-
"prefix": "ms",
|
|
8
|
-
"port": 12100,
|
|
9
|
-
"path": "../web-multiselect",
|
|
10
|
-
"npm": "@keenmate/web-multiselect",
|
|
11
|
-
"repo": "https://github.com/keenmate/web-multiselect",
|
|
12
|
-
"examples": "https://examples.web-multiselect.keenmate.dev",
|
|
13
|
-
"docs": "https://web-multiselect.keenmate.dev",
|
|
14
|
-
"showcase": "web-multiselect-showcase"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"id": "web-daterangepicker",
|
|
18
|
-
"kind": "component",
|
|
19
|
-
"prefix": "drp",
|
|
20
|
-
"port": 12200,
|
|
21
|
-
"path": "../web-daterangepicker",
|
|
22
|
-
"npm": "@keenmate/web-daterangepicker",
|
|
23
|
-
"repo": "https://github.com/keenmate/web-daterangepicker",
|
|
24
|
-
"docs": "https://web-daterangepicker.keenmate.dev",
|
|
25
|
-
"showcase": "web-daterangepicker-showcase"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"id": "web-treeview",
|
|
29
|
-
"kind": "component",
|
|
30
|
-
"prefix": "wtv",
|
|
31
|
-
"port": 12300,
|
|
32
|
-
"path": "../web-treeview",
|
|
33
|
-
"npm": "@keenmate/web-treeview",
|
|
34
|
-
"repo": "https://github.com/keenmate/web-treeview",
|
|
35
|
-
"showcase": "web-treeview-showcase"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"id": "web-commandpalette",
|
|
39
|
-
"kind": "component",
|
|
40
|
-
"prefix": "cp",
|
|
41
|
-
"port": 12400,
|
|
42
|
-
"path": "../web-commandpalette",
|
|
43
|
-
"npm": "@keenmate/web-commandpalette",
|
|
44
|
-
"repo": "https://github.com/keenmate/web-commandpalette",
|
|
45
|
-
"examples": "https://examples.web-commandpalette.keenmate.dev"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"id": "web-dropzone",
|
|
49
|
-
"kind": "component",
|
|
50
|
-
"prefix": "dz",
|
|
51
|
-
"port": 12500,
|
|
52
|
-
"path": "../web-dropzone",
|
|
53
|
-
"npm": "@keenmate/web-dropzone",
|
|
54
|
-
"repo": "https://github.com/keenmate/web-dropzone",
|
|
55
|
-
"monorepo": true,
|
|
56
|
-
"note": "Monorepo: renderer + headless core."
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"id": "web-grid",
|
|
60
|
-
"kind": "component",
|
|
61
|
-
"prefix": "wg",
|
|
62
|
-
"port": 12600,
|
|
63
|
-
"path": "../web-grid",
|
|
64
|
-
"npm": "@keenmate/web-grid",
|
|
65
|
-
"repo": "https://github.com/keenmate/web-grid",
|
|
66
|
-
"docs": "https://web-grid.keenmate.com",
|
|
67
|
-
"showcase": "web-grid-showcase",
|
|
68
|
-
"monorepo": true
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id": "web-editor",
|
|
72
|
-
"kind": "component",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"note": "
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"id": "web-switch",
|
|
82
|
-
"kind": "component",
|
|
83
|
-
"port": 12800,
|
|
84
|
-
"path": "../web-switch",
|
|
85
|
-
"npm": "@keenmate/web-switch",
|
|
86
|
-
"repo": "https://github.com/keenmate/web-switch",
|
|
87
|
-
"note": "Svelte-based wrapper around @keenmate/svelte-switch."
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"id": "web-timepicker",
|
|
91
|
-
"kind": "component",
|
|
92
|
-
"port": 12900,
|
|
93
|
-
"path": "../web-timepicker",
|
|
94
|
-
"note": "Vanilla-JS prototypes (spiral / slider time pickers); no package yet."
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"id": "web-wysiwyg",
|
|
98
|
-
"kind": "component",
|
|
99
|
-
"port": 13000,
|
|
100
|
-
"path": "../web-wysiwyg",
|
|
101
|
-
"note": "ProseMirror exploration; superseded by web-editor."
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
"id": "web-components-core",
|
|
105
|
-
"kind": "library",
|
|
106
|
-
"port": 15000,
|
|
107
|
-
"path": "../web-components-core",
|
|
108
|
-
"npm": "@keenmate/web-components-core",
|
|
109
|
-
"note": "Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset."
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"id": "web-components",
|
|
113
|
-
"kind": "library",
|
|
114
|
-
"port": 15100,
|
|
115
|
-
"path": ".",
|
|
116
|
-
"npm": "@keenmate/web-components-devkit",
|
|
117
|
-
"repo": "https://github.com/keenmate/web-components",
|
|
118
|
-
"note": "This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI)."
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"id": "
|
|
122
|
-
"kind": "
|
|
123
|
-
"port":
|
|
124
|
-
"path": "../
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Registry of Keenmate web components — source for the README catalogue (`npm run readme`). `port` is the repo's block: dev server = port, preview = port + 1, examples image = port + 10. Each kind has its own range with headroom (components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900, tests 17000–17900), numbered by 100 in registry order; `kmwc doctor` checks package.json#kmwc.ports against it and `kmwc new` takes the next block. `prefix` mirrors the reserved-prefix table in BlissFramework guidelines/web-components/base-variables.md (the authority).",
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"id": "web-multiselect",
|
|
6
|
+
"kind": "component",
|
|
7
|
+
"prefix": "ms",
|
|
8
|
+
"port": 12100,
|
|
9
|
+
"path": "../web-multiselect",
|
|
10
|
+
"npm": "@keenmate/web-multiselect",
|
|
11
|
+
"repo": "https://github.com/keenmate/web-multiselect",
|
|
12
|
+
"examples": "https://examples.web-multiselect.keenmate.dev",
|
|
13
|
+
"docs": "https://web-multiselect.keenmate.dev",
|
|
14
|
+
"showcase": "web-multiselect-showcase"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "web-daterangepicker",
|
|
18
|
+
"kind": "component",
|
|
19
|
+
"prefix": "drp",
|
|
20
|
+
"port": 12200,
|
|
21
|
+
"path": "../web-daterangepicker",
|
|
22
|
+
"npm": "@keenmate/web-daterangepicker",
|
|
23
|
+
"repo": "https://github.com/keenmate/web-daterangepicker",
|
|
24
|
+
"docs": "https://web-daterangepicker.keenmate.dev",
|
|
25
|
+
"showcase": "web-daterangepicker-showcase"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "web-treeview",
|
|
29
|
+
"kind": "component",
|
|
30
|
+
"prefix": "wtv",
|
|
31
|
+
"port": 12300,
|
|
32
|
+
"path": "../web-treeview",
|
|
33
|
+
"npm": "@keenmate/web-treeview",
|
|
34
|
+
"repo": "https://github.com/keenmate/web-treeview",
|
|
35
|
+
"showcase": "web-treeview-showcase"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "web-commandpalette",
|
|
39
|
+
"kind": "component",
|
|
40
|
+
"prefix": "cp",
|
|
41
|
+
"port": 12400,
|
|
42
|
+
"path": "../web-commandpalette",
|
|
43
|
+
"npm": "@keenmate/web-commandpalette",
|
|
44
|
+
"repo": "https://github.com/keenmate/web-commandpalette",
|
|
45
|
+
"examples": "https://examples.web-commandpalette.keenmate.dev"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "web-dropzone",
|
|
49
|
+
"kind": "component",
|
|
50
|
+
"prefix": "dz",
|
|
51
|
+
"port": 12500,
|
|
52
|
+
"path": "../web-dropzone",
|
|
53
|
+
"npm": "@keenmate/web-dropzone",
|
|
54
|
+
"repo": "https://github.com/keenmate/web-dropzone",
|
|
55
|
+
"monorepo": true,
|
|
56
|
+
"note": "Monorepo: renderer + headless core."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "web-grid",
|
|
60
|
+
"kind": "component",
|
|
61
|
+
"prefix": "wg",
|
|
62
|
+
"port": 12600,
|
|
63
|
+
"path": "../web-grid",
|
|
64
|
+
"npm": "@keenmate/web-grid",
|
|
65
|
+
"repo": "https://github.com/keenmate/web-grid",
|
|
66
|
+
"docs": "https://web-grid.keenmate.com",
|
|
67
|
+
"showcase": "web-grid-showcase",
|
|
68
|
+
"monorepo": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "web-editor",
|
|
72
|
+
"kind": "component",
|
|
73
|
+
"prefix": "km",
|
|
74
|
+
"port": 12700,
|
|
75
|
+
"path": "../web-editor",
|
|
76
|
+
"npm": "@keenmate/web-editor",
|
|
77
|
+
"examples": "https://examples.web-editor.keenmate.dev",
|
|
78
|
+
"note": "ProseMirror-based WYSIWYG / Markdown editor."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "web-switch",
|
|
82
|
+
"kind": "component",
|
|
83
|
+
"port": 12800,
|
|
84
|
+
"path": "../web-switch",
|
|
85
|
+
"npm": "@keenmate/web-switch",
|
|
86
|
+
"repo": "https://github.com/keenmate/web-switch",
|
|
87
|
+
"note": "Svelte-based wrapper around @keenmate/svelte-switch."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "web-timepicker",
|
|
91
|
+
"kind": "component",
|
|
92
|
+
"port": 12900,
|
|
93
|
+
"path": "../web-timepicker",
|
|
94
|
+
"note": "Vanilla-JS prototypes (spiral / slider time pickers); no package yet."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "web-wysiwyg",
|
|
98
|
+
"kind": "component",
|
|
99
|
+
"port": 13000,
|
|
100
|
+
"path": "../web-wysiwyg",
|
|
101
|
+
"note": "ProseMirror exploration; superseded by web-editor."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "web-components-core",
|
|
105
|
+
"kind": "library",
|
|
106
|
+
"port": 15000,
|
|
107
|
+
"path": "../web-components-core",
|
|
108
|
+
"npm": "@keenmate/web-components-core",
|
|
109
|
+
"note": "Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset."
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "web-components",
|
|
113
|
+
"kind": "library",
|
|
114
|
+
"port": 15100,
|
|
115
|
+
"path": ".",
|
|
116
|
+
"npm": "@keenmate/web-components-devkit",
|
|
117
|
+
"repo": "https://github.com/keenmate/web-components",
|
|
118
|
+
"note": "This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI)."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "pure-css",
|
|
122
|
+
"kind": "library",
|
|
123
|
+
"port": 15200,
|
|
124
|
+
"path": "../pure-css",
|
|
125
|
+
"npm": "@keenmate/pure-css",
|
|
126
|
+
"note": "CSS framework mirroring the base-css-variables token contract (demo site + e2e on the dev port)."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "base-css-variables",
|
|
130
|
+
"kind": "library",
|
|
131
|
+
"port": 15300,
|
|
132
|
+
"path": "../base-css-variables",
|
|
133
|
+
"npm": "@keenmate/base-css-variables",
|
|
134
|
+
"repo": "https://github.com/keenmate/base-css-variables",
|
|
135
|
+
"note": "The shared --base-* token contract (base-variables.css) + base-contract-check; examples site + e2e on the dev port."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "web-multiselect-showcase",
|
|
139
|
+
"kind": "showcase",
|
|
140
|
+
"port": 16000,
|
|
141
|
+
"path": "../web-multiselect-showcase",
|
|
142
|
+
"for": "web-multiselect"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "web-daterangepicker-showcase",
|
|
146
|
+
"kind": "showcase",
|
|
147
|
+
"port": 16100,
|
|
148
|
+
"path": "../web-daterangepicker-showcase",
|
|
149
|
+
"for": "web-daterangepicker"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "web-treeview-showcase",
|
|
153
|
+
"kind": "showcase",
|
|
154
|
+
"port": 16200,
|
|
155
|
+
"path": "../web-treeview-showcase",
|
|
156
|
+
"for": "web-treeview"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "web-grid-showcase",
|
|
160
|
+
"kind": "showcase",
|
|
161
|
+
"port": 16300,
|
|
162
|
+
"path": "../web-grid-showcase",
|
|
163
|
+
"for": "web-grid",
|
|
164
|
+
"url": "https://web-grid.keenmate.com"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "web-multiselect-docs",
|
|
168
|
+
"kind": "docs",
|
|
169
|
+
"port": 16400,
|
|
170
|
+
"path": "../web-multiselect-docs",
|
|
171
|
+
"for": "web-multiselect",
|
|
172
|
+
"note": "Content-only docs (keendocs)."
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "web-component-framework-test",
|
|
176
|
+
"kind": "test",
|
|
177
|
+
"port": 17000,
|
|
178
|
+
"path": "../web-component-framework-test",
|
|
179
|
+
"note": "Smoke + contrast tests inside CSS host frameworks (plain / Bootstrap / …)."
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "web-multiselect-test",
|
|
183
|
+
"kind": "test",
|
|
184
|
+
"port": 17100,
|
|
185
|
+
"path": "../web-multiselect-test",
|
|
186
|
+
"for": "web-multiselect",
|
|
187
|
+
"note": "npm-install vs direct-file consumption checks before publishing."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "web-multiselect-positioning-demo",
|
|
191
|
+
"kind": "test",
|
|
192
|
+
"port": 17200,
|
|
193
|
+
"path": "../web-multiselect-positioning-demo",
|
|
194
|
+
"for": "web-multiselect",
|
|
195
|
+
"note": "Single-page positioning repro."
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "web-playground",
|
|
199
|
+
"kind": "test",
|
|
200
|
+
"port": 17300,
|
|
201
|
+
"path": "playground",
|
|
202
|
+
"for": "web-components",
|
|
203
|
+
"note": "Devkit e2e test bed — a component scaffolded with `kmwc new`, linked to this repo."
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/web-components-devkit",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc02",
|
|
4
4
|
"description": "Shared dev, example-site and build infrastructure for Keenmate web components: Vite preset, examples kit (chapter nav, copy-link, controls store, shared styles), Playwright preset and the `kmwc` CLI (sync, doctor, check-vars, new, readme).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"readme:check": "node src/cli/kmwc.js readme --check",
|
|
48
48
|
"test": "node --test \"test/**/*.test.js\"",
|
|
49
49
|
"test:e2e": "cd playground && npx playwright test",
|
|
50
|
+
"check:contract": "base-contract-check src templates playground",
|
|
50
51
|
"playground": "cd playground && vite",
|
|
51
52
|
"playground:build": "cd playground && vite build --mode examples",
|
|
52
53
|
"playground:lib": "cd playground && vite build"
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
}
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
69
|
+
"@keenmate/base-css-variables": "^2.0.0-rc07",
|
|
68
70
|
"typescript": "^5.9.3",
|
|
69
71
|
"vite": "^7.3.7"
|
|
70
72
|
},
|
package/src/cli/commands/new.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* kmwc new web-colorpicker --prefix cpk [--title "Web Color Picker"] [--tag web-colorpicker]
|
|
5
5
|
* [--dir ../web-colorpicker] [--port 13200] [--minimal] [--register] [--offline]
|
|
6
|
+
* kmwc new web-editor --adopt --dir /tmp/web-editor [--prefix …]
|
|
6
7
|
*
|
|
7
8
|
* Default: a small working sample (a toggle with `label` / `active` inputs and
|
|
8
9
|
* a `change` event) to rename or replace. `--minimal`: an empty skeleton — no
|
|
@@ -21,6 +22,12 @@
|
|
|
21
22
|
* `.claude/commands/publish.md`, examples, fixtures + e2e specs, then the
|
|
22
23
|
* `kmwc sync` files. The port block (dev = block, image = +10) is the next free
|
|
23
24
|
* one in the component range (see PORT_RANGES in config.js; `--port` overrides).
|
|
25
|
+
*
|
|
26
|
+
* `--adopt` is for MIGRATING a component that is already registered: the id must
|
|
27
|
+
* be in components.json, the port block (and prefix, unless the entry has none —
|
|
28
|
+
* then `--prefix`) come from its entry, the npm-name check is skipped (the package
|
|
29
|
+
* may exist), and the registry is left alone. Scaffold into a scratch `--dir`,
|
|
30
|
+
* then move the component's own code into the generated shape.
|
|
24
31
|
*/
|
|
25
32
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
26
33
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
@@ -184,7 +191,7 @@ export function generatePackageJson({ componentId, packageName, tagline, tag, pr
|
|
|
184
191
|
export async function run(args) {
|
|
185
192
|
const requestedName = args._[0];
|
|
186
193
|
if (!requestedName) {
|
|
187
|
-
throw new Error('usage: kmwc new <name> --prefix <2-4 letters> [--title "…"] [--tag …] [--dir …] [--port …] [--register] [--offline]');
|
|
194
|
+
throw new Error('usage: kmwc new <name> --prefix <2-4 letters> [--title "…"] [--tag …] [--dir …] [--port …] [--register] [--offline] [--adopt]');
|
|
188
195
|
}
|
|
189
196
|
|
|
190
197
|
const unscopedName = getUnscopedName(requestedName);
|
|
@@ -201,24 +208,35 @@ export async function run(args) {
|
|
|
201
208
|
|
|
202
209
|
// ── intake ───────────────────────────────────────────────────────────────
|
|
203
210
|
const registry = getRegistry();
|
|
204
|
-
|
|
211
|
+
const isAdopting = !!args.adopt;
|
|
212
|
+
const adoptedEntry = registry.components.find((entry) => entry.id === componentId);
|
|
213
|
+
if (isAdopting && !adoptedEntry) throw new Error(`--adopt: "${componentId}" is not in components.json — register it, or drop --adopt for a new component`);
|
|
214
|
+
if (!isAdopting && adoptedEntry) throw new Error(`"${componentId}" is already in components.json (migrating it? add --adopt)`);
|
|
215
|
+
if (isAdopting && args.register) throw new Error('--adopt never edits the registry — drop --register');
|
|
205
216
|
if (existsSync(targetPath) && readdirSync(targetPath).length) throw new Error(`${targetPath} exists and is not empty`);
|
|
206
217
|
|
|
207
|
-
|
|
218
|
+
if (isAdopting && adoptedEntry.prefix && args.prefix && args.prefix !== adoptedEntry.prefix) {
|
|
219
|
+
throw new Error(`--prefix ${args.prefix} ≠ the registered prefix "${adoptedEntry.prefix}"`);
|
|
220
|
+
}
|
|
221
|
+
const prefix = (isAdopting && adoptedEntry.prefix) || args.prefix || generatePrefix(componentId);
|
|
208
222
|
if (!prefix) {
|
|
209
223
|
throw new Error(
|
|
210
224
|
`cannot derive a CSS prefix for "${componentId}" — is "${featureName}" one English word or several? ` +
|
|
211
225
|
`Pass --prefix (initials of each word, e.g. daterangepicker → drp; component-intake.md "C — CSS prefix derivation").`
|
|
212
226
|
);
|
|
213
227
|
}
|
|
214
|
-
|
|
228
|
+
// An adopted component's own registered prefix isn't a clash with itself.
|
|
229
|
+
const reservedPrefixes = getReservedPrefixes(registry);
|
|
230
|
+
if (isAdopting && adoptedEntry.prefix) reservedPrefixes.delete(adoptedEntry.prefix);
|
|
231
|
+
const prefixError = checkPrefix(prefix, reservedPrefixes);
|
|
215
232
|
if (prefixError) throw new Error(prefixError);
|
|
216
233
|
|
|
217
|
-
if (!args.offline && checkIsPackageNameTaken(packageName)) throw new Error(`${packageName} already exists on npm (component-intake.md "B")`);
|
|
234
|
+
if (!isAdopting && !args.offline && checkIsPackageNameTaken(packageName)) throw new Error(`${packageName} already exists on npm (component-intake.md "B")`);
|
|
218
235
|
|
|
219
|
-
|
|
236
|
+
if (isAdopting && args.port && Number(args.port) !== adoptedEntry.port) throw new Error(`--port ${args.port} ≠ the registered port block ${adoptedEntry.port}`);
|
|
237
|
+
const devPort = isAdopting ? adoptedEntry.port : Number(args.port) || getNextPortBlock(registry);
|
|
220
238
|
if (devPort % PORT_BLOCK_SIZE !== 0) throw new Error(`--port ${devPort} is not a port block (a multiple of ${PORT_BLOCK_SIZE})`);
|
|
221
|
-
const portOwner = registry.components.find((entry) => entry.port === devPort);
|
|
239
|
+
const portOwner = registry.components.find((entry) => entry.port === devPort && entry.id !== componentId);
|
|
222
240
|
if (portOwner) throw new Error(`port block ${devPort} belongs to ${portOwner.id} — omit --port to take the next free block`);
|
|
223
241
|
const corePackageJsonPath = join(DEVKIT_ROOT, '..', 'web-components-core', 'package.json');
|
|
224
242
|
const coreVersion = existsSync(corePackageJsonPath) ? getJsonFile(corePackageJsonPath).version : '1.0.0-rc12';
|
|
@@ -279,7 +297,10 @@ export async function run(args) {
|
|
|
279
297
|
repo: `https://github.com/keenmate/${componentId}`,
|
|
280
298
|
port: devPort,
|
|
281
299
|
};
|
|
282
|
-
if (
|
|
300
|
+
if (isAdopting) {
|
|
301
|
+
if (!adoptedEntry.prefix) console.log(`
|
|
302
|
+
The registry entry has no prefix — add "prefix": "${prefix}" to its components.json entry.`);
|
|
303
|
+
} else if (args.register) {
|
|
283
304
|
const registryPath = join(DEVKIT_ROOT, 'components.json');
|
|
284
305
|
const registryJson = getJsonFile(registryPath);
|
|
285
306
|
const lastComponentIndex = registryJson.components.map((entry) => entry.kind).lastIndexOf('component');
|
package/src/cli/kmwc.js
CHANGED
|
@@ -41,6 +41,7 @@ Usage: kmwc <command> [options]
|
|
|
41
41
|
new <name> Scaffold a new component (e.g. \`kmwc new web-colorpicker --prefix cpk\`).
|
|
42
42
|
--title "…" --tag … --prefix … --dir … --port … --register
|
|
43
43
|
--minimal: empty skeleton (no inputs/events) instead of the sample
|
|
44
|
+
--adopt: an already registered component (migration scratch scaffold)
|
|
44
45
|
readme [--check] Regenerate the README catalogue from components.json (devkit repo).
|
|
45
46
|
|
|
46
47
|
Global: --root <dir> Act on <dir> instead of the current directory.`);
|
package/src/config.d.ts
CHANGED
|
@@ -123,6 +123,8 @@ export interface KmwcConfig {
|
|
|
123
123
|
|
|
124
124
|
export const DEVKIT_ROOT: string;
|
|
125
125
|
export const DEFAULT_RULEBOOK_PATH: string;
|
|
126
|
+
/** The nearest `<ancestor>/BlissFramework/guidelines/web-components` above `startPath`, or null. */
|
|
127
|
+
export function getRulebookPath(startPath: string): string | null;
|
|
126
128
|
export const LEGACY_FILES: string[];
|
|
127
129
|
export const PACKAGE_LEAK_RULES: Array<{ pattern: RegExp; label: string }>;
|
|
128
130
|
export const PACKAGE_FILES_EXCLUSIONS: string[];
|
package/src/config.js
CHANGED
|
@@ -19,8 +19,28 @@ import { createRequire } from 'node:module';
|
|
|
19
19
|
/** Absolute path of the devkit package root (this repo). */
|
|
20
20
|
export const DEVKIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const RULEBOOK_RELATIVE_PATH = join('BlissFramework', 'guidelines', 'web-components');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The nearest BlissFramework rulebook checkout: `<ancestor>/BlissFramework/
|
|
26
|
+
* guidelines/web-components` for the first ancestor of `startPath` that has one
|
|
27
|
+
* (e.g. C:\Git\KM\web-editor → C:\Git\BlissFramework\…), or null. Searching
|
|
28
|
+
* from the component (not a fixed path from the devkit) is what keeps it
|
|
29
|
+
* working when the devkit is installed into node_modules from npm.
|
|
30
|
+
*/
|
|
31
|
+
export function getRulebookPath(startPath) {
|
|
32
|
+
let directory = resolve(startPath);
|
|
33
|
+
for (;;) {
|
|
34
|
+
const candidatePath = join(directory, RULEBOOK_RELATIVE_PATH);
|
|
35
|
+
if (existsSync(candidatePath)) return candidatePath;
|
|
36
|
+
const parentDirectory = dirname(directory);
|
|
37
|
+
if (parentDirectory === directory) return null;
|
|
38
|
+
directory = parentDirectory;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Fallback rulebook location: the nearest checkout above the devkit, else next to the KM folder. */
|
|
43
|
+
export const DEFAULT_RULEBOOK_PATH = getRulebookPath(DEVKIT_ROOT) ?? resolve(DEVKIT_ROOT, '..', '..', RULEBOOK_RELATIVE_PATH);
|
|
24
44
|
|
|
25
45
|
/** Files the devkit now owns — finding one of these in a component is a doctor error. */
|
|
26
46
|
export const LEGACY_FILES = [
|
|
@@ -235,7 +255,9 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
235
255
|
checkMode: cssVariables.checkMode || 'declared',
|
|
236
256
|
},
|
|
237
257
|
rulebook: {
|
|
238
|
-
path:
|
|
258
|
+
path:
|
|
259
|
+
process.env.KMWC_RULEBOOK_PATH ||
|
|
260
|
+
(rulebook.path ? resolve(root, rulebook.path) : getRulebookPath(root) ?? DEFAULT_RULEBOOK_PATH),
|
|
239
261
|
skippedChecks: rulebook.skippedChecks || [],
|
|
240
262
|
},
|
|
241
263
|
sync: {
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
button.secondary,
|
|
177
177
|
button.btn-secondary {
|
|
178
178
|
background: var(--kmwc-neutral-color);
|
|
179
|
-
color: var(--kmwc-text-color-on-
|
|
179
|
+
color: var(--kmwc-text-color-on-neutral);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
button.secondary:hover,
|
|
@@ -195,11 +195,11 @@
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
button.btn-danger {
|
|
198
|
-
background: var(--kmwc-danger-color);
|
|
199
|
-
color: var(--kmwc-text-color-on-
|
|
198
|
+
background: var(--kmwc-danger-fill-color);
|
|
199
|
+
color: var(--kmwc-text-color-on-danger);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
button.btn-danger:hover {
|
|
203
|
-
background:
|
|
203
|
+
background: var(--kmwc-danger-fill-color-hover);
|
|
204
204
|
}
|
|
205
205
|
}
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
.kmwc-index__badge {
|
|
94
94
|
display: inline-block;
|
|
95
95
|
background: var(--kmwc-badge-new-bg);
|
|
96
|
-
color: var(--kmwc-text-color-on-
|
|
96
|
+
color: var(--kmwc-text-color-on-badge-new);
|
|
97
97
|
padding: 0.15rem 0.5rem;
|
|
98
98
|
border-radius: var(--kmwc-radius-sm);
|
|
99
99
|
font-size: 0.65rem;
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
component together — demo pages demonstrate cross-component theming;
|
|
10
10
|
• with no --base-* set, the page still looks right, in light and dark.
|
|
11
11
|
|
|
12
|
+
Fallbacks are the contract's own (@keenmate/base-css-variables): an input
|
|
13
|
+
falls back to its default, a derived token to the contract formula on the
|
|
14
|
+
--kmwc-* tokens, so a page looks the same with and without the contract.
|
|
15
|
+
`npm run check:contract` (base-contract-check) enforces it.
|
|
16
|
+
|
|
12
17
|
Unlike component variables (theme-container.md: never on :root), page
|
|
13
18
|
tokens DO live on :root — the page is the scope here. Feature files read
|
|
14
19
|
only --kmwc-* tokens; colour literals live in this file alone.
|
|
@@ -38,42 +43,42 @@
|
|
|
38
43
|
/* ======================================================================
|
|
39
44
|
COLORS — ACCENT
|
|
40
45
|
====================================================================== */
|
|
41
|
-
--kmwc-accent-color: var(--base-
|
|
42
|
-
--kmwc-accent-end-color:
|
|
43
|
-
--kmwc-accent-color-hover: var(--base-
|
|
44
|
-
--kmwc-accent-color-active: var(--base-
|
|
45
|
-
--kmwc-accent-light-bg: var(--base-
|
|
46
|
-
--kmwc-accent-light-bg-hover: var(--base-
|
|
46
|
+
--kmwc-accent-color: var(--base-primary-color, #3b82f6);
|
|
47
|
+
--kmwc-accent-end-color: oklch(from var(--kmwc-accent-color) calc(l - 0.12) c calc(h + 25)); /* header gradient end */
|
|
48
|
+
--kmwc-accent-color-hover: var(--base-primary-color-hover, light-dark(color-mix(in srgb, black 15%, var(--kmwc-accent-color)), color-mix(in srgb, white 15%, var(--kmwc-accent-color))));
|
|
49
|
+
--kmwc-accent-color-active: var(--base-primary-color-active, light-dark(color-mix(in srgb, black 30%, var(--kmwc-accent-color)), color-mix(in srgb, white 30%, var(--kmwc-accent-color))));
|
|
50
|
+
--kmwc-accent-light-bg: var(--base-primary-bg-subtle, light-dark(color-mix(in srgb, var(--kmwc-accent-color) 10%, transparent), color-mix(in srgb, var(--kmwc-accent-color) 20%, transparent)));
|
|
51
|
+
--kmwc-accent-light-bg-hover: var(--base-primary-bg-subtle-hover, light-dark(color-mix(in srgb, var(--kmwc-accent-color) 15%, transparent), color-mix(in srgb, var(--kmwc-accent-color) 30%, transparent)));
|
|
47
52
|
--kmwc-accent-border-color: color-mix(in srgb, var(--kmwc-accent-color) 40%, transparent);
|
|
48
53
|
--kmwc-accent-shadow-color: color-mix(in srgb, var(--kmwc-accent-color) 35%, transparent);
|
|
49
|
-
--kmwc-text-color-on-accent: var(--base-text-color-
|
|
54
|
+
--kmwc-text-color-on-accent: var(--base-on-primary-text-color, color(from color(from var(--kmwc-accent-color) srgb calc(0.21 * r * r * (0.4 + r * (0.82 - 0.22 * r)) + 0.72 * g * g * (0.4 + g * (0.82 - 0.22 * g)) + 0.07 * b * b * (0.4 + b * (0.82 - 0.22 * b))) 0 0) srgb clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1)));
|
|
50
55
|
--kmwc-header-bg: linear-gradient(135deg, var(--kmwc-accent-color) 0%, var(--kmwc-accent-end-color) 100%);
|
|
51
56
|
|
|
52
57
|
/* ======================================================================
|
|
53
58
|
COLORS — TEXT
|
|
54
59
|
====================================================================== */
|
|
55
|
-
--kmwc-text-color-1: var(--base-text-color-1, light-dark(#
|
|
56
|
-
--kmwc-text-color-2: var(--base-text-color-2,
|
|
57
|
-
--kmwc-text-color-3: var(--base-text-color-3,
|
|
58
|
-
--kmwc-text-color-4: var(--base-text-color-4,
|
|
60
|
+
--kmwc-text-color-1: var(--base-text-color-1, light-dark(#334155, #f1f5f9));
|
|
61
|
+
--kmwc-text-color-2: var(--base-text-color-2, color-mix(in srgb, var(--kmwc-text-color-1) 75%, var(--kmwc-main-bg)));
|
|
62
|
+
--kmwc-text-color-3: var(--base-text-color-3, color-mix(in srgb, var(--kmwc-text-color-1) 55%, var(--kmwc-main-bg)));
|
|
63
|
+
--kmwc-text-color-4: var(--base-text-color-4, color-mix(in srgb, var(--kmwc-text-color-1) 40%, var(--kmwc-main-bg)));
|
|
59
64
|
|
|
60
65
|
/* ======================================================================
|
|
61
66
|
COLORS — SURFACE
|
|
62
67
|
====================================================================== */
|
|
63
|
-
--kmwc-page-bg: light-dark(#
|
|
64
|
-
--kmwc-main-bg: var(--base-main-bg, light-dark(#ffffff, #
|
|
65
|
-
--kmwc-elevated-bg: var(--base-elevated-bg, light-dark(
|
|
66
|
-
--kmwc-hover-bg: var(--base-hover-bg, color-mix(in
|
|
67
|
-
--kmwc-border-color: var(--base-border-color, light-dark(
|
|
68
|
-
--kmwc-border-color-strong: light-dark(
|
|
69
|
-
--kmwc-shadow-color: light-dark(rgba(0, 0, 0, 0.
|
|
68
|
+
--kmwc-page-bg: var(--base-page-bg, light-dark(#f8f9fa, #0f172a));
|
|
69
|
+
--kmwc-main-bg: var(--base-main-bg, light-dark(#ffffff, #1e293b));
|
|
70
|
+
--kmwc-elevated-bg: var(--base-elevated-bg, light-dark(oklch(from var(--kmwc-main-bg) calc(l - 0.03) c h), oklch(from var(--kmwc-main-bg) calc(l + 0.06) c h)));
|
|
71
|
+
--kmwc-hover-bg: var(--base-hover-bg, light-dark(color-mix(in oklch, oklch(from var(--kmwc-main-bg) calc(l - 0.45) c h) 14%, transparent), color-mix(in oklch, oklch(from var(--kmwc-main-bg) calc(l + 0.45) c h) 28%, transparent)));
|
|
72
|
+
--kmwc-border-color: var(--base-border-color, light-dark(oklch(from var(--kmwc-main-bg) calc(l - 0.096) c h), oklch(from var(--kmwc-main-bg) calc(l + 0.192) c h)));
|
|
73
|
+
--kmwc-border-color-strong: var(--base-input-border-color, light-dark(color-mix(in srgb, var(--kmwc-text-color-1) 45%, var(--kmwc-main-bg)), color-mix(in srgb, var(--kmwc-text-color-1) 35%, var(--kmwc-main-bg)))); /* control outline */
|
|
74
|
+
--kmwc-shadow-color: var(--base-shadow-color, light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.4)));
|
|
70
75
|
|
|
71
76
|
/* ======================================================================
|
|
72
77
|
COLORS — INPUTS & CODE
|
|
73
78
|
====================================================================== */
|
|
74
|
-
--kmwc-input-bg: var(--base-input-bg,
|
|
75
|
-
--kmwc-input-color: var(--base-input-color, var(--kmwc-text-color-1));
|
|
76
|
-
--kmwc-input-border: var(--base-input-border, 1px solid
|
|
79
|
+
--kmwc-input-bg: var(--base-input-bg, var(--kmwc-main-bg));
|
|
80
|
+
--kmwc-input-color: var(--base-input-text-color, var(--kmwc-text-color-1));
|
|
81
|
+
--kmwc-input-border: var(--base-input-border, 1px solid var(--kmwc-border-color-strong));
|
|
77
82
|
--kmwc-code-bg: light-dark(#2d3748, #0d1117);
|
|
78
83
|
--kmwc-code-text-color: #e2e8f0;
|
|
79
84
|
--kmwc-code-comment-color: #8b949e;
|
|
@@ -90,18 +95,27 @@
|
|
|
90
95
|
/* ======================================================================
|
|
91
96
|
COLORS — SEMANTIC
|
|
92
97
|
====================================================================== */
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
--kmwc-
|
|
96
|
-
--kmwc-
|
|
98
|
+
/* -color = the role as text on a surface (notes, feedback); the danger
|
|
99
|
+
button fills with --kmwc-danger-fill-color. */
|
|
100
|
+
--kmwc-success-color: var(--base-success-text-color, light-dark(#155724, #34d399));
|
|
101
|
+
--kmwc-warning-color: var(--base-warning-text-color, light-dark(#856404, #fbbf24));
|
|
102
|
+
--kmwc-danger-color: var(--base-danger-text-color, light-dark(#721c24, #f87171));
|
|
103
|
+
--kmwc-info-color: var(--base-info-text-color, light-dark(#0c5460, #22d3ee));
|
|
104
|
+
--kmwc-danger-fill-color: var(--base-danger-color, #dc2626);
|
|
105
|
+
--kmwc-danger-fill-color-hover: var(--base-danger-color-hover, light-dark(color-mix(in srgb, black 15%, var(--kmwc-danger-fill-color)), color-mix(in srgb, white 15%, var(--kmwc-danger-fill-color))));
|
|
106
|
+
--kmwc-text-color-on-danger: var(--base-on-danger-text-color, color(from color(from var(--kmwc-danger-fill-color) srgb calc(0.21 * r * r * (0.4 + r * (0.82 - 0.22 * r)) + 0.72 * g * g * (0.4 + g * (0.82 - 0.22 * g)) + 0.07 * b * b * (0.4 + b * (0.82 - 0.22 * b))) 0 0) srgb clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1)));
|
|
97
107
|
--kmwc-neutral-color: light-dark(#6b7280, #9ca3af);
|
|
98
108
|
--kmwc-badge-new-bg: light-dark(#e53e3e, #f56565);
|
|
109
|
+
/* Text on the neutral / badge fills: the contract's on-colour formula on
|
|
110
|
+
their own background, not on the accent (a light primary would turn it black). */
|
|
111
|
+
--kmwc-text-color-on-neutral: color(from color(from var(--kmwc-neutral-color) srgb calc(0.21 * r * r * (0.4 + r * (0.82 - 0.22 * r)) + 0.72 * g * g * (0.4 + g * (0.82 - 0.22 * g)) + 0.07 * b * b * (0.4 + b * (0.82 - 0.22 * b))) 0 0) srgb clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1));
|
|
112
|
+
--kmwc-text-color-on-badge-new: color(from color(from var(--kmwc-badge-new-bg) srgb calc(0.21 * r * r * (0.4 + r * (0.82 - 0.22 * r)) + 0.72 * g * g * (0.4 + g * (0.82 - 0.22 * g)) + 0.07 * b * b * (0.4 + b * (0.82 - 0.22 * b))) 0 0) srgb clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1));
|
|
99
113
|
|
|
100
114
|
/* ======================================================================
|
|
101
115
|
TYPOGRAPHY & SHAPE
|
|
102
116
|
====================================================================== */
|
|
103
|
-
--kmwc-font-family: var(--base-font-family, -apple-system, BlinkMacSystemFont,
|
|
104
|
-
--kmwc-mono-font-family:
|
|
117
|
+
--kmwc-font-family: var(--base-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
118
|
+
--kmwc-mono-font-family: var(--base-font-family-mono, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
|
|
105
119
|
--kmwc-radius-lg: 12px;
|
|
106
120
|
--kmwc-radius-md: 8px;
|
|
107
121
|
--kmwc-radius-sm: 6px;
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* kmwc:layout page (default) | bare (no header / container — e.g. the index)
|
|
19
19
|
* kmwc:entry component entry script; default /<library.entry>, "none" to skip
|
|
20
20
|
* kmwc:body-class classes for <body> (e.g. dark-output)
|
|
21
|
-
* <link> and <style> elements anywhere in a fragment move into <head
|
|
21
|
+
* <link> and <style> elements anywhere in a fragment move into <head>
|
|
22
|
+
* (not those inside a <script> — there they are strings, e.g. shadow-DOM styles).
|
|
22
23
|
* Full documents (<html …>) are left as they are — pages migrate one at a time.
|
|
23
24
|
*
|
|
24
25
|
* Pure string work on purpose: unit-testable, no HTML parser dependency. The
|
|
@@ -42,6 +43,9 @@ const HEAD_ELEMENT_PATTERN = /<link\b[^>]*>\s*|<style\b[^>]*>[\s\S]*?<\/style>\s
|
|
|
42
43
|
const ATTRIBUTE_PATTERN = /([a-zA-Z][\w-]*)(?:\s*=\s*"([^"]*)")?/g;
|
|
43
44
|
|
|
44
45
|
const HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
46
|
+
// Comments plus whole <script> elements: a <style> / <link> inside a script is a string
|
|
47
|
+
// (shadow-DOM styles, an SVG's own <style>), never a page element to move into <head>.
|
|
48
|
+
const COMMENT_OR_SCRIPT_PATTERN = /<!--[\s\S]*?-->|<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
45
49
|
// A full document STARTS with a doctype or <html> (after a BOM, whitespace, comments). Matching
|
|
46
50
|
// `<html>` anywhere would misread a fragment whose script or code sample mentions it.
|
|
47
51
|
const DOCUMENT_START_PATTERN = /^?(?:\s|<!--[\s\S]*?-->)*<(?:!doctype\b|html[\s>])/i;
|
|
@@ -50,12 +54,12 @@ const DOCUMENT_START_PATTERN = /^?(?:\s|<!--[\s\S]*?-->)*<(?:!doctype\b|html[
|
|
|
50
54
|
* Apply `mapper` to the text OUTSIDE HTML comments only, so a usage example in
|
|
51
55
|
* a comment (`<!-- <kmwc-include src="…"> -->`) is never expanded.
|
|
52
56
|
*/
|
|
53
|
-
export function mapOutsideComments(html, mapper) {
|
|
57
|
+
export function mapOutsideComments(html, mapper, skippedPattern = HTML_COMMENT_PATTERN) {
|
|
54
58
|
let mappedHtml = '';
|
|
55
59
|
let lastIndex = 0;
|
|
56
|
-
for (const
|
|
57
|
-
mappedHtml += mapper(html.slice(lastIndex,
|
|
58
|
-
lastIndex =
|
|
60
|
+
for (const skipped of html.matchAll(skippedPattern)) {
|
|
61
|
+
mappedHtml += mapper(html.slice(lastIndex, skipped.index)) + skipped[0];
|
|
62
|
+
lastIndex = skipped.index + skipped[0].length;
|
|
59
63
|
}
|
|
60
64
|
return mappedHtml + mapper(html.slice(lastIndex));
|
|
61
65
|
}
|
|
@@ -165,11 +169,14 @@ export function renderLayout(fragmentHtml, { config, pagePath }) {
|
|
|
165
169
|
if (layout === 'page' && !pageMeta.title) throw new Error(`kmwc: ${pagePath}: a fragment page needs <meta name="kmwc:title" content="…">`);
|
|
166
170
|
|
|
167
171
|
const headElements = [];
|
|
168
|
-
const bodyContent = mapOutsideComments(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
})
|
|
172
|
+
const bodyContent = mapOutsideComments(
|
|
173
|
+
htmlWithoutMeta,
|
|
174
|
+
(text) =>
|
|
175
|
+
text.replace(HEAD_ELEMENT_PATTERN, (element) => {
|
|
176
|
+
headElements.push(` ${element.trim()}`);
|
|
177
|
+
return '';
|
|
178
|
+
}),
|
|
179
|
+
COMMENT_OR_SCRIPT_PATTERN
|
|
173
180
|
);
|
|
174
181
|
|
|
175
182
|
const entry = pageMeta.entry ?? `/${config.library.entry.replace(/^\.?\//, '')}`;
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
{ "name": "base-main-bg", "required": false, "usage": "Component surface" },
|
|
7
7
|
{ "name": "base-text-color-1", "required": false, "usage": "Label text" },
|
|
8
8
|
{ "name": "base-border-color", "required": false, "usage": "Button border" },
|
|
9
|
-
{ "name": "base-
|
|
10
|
-
{ "name": "base-text-color
|
|
11
|
-
{ "name": "base-hover-bg", "required": false, "usage": "Hover background (falls back to
|
|
9
|
+
{ "name": "base-primary-color", "required": false, "usage": "Active state fill, focus outline" },
|
|
10
|
+
{ "name": "base-on-primary-text-color", "required": false, "usage": "Label text in the active state" },
|
|
11
|
+
{ "name": "base-hover-bg", "required": false, "usage": "Hover background (falls back to the contract formula on --{{PREFIX}}-bg)" },
|
|
12
12
|
{ "name": "base-font-family", "required": false, "usage": "Font family" },
|
|
13
13
|
{ "name": "base-font-size-base", "required": false, "usage": "Font size (multiplier × --{{PREFIX}}-rem)" },
|
|
14
14
|
{ "name": "base-border-radius-md", "required": false, "usage": "Corner radius (multiplier × --{{PREFIX}}-rem)" },
|
|
@@ -27,7 +27,7 @@ Combines a `--base-*` override (all components) with a `--{{PREFIX}}-*` one
|
|
|
27
27
|
|
|
28
28
|
```html
|
|
29
29
|
<style>
|
|
30
|
-
:root { --base-
|
|
30
|
+
:root { --base-primary-color: #10b981; }
|
|
31
31
|
</style>
|
|
32
32
|
<{{TAG}} label="Dark" data-theme="dark" style="--{{PREFIX}}-root-border-radius: 999px"></{{TAG}}>
|
|
33
33
|
```
|
|
@@ -9,18 +9,18 @@ test.describe('<{{TAG}}> dark mode', () => {
|
|
|
9
9
|
test('defaults to light, per-instance data-theme="dark" goes dark', async ({ page }) => {
|
|
10
10
|
await page.goto('/test/dark-mode.html');
|
|
11
11
|
expect(await getHostBackground(page, '#light-default')).toBe('rgb(255, 255, 255)');
|
|
12
|
-
expect(await getHostBackground(page, '#dark-instance')).toBe('rgb(
|
|
12
|
+
expect(await getHostBackground(page, '#dark-instance')).toBe('rgb(30, 41, 59)');
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
test('an ancestor data-theme="dark" reaches the component', async ({ page }) => {
|
|
16
16
|
await page.goto('/test/dark-mode.html');
|
|
17
|
-
expect(await getHostBackground(page, '#dark-ancestor')).toBe('rgb(
|
|
17
|
+
expect(await getHostBackground(page, '#dark-ancestor')).toBe('rgb(30, 41, 59)');
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
test('a page color-scheme of dark is inherited', async ({ page }) => {
|
|
21
21
|
await page.goto('/test/dark-mode.html');
|
|
22
22
|
await page.evaluate(() => (document.documentElement.style.colorScheme = 'dark'));
|
|
23
|
-
expect(await getHostBackground(page, '#light-default')).toBe('rgb(
|
|
23
|
+
expect(await getHostBackground(page, '#light-default')).toBe('rgb(30, 41, 59)');
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('--base-* themes the component', async ({ page }) => {
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
|
|
19
19
|
<div class="card">
|
|
20
20
|
<h2>TH02 · Variable overrides</h2>
|
|
21
|
-
<p><code>--base-
|
|
21
|
+
<p><code>--base-primary-color</code> themes every Keenmate component in a subtree; <code>--{{PREFIX}}-accent-color</code> one instance.</p>
|
|
22
22
|
<kmwc-demo>
|
|
23
|
-
<div style="--base-
|
|
24
|
-
<{{TAG}} id="th-base" label="--base-
|
|
23
|
+
<div style="--base-primary-color: #10b981">
|
|
24
|
+
<{{TAG}} id="th-base" label="--base-primary-color" active></{{TAG}}>
|
|
25
25
|
<{{TAG}} id="th-local" label="--{{PREFIX}}-accent-color" active style="--{{PREFIX}}-accent-color: #8b5cf6"></{{TAG}}>
|
|
26
26
|
</div>
|
|
27
27
|
</kmwc-demo>
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
CSS CUSTOM PROPERTIES (:host level)
|
|
3
3
|
==============================================================================
|
|
4
4
|
Every --{{PREFIX}}-* variable reads through the cross-component --base-* hook
|
|
5
|
-
with a standalone fallback
|
|
6
|
-
|
|
5
|
+
with a standalone fallback: an input falls back to the contract default, a
|
|
6
|
+
derived token to the contract formula on this component's own tokens
|
|
7
|
+
(base-contract-check). Colours use light-dark(), so the component follows
|
|
8
|
+
the page's color-scheme. `color-scheme` is deliberately
|
|
7
9
|
NOT declared here (it would shadow the page's) — see dark-mode.css.
|
|
8
10
|
|
|
9
11
|
Override from outside the shadow DOM:
|
|
@@ -21,12 +23,12 @@
|
|
|
21
23
|
/* ==========================================================================
|
|
22
24
|
COLORS
|
|
23
25
|
========================================================================== */
|
|
24
|
-
--{{PREFIX}}-bg: var(--base-main-bg, light-dark(#ffffff, #
|
|
25
|
-
--{{PREFIX}}-text-color-1: var(--base-text-color-1, light-dark(#
|
|
26
|
-
--{{PREFIX}}-border-color: var(--base-border-color, light-dark(
|
|
27
|
-
--{{PREFIX}}-accent-color: var(--base-
|
|
28
|
-
--{{PREFIX}}-text-color-on-accent: var(--base-text-color-
|
|
29
|
-
--{{PREFIX}}-hover-bg: var(--base-hover-bg, color-mix(in
|
|
26
|
+
--{{PREFIX}}-bg: var(--base-main-bg, light-dark(#ffffff, #1e293b));
|
|
27
|
+
--{{PREFIX}}-text-color-1: var(--base-text-color-1, light-dark(#334155, #f1f5f9));
|
|
28
|
+
--{{PREFIX}}-border-color: var(--base-border-color, light-dark(oklch(from var(--{{PREFIX}}-bg) calc(l - 0.096) c h), oklch(from var(--{{PREFIX}}-bg) calc(l + 0.192) c h)));
|
|
29
|
+
--{{PREFIX}}-accent-color: var(--base-primary-color, #3b82f6);
|
|
30
|
+
--{{PREFIX}}-text-color-on-accent: var(--base-on-primary-text-color, color(from color(from var(--{{PREFIX}}-accent-color) srgb calc(0.21 * r * r * (0.4 + r * (0.82 - 0.22 * r)) + 0.72 * g * g * (0.4 + g * (0.82 - 0.22 * g)) + 0.07 * b * b * (0.4 + b * (0.82 - 0.22 * b))) 0 0) srgb clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1) clamp(0, (0.34 - r) * 1e9, 1)));
|
|
31
|
+
--{{PREFIX}}-hover-bg: var(--base-hover-bg, light-dark(color-mix(in oklch, oklch(from var(--{{PREFIX}}-bg) calc(l - 0.45) c h) 14%, transparent), color-mix(in oklch, oklch(from var(--{{PREFIX}}-bg) calc(l + 0.45) c h) 28%, transparent)));
|
|
30
32
|
|
|
31
33
|
/* ==========================================================================
|
|
32
34
|
TYPOGRAPHY & SHAPE
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
CSS CUSTOM PROPERTIES (:host level)
|
|
3
3
|
==============================================================================
|
|
4
4
|
Every --{{PREFIX}}-* variable reads through the cross-component --base-* hook
|
|
5
|
-
with a standalone fallback
|
|
6
|
-
|
|
5
|
+
with a standalone fallback: an input falls back to the contract default, a
|
|
6
|
+
derived token to the contract formula on this component's own tokens
|
|
7
|
+
(base-contract-check). Colours use light-dark(), so the component follows
|
|
8
|
+
the page's color-scheme. `color-scheme` is deliberately
|
|
7
9
|
NOT declared here (it would shadow the page's) — see dark-mode.css.
|
|
8
10
|
|
|
9
11
|
Override from outside the shadow DOM:
|
|
@@ -14,9 +16,9 @@
|
|
|
14
16
|
/* Unitless --base-* sizes multiply through --{{PREFIX}}-rem. */
|
|
15
17
|
--{{PREFIX}}-rem: 10px;
|
|
16
18
|
|
|
17
|
-
--{{PREFIX}}-bg: var(--base-main-bg, light-dark(#ffffff, #
|
|
18
|
-
--{{PREFIX}}-text-color-1: var(--base-text-color-1, light-dark(#
|
|
19
|
-
--{{PREFIX}}-border-color: var(--base-border-color, light-dark(
|
|
19
|
+
--{{PREFIX}}-bg: var(--base-main-bg, light-dark(#ffffff, #1e293b));
|
|
20
|
+
--{{PREFIX}}-text-color-1: var(--base-text-color-1, light-dark(#334155, #f1f5f9));
|
|
21
|
+
--{{PREFIX}}-border-color: var(--base-border-color, light-dark(oklch(from var(--{{PREFIX}}-bg) calc(l - 0.096) c h), oklch(from var(--{{PREFIX}}-bg) calc(l + 0.192) c h)));
|
|
20
22
|
|
|
21
23
|
--{{PREFIX}}-font-family: var(--base-font-family, inherit);
|
|
22
24
|
--{{PREFIX}}-font-size-base: calc(var(--base-font-size-base, 1.6) * var(--{{PREFIX}}-rem));
|