@keenmate/web-components-devkit 1.0.0-rc05 → 1.0.0-rc06
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 +48 -0
- package/README.md +32 -10
- package/package.json +1 -1
- package/src/cli/args.js +32 -9
- package/src/cli/commands/check-vars.js +4 -0
- package/src/cli/commands/doctor.js +32 -15
- package/src/cli/kmwc.js +71 -33
- package/src/cli/sync-plan.js +2 -2
- package/src/config.d.ts +14 -4
- package/src/config.js +13 -2
- package/src/examples/css/content.css +41 -0
- package/src/examples/css/form-controls.css +4 -0
- package/src/examples/css/widgets.css +12 -4
- package/src/vite/index.js +8 -1
- package/templates/claude-block.md +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-rc06] - 2026-11-01 [PUBLISHED]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`<kbd>` and `<mark>`** — every component documents a keyboard map and most highlight
|
|
8
|
+
search matches, and pages either left them in the browser's look (core's Interaction page:
|
|
9
|
+
18 bare `<kbd>`s) or styled them locally (core's Search page). A `<kbd>` now looks like the
|
|
10
|
+
spotlight's key hints and a `<mark>` like its match highlight. Widgets page WG08.
|
|
11
|
+
- **Field widths** — a native field is ~20 characters wide, so pages set inline widths (core:
|
|
12
|
+
14). A number field is now 7rem (six digits and the spin buttons; a `.form-group` still
|
|
13
|
+
stretches it), and `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a short
|
|
14
|
+
field. `.push-end` moves an item to the end of a `.controls` row. Form controls page FC12.
|
|
15
|
+
- **`kmwc.library: false`** — for a repo that builds its package itself (core: `tsc`) or ships
|
|
16
|
+
none. Plain `vite build`, the devkit's library mode, refuses to run instead of emptying
|
|
17
|
+
`dist/`; `vite build --mode examples` still builds the site, and sync no longer proposes a
|
|
18
|
+
`build: vite build` script. `{ "shouldBuild": false, "entry": … }` keeps the pages' entry.
|
|
19
|
+
- **`kmwc.cssVariables: false`** (or `checkMode: "off"`) — sync stopped re-adding `check:vars`
|
|
20
|
+
only when there was no manifest; now it can be switched off (core builds its variable names
|
|
21
|
+
by interpolation, which the checker can't see). `kmwc check-vars` then skips.
|
|
22
|
+
- **Doctor knows libraries** — a repo whose registry entry (or `kmwc.kind`) is `library` runs
|
|
23
|
+
only the rulebook suites that apply to it: naming-conventions, readme-structure,
|
|
24
|
+
publish-command, example-pages. The CSS, Custom Elements Manifest, overlay and
|
|
25
|
+
component-structure suites check what a custom element ships; on core they gave 12 errors
|
|
26
|
+
and 4 "suite could not run" warnings. No prefix warning for a library.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`kmwc <command> --help` ran the command** — only a top-level `--help` was handled and
|
|
31
|
+
unknown flags were ignored, so `kmwc sync --help` did a full sync (it overwrote core's
|
|
32
|
+
Makefile). `--help` / `-h` after any command now prints that command's usage and exits 0,
|
|
33
|
+
and an unknown option is an error (exit 2). Every command declares its flags; a boolean
|
|
34
|
+
flag no longer swallows the next word as its value.
|
|
35
|
+
- **Inline `<code>` widened a phone page** — a long identifier has no break opportunity, so
|
|
36
|
+
`resolvePresentation(mode, env, map)` in a heading made core's page 350px wide at 320px.
|
|
37
|
+
Inline code now breaks anywhere; code in a `<pre>` keeps its lines.
|
|
38
|
+
- **Muted log text failed contrast on the light panel** — `--muted` lines, the seq / time /
|
|
39
|
+
source cells and the empty placeholder used `--kmwc-text-color-3`: 2.83:1 on the light
|
|
40
|
+
`.log-panel` in the light theme (WCAG AA wants 4.5:1). They now use `--kmwc-text-color-2`.
|
|
41
|
+
A spec checks every tone on both panels in both themes.
|
|
42
|
+
- **`.css-readout` sat on the demo above it** — it had no top margin (core carried 8 inline
|
|
43
|
+
margins); it now has 0.75rem.
|
|
44
|
+
|
|
45
|
+
- **Timeline log lines on a phone** — the cells (`.log-line__seq`, `__time`, `__source`,
|
|
46
|
+
`__name`) never shrink and the row never wrapped, so on a narrow panel, or with a long
|
|
47
|
+
source name, the detail got the few pixels left over: one letter per line. The row now
|
|
48
|
+
wraps, and the detail wants ~14rem beside the cells; with less room it drops under them at
|
|
49
|
+
full width. Wide panels are unchanged. A cell is capped at the panel width.
|
|
50
|
+
|
|
3
51
|
## [1.0.0-rc05] - 2026-11-01 [PUBLISHED]
|
|
4
52
|
|
|
5
53
|
### Changed
|
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-rc06
|
|
23
|
+
|
|
24
|
+
- **Example pages — `<kbd>`, `<mark>`, field widths and `.push-end`** — Every component documents a keyboard map and most highlight search matches. Pages either left `<kbd>` in the browser's look (core's Interaction page had 18 bare keys) or styled `<mark>` themselves. A `<kbd>` now looks like the spotlight's key hints and a `<mark>` like its match highlight. Native fields are about 20 characters wide, so pages set inline widths; core had 14. A number field is now 7rem by default. `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a short field, and a `.form-group` still stretches it to the row. `.push-end` moves an item to the end of a `.controls` row. See the playground's WG08 and FC12.
|
|
25
|
+
- **Example pages — phone and contrast fixes** — Inline `<code>` now breaks anywhere. Before, a long identifier such as `resolvePresentation(mode, env, map)` in a heading made a page 350px wide on a 320px screen; code inside a `<pre>` keeps its lines. Muted log text on the light `.log-panel` (`--muted` lines, the seq / time / source cells, the empty placeholder) measured 2.83:1 in the light theme. It now uses `--kmwc-text-color-2`, and a spec checks every tone on both panels in both themes against WCAG AA's 4.5:1. Timeline rows wrap, so on a phone or with a long source name the detail drops under the cells instead of showing one letter per line. `.css-readout` gets a top margin after the demo it measures.
|
|
26
|
+
- **CLI — `kmwc <command> --help` no longer runs the command** — Only a top-level `--help` was handled, and unknown flags were silently ignored, so `kmwc sync --help` did a full sync and overwrote core's hand-written Makefile. Every command now lists the flags it accepts. `--help` / `-h` after any command prints that command's usage and exits 0, and an unknown option is an error (exit 2). A boolean flag no longer takes the next word as its value.
|
|
27
|
+
- **Libraries — `library: false`, `cssVariables: false`, and a doctor that knows the kind** — A repo like web-components-core builds its package with `tsc`. A plain `vite build` (the devkit's library mode) would empty its `dist/`, so `"kmwc": { "library": false }` now makes it refuse to run, while `vite build --mode examples` still builds the site. `"cssVariables": false` turns off the variables-manifest check, so sync stops re-adding `check:vars`. Doctor reads the registry kind (or `kmwc.kind`): a `library` runs only naming-conventions, readme-structure, publish-command and example-pages. On core that drops 12 errors and 4 "suite could not run" warnings that didn't apply to a library.
|
|
28
|
+
|
|
22
29
|
## What's New in v1.0.0-rc05
|
|
23
30
|
|
|
24
31
|
- **Log panels — one tone set for every log line** — web-components-core's example pages each coloured their log lines with their own classes (`.warn`, `.lv-error`, `data-kind="hook"` …), four schemes mixing severity with "what happened". A `.log-line` now takes one of five tones — `--ok`, `--info`, `--warn`, `--err`, `--muted`, the same names as `.pill` and `.status-dot` (which gain `--info` / `--muted`) — and `--action` marks a "▶ you clicked X" line. Kinds such as hook or callback map onto those tones; the kind itself goes in a cell as text. On `.log-panel--dark` the tones switch to fixed light tints (`--kmwc-console-ok|info|warn|err-color`), so they stay readable on the console in both themes, while a light `.log-panel` follows the page theme. Every line keeps its line breaks and indentation.
|
|
@@ -26,12 +33,6 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
26
33
|
- **`.event-log` — the legacy console now renders as `.log-panel--dark`** — Many component pages (web-grid, web-dropzone, web-treeview, the showcases) still use `.event-log` / `.event-entry` / `.event-time` / `.event-name`, which differed slightly from the newer panel (grey base text, 0.8rem, a 200px cap, no columns). It is now the same panel, and an `.event-entry` holding `.event-time` / `.event-name` is a timeline row with an aligned time column and tones, so old and new logs look the same. Other `.event-log` markup — lines without `.event-entry`, page-own classes — only gets the panel look, so existing layouts don't break.
|
|
27
34
|
- **Small helpers — `.mono`, `td.is-highlight`, `.check-list`** — `.mono` sets the monospace font without the code chip, and `textarea.mono` makes a JSON or config editor. `td.is-highlight` (or `th`) tints one table cell, where only whole rows could be highlighted before. `.check-list` stacks checkbox / radio options one per line, a long label wrapping beside its box, with room for a trailing `.pill`. The playground shows everything on the Widgets (WG01–WG07), Form controls (FC11) and Tables (TB06) pages.
|
|
28
35
|
|
|
29
|
-
## What's New in v1.0.0-rc04
|
|
30
|
-
|
|
31
|
-
- **Example-page grids — wide content scrolls instead of widening the page** — A `<pre>` or table inside a `.grid`, `.grid-2` or `.card-grid` item still pushed the page wider than a phone: grid items default to `min-width: auto` (their min-content), so the single `1fr` column at ≤ 768 px grew to fit the content instead of letting it scroll. Every item of the three grids now gets `min-width: 0`, the same rule pure-css puts on its `.pc-col-*` columns. Found by two agents migrating web-components-core's example pages, which drop their page-local workaround with this release.
|
|
32
|
-
- **`.controls` — long `<code>` in a button wraps on a phone** — rc03 wrapped `<code>` only inside `.controls` labels, so a button whose label was an unbroken API name (`setAttribute("…")`) still overflowed the row. The wrap rule (`overflow-wrap: anywhere`) now covers any `<code>` in a `.controls` row, and a `.controls` button is capped at the row width. An e2e spec injects both cases at 390 px and checks that the page stays inside the screen.
|
|
33
|
-
- **Playground — a Widgets page for the kit's opt-in helpers** — `.log-panel` (light and `--dark`), the legacy `.event-log`, `.pill` / `.status-dot` tones, `.display-sliders` and `.css-readout` had no demo, so their look was only checked on component pages. The new `examples-widgets.html` (WG01–WG04) shows them as they are today, including the gaps the coming log-tone work fixes (no line tones, leading spaces collapsing in a log line), and gets its own e2e spec.
|
|
34
|
-
|
|
35
36
|
## Catalogue
|
|
36
37
|
|
|
37
38
|
<!-- kmwc:catalogue:start — generated by `npm run readme`, do not edit inside -->
|
|
@@ -142,7 +143,8 @@ Every field is optional. Defaults are derived from the package.
|
|
|
142
143
|
"tagline": "Feature-rich multiselect …", // default: package.json description
|
|
143
144
|
"tag": "web-multiselect",
|
|
144
145
|
"prefix": "ms", // reserved CSS prefix (rulebook base-variables.md)
|
|
145
|
-
"
|
|
146
|
+
"kind": "component", // default: the components.json entry's kind
|
|
147
|
+
"library": { // false: no library build (see below)
|
|
146
148
|
"entry": "src/index.ts", // default
|
|
147
149
|
"globalName": "MultiSelect", // UMD global
|
|
148
150
|
"fileName": "multiselect", // → dist/multiselect.js + .umd.js
|
|
@@ -165,7 +167,7 @@ Every field is optional. Defaults are derived from the package.
|
|
|
165
167
|
"runner": "podman",
|
|
166
168
|
"packageJsonPaths": ["package.json"] // monorepos: list workspace package.jsons too
|
|
167
169
|
},
|
|
168
|
-
"cssVariables": {
|
|
170
|
+
"cssVariables": { // false: no variables manifest, no check:vars
|
|
169
171
|
"sources": ["src/css"], // dirs (stylesheets) or explicit files (any type)
|
|
170
172
|
"internalVariables": ["ms-input-current-width"], // runtime internals, not theming knobs
|
|
171
173
|
"checkMode": "declared" // or "consumed" (var() reads)
|
|
@@ -179,6 +181,16 @@ Every field is optional. Defaults are derived from the package.
|
|
|
179
181
|
}
|
|
180
182
|
```
|
|
181
183
|
|
|
184
|
+
**A library** (registry `kind: "library"`, e.g. web-components-core) builds its
|
|
185
|
+
package itself (`tsc`) or ships none. `"library": false` (or `{ "shouldBuild":
|
|
186
|
+
false, "entry": … }` to keep the pages' entry script) makes plain `vite build`
|
|
187
|
+
refuse to run — it is the devkit's library mode and would empty `dist/` — while
|
|
188
|
+
`vite build --mode examples` still builds the site, and `kmwc sync` no longer
|
|
189
|
+
proposes a `build: vite build` script. `"cssVariables": false` turns off the
|
|
190
|
+
manifest check (no `check:vars` script, `kmwc check-vars` skips), for variables
|
|
191
|
+
the checker can't see, such as names built by string interpolation. Doctor runs
|
|
192
|
+
only the rulebook suites that apply to a library (see below).
|
|
193
|
+
|
|
182
194
|
### Vite preset
|
|
183
195
|
|
|
184
196
|
```ts
|
|
@@ -265,7 +277,8 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
265
277
|
`.demo-area`, `.demo-card`, `.code-block`, `.note(.warning|.info|.success|.error)`,
|
|
266
278
|
`.grid`/`.card-grid`, `.reference-table` (+ `.table-*` modifiers), `.log-panel`, `.pill`,
|
|
267
279
|
`.status-dot`, `.control-group`, buttons (`.btn-secondary`, `.btn-outline`,
|
|
268
|
-
`.btn-danger`)
|
|
280
|
+
`.btn-danger`), `<kbd>` keys and `<mark>` search matches (the spotlight's look),
|
|
281
|
+
`.push-end` (to the end of a flex row) — while everything the kit **generates** is prefixed BEM
|
|
269
282
|
(`kmwc-chapter-nav__item--active`, `kmwc-copy-link--copied`, …).
|
|
270
283
|
- **Form controls** look the same on every page without a class: text fields,
|
|
271
284
|
selects and list boxes (`multiple` / `size`), textareas, checkboxes, radios,
|
|
@@ -275,7 +288,9 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
275
288
|
selects and buttons share `--kmwc-control-height`, so a row of them lines up
|
|
276
289
|
in any container, not only `.controls`. The rules are zero-specificity
|
|
277
290
|
(`:where()`); `kmwc-native` on an element or an ancestor keeps the browser's
|
|
278
|
-
own controls (a light-DOM component that draws its own inputs).
|
|
291
|
+
own controls (a light-DOM component that draws its own inputs). A number field
|
|
292
|
+
is 7rem wide; `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a
|
|
293
|
+
short field without an inline style. The
|
|
279
294
|
playground's `examples-forms.html` shows them all.
|
|
280
295
|
- **Tables:** any `<table>` gets a header fill, row lines and a hover tint;
|
|
281
296
|
`.reference-table` is the full-width, fully bordered API table. Modifiers
|
|
@@ -351,8 +366,12 @@ kmwc check-release --tag t latest ⇔ release version, rc ⇔ -rcN, version n
|
|
|
351
366
|
kmwc new web-<name> scaffold a component (--prefix, --title, --tag, --dir, --port, --minimal, --register)
|
|
352
367
|
--adopt: scaffold an ALREADY registered component (its port/prefix) to migrate into
|
|
353
368
|
kmwc readme [--check] regenerate this README's catalogue (devkit repo only)
|
|
369
|
+
kmwc <command> --help that command's usage — never runs the command
|
|
354
370
|
```
|
|
355
371
|
|
|
372
|
+
An unknown option is an error (exit 2), never ignored — a typo can't run a
|
|
373
|
+
command with its defaults.
|
|
374
|
+
|
|
356
375
|
Generated files start with a `GENERATED by kmwc sync` header. Repo-specific
|
|
357
376
|
make targets go in `Makefile.local`, which the generated Makefile includes at
|
|
358
377
|
the end. Per-developer overrides (`DOCKER_RUNNER`, `IMAGE_PORT`) go in
|
|
@@ -364,6 +383,9 @@ and the e2e suite before building and publishing.
|
|
|
364
383
|
base-variables, color-scheme, readme-structure, custom-elements-manifest,
|
|
365
384
|
responsive-overlay, publish-command, example-pages) with `bash`, passing `kmwc.prefix`, and
|
|
366
385
|
reports each `FAIL` as an error pointing at the matching `.checks.md` entry.
|
|
386
|
+
A `library` runs only naming-conventions, readme-structure, publish-command
|
|
387
|
+
and example-pages; the others check what a custom element ships (its CSS,
|
|
388
|
+
manifest, overlay).
|
|
367
389
|
When the rulebook isn't checked out (or there's no bash) the suites are
|
|
368
390
|
skipped with a warning — the rulebook stays the single source of truth; the
|
|
369
391
|
devkit never copies its rules.
|
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-rc06",
|
|
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",
|
package/src/cli/args.js
CHANGED
|
@@ -1,16 +1,39 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Flag parser: --flag, --key value, --key=value, positionals. `flagSpec` names
|
|
3
|
+
* every flag a command takes — `booleans` never take a value, `values` always
|
|
4
|
+
* do. Anything else is an error: an ignored typo (or `--help` before it was
|
|
5
|
+
* handled) used to run the command for real.
|
|
6
|
+
*
|
|
7
|
+
* @param {string[]} args
|
|
8
|
+
* @param {{ booleans?: string[], values?: string[] }} [flagSpec]
|
|
9
|
+
*/
|
|
10
|
+
export function parseArgs(args, flagSpec = {}) {
|
|
11
|
+
const booleanFlags = new Set(flagSpec.booleans || []);
|
|
12
|
+
const valueFlags = new Set(['root', ...(flagSpec.values || [])]);
|
|
3
13
|
const out = { _: [] };
|
|
4
14
|
for (let i = 0; i < args.length; i++) {
|
|
5
|
-
const
|
|
6
|
-
if (!
|
|
7
|
-
out._.push(
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
if (!arg.startsWith('-') || arg === '-') {
|
|
17
|
+
out._.push(arg);
|
|
8
18
|
continue;
|
|
9
19
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
if (!arg.startsWith('--')) throw new Error(`unknown option "${arg}"`);
|
|
21
|
+
const [key, inline] = arg.slice(2).split(/=(.*)/s);
|
|
22
|
+
if (booleanFlags.has(key)) {
|
|
23
|
+
if (inline !== undefined) throw new Error(`--${key} takes no value`);
|
|
24
|
+
out[key] = true;
|
|
25
|
+
} else if (valueFlags.has(key)) {
|
|
26
|
+
if (inline !== undefined) out[key] = inline;
|
|
27
|
+
else if (args[i + 1] !== undefined && !args[i + 1].startsWith('--')) out[key] = args[++i];
|
|
28
|
+
else throw new Error(`--${key} needs a value`);
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(`unknown option "--${key}"`);
|
|
31
|
+
}
|
|
14
32
|
}
|
|
15
33
|
return out;
|
|
16
34
|
}
|
|
35
|
+
|
|
36
|
+
/** True when the arguments ask for help (`--help` or `-h` anywhere). */
|
|
37
|
+
export function checkIsHelpRequested(args) {
|
|
38
|
+
return args.some((arg) => arg === '--help' || arg === '-h');
|
|
39
|
+
}
|
|
@@ -68,6 +68,10 @@ export function checkManifestVariables({ cssText, manifest, prefix, internalVari
|
|
|
68
68
|
export async function run(args) {
|
|
69
69
|
const config = getKmwcConfig(args.root);
|
|
70
70
|
const isStrict = !!args.strict;
|
|
71
|
+
if (!config.cssVariables.isEnabled) {
|
|
72
|
+
console.log('kmwc check-vars: off (kmwc.cssVariables is false) — nothing to check.');
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
71
75
|
const manifestPath = resolve(config.root, config.cssVariables.manifest);
|
|
72
76
|
if (!existsSync(manifestPath)) {
|
|
73
77
|
console.log(`kmwc check-vars: no ${config.cssVariables.manifest} — nothing to check.`);
|
|
@@ -25,21 +25,33 @@ const LEGACY_PAGE_REFERENCES = ['examples-shared.css', 'examples-chapter-nav.js'
|
|
|
25
25
|
const VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.mjs', 'vite.config.js'];
|
|
26
26
|
const PLAYWRIGHT_CONFIG_FILES = ['playwright.config.ts', 'playwright.config.mjs', 'playwright.config.js'];
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Rulebook suites in the rulebook's canonical reading order; the prefix is
|
|
30
|
+
* passed where the script takes one. `isComponentOnly` suites check what a
|
|
31
|
+
* custom element ships (its CSS, manifest, overlay) — a `library` (registry
|
|
32
|
+
* kind, or kmwc.kind) skips them.
|
|
33
|
+
*/
|
|
29
34
|
export const RULEBOOK_SUITES = [
|
|
30
|
-
{ name: 'component-structure', isPrefixed: false },
|
|
31
|
-
{ name: 'naming-conventions', isPrefixed: true },
|
|
32
|
-
{ name: 'css-structure', isPrefixed: true },
|
|
33
|
-
{ name: 'theme-container', isPrefixed: true },
|
|
34
|
-
{ name: 'base-variables', isPrefixed: true },
|
|
35
|
-
{ name: 'color-scheme', isPrefixed: true },
|
|
36
|
-
{ name: 'readme-structure', isPrefixed: false },
|
|
37
|
-
{ name: 'custom-elements-manifest', isPrefixed: false },
|
|
38
|
-
{ name: 'responsive-overlay', isPrefixed: true },
|
|
39
|
-
{ name: 'publish-command', isPrefixed: false },
|
|
40
|
-
{ name: 'example-pages', isPrefixed: false },
|
|
35
|
+
{ name: 'component-structure', isPrefixed: false, isComponentOnly: true },
|
|
36
|
+
{ name: 'naming-conventions', isPrefixed: true, isComponentOnly: false },
|
|
37
|
+
{ name: 'css-structure', isPrefixed: true, isComponentOnly: true },
|
|
38
|
+
{ name: 'theme-container', isPrefixed: true, isComponentOnly: true },
|
|
39
|
+
{ name: 'base-variables', isPrefixed: true, isComponentOnly: true },
|
|
40
|
+
{ name: 'color-scheme', isPrefixed: true, isComponentOnly: true },
|
|
41
|
+
{ name: 'readme-structure', isPrefixed: false, isComponentOnly: false },
|
|
42
|
+
{ name: 'custom-elements-manifest', isPrefixed: false, isComponentOnly: true },
|
|
43
|
+
{ name: 'responsive-overlay', isPrefixed: true, isComponentOnly: true },
|
|
44
|
+
{ name: 'publish-command', isPrefixed: false, isComponentOnly: false },
|
|
45
|
+
{ name: 'example-pages', isPrefixed: false, isComponentOnly: false },
|
|
41
46
|
];
|
|
42
47
|
|
|
48
|
+
const LIBRARY_KIND = 'library';
|
|
49
|
+
|
|
50
|
+
/** The suites that apply to a repo of `kind`. Pure — exported for tests. */
|
|
51
|
+
export function getRulebookSuites(kind) {
|
|
52
|
+
return RULEBOOK_SUITES.filter((suite) => kind !== LIBRARY_KIND || !suite.isComponentOnly);
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
function getMajorVersion(versionRange) {
|
|
44
56
|
const match = String(versionRange || '').match(/(\d+)/);
|
|
45
57
|
return match ? Number(match[1]) : null;
|
|
@@ -85,7 +97,7 @@ export function checkComponent(config, doctorConfig = {}) {
|
|
|
85
97
|
} else {
|
|
86
98
|
addPass('config', 'package.json#kmwc present');
|
|
87
99
|
if (!config.packageJson.kmwc.title) addWarning('config', 'kmwc.title not set (falls back to a title-cased id)', 'set kmwc.title');
|
|
88
|
-
if (!config.prefix) addWarning('config', 'kmwc.prefix not set — rulebook suites run without a CSS prefix', 'set kmwc.prefix to the prefix reserved in base-variables.md');
|
|
100
|
+
if (!config.prefix && config.kind !== LIBRARY_KIND) addWarning('config', 'kmwc.prefix not set — rulebook suites run without a CSS prefix', 'set kmwc.prefix to the prefix reserved in base-variables.md');
|
|
89
101
|
if (!config.packageJson.kmwc.ports?.dev) addWarning('config', 'kmwc.ports.dev not set (falls back to 5173)', 'take the port from the devkit components.json');
|
|
90
102
|
}
|
|
91
103
|
|
|
@@ -248,7 +260,12 @@ function checkRulebook(config, { addPass, addWarning, addError }) {
|
|
|
248
260
|
return;
|
|
249
261
|
}
|
|
250
262
|
const skippedSuites = new Set(config.rulebook.skippedChecks);
|
|
251
|
-
|
|
263
|
+
const suites = getRulebookSuites(config.kind);
|
|
264
|
+
if (suites.length < RULEBOOK_SUITES.length) {
|
|
265
|
+
const componentOnlySuiteNames = RULEBOOK_SUITES.filter((suite) => !suites.includes(suite)).map((suite) => suite.name);
|
|
266
|
+
addPass('rulebook', `${config.kind}: component-only suites not run (${componentOnlySuiteNames.join(', ')})`);
|
|
267
|
+
}
|
|
268
|
+
for (const suite of suites) {
|
|
252
269
|
if (skippedSuites.has(suite.name)) continue;
|
|
253
270
|
if (!existsSync(join(config.rulebook.path, `${suite.name}.checks.sh`))) continue;
|
|
254
271
|
const suiteResult = runRulebookSuite(config, suite);
|
|
@@ -280,7 +297,7 @@ export async function run(args) {
|
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
const LEVEL_ICONS = { pass: '✓', warning: '⚠', error: '✗' };
|
|
283
|
-
console.log(`kmwc doctor — ${config.packageName} (${config.root})\n`);
|
|
300
|
+
console.log(`kmwc doctor — ${config.packageName}${config.kind === 'component' ? '' : ` [${config.kind}]`} (${config.root})\n`);
|
|
284
301
|
for (const result of results) {
|
|
285
302
|
if (result.level === 'pass' && !args.verbose) continue;
|
|
286
303
|
console.log(`${LEVEL_ICONS[result.level]} [${result.checkName}] ${result.message}${result.fixHint ? `\n → ${result.fixHint}` : ''}`);
|
package/src/cli/kmwc.js
CHANGED
|
@@ -8,59 +8,97 @@
|
|
|
8
8
|
* kmwc check-release --tag <tag> gate a publish (version ⇔ dist-tag, not yet on npm)
|
|
9
9
|
* kmwc new <name> [options] scaffold a new component repo next to the devkit
|
|
10
10
|
* kmwc readme [--check] regenerate the devkit README catalogue (devkit repo only)
|
|
11
|
+
* kmwc <command> --help that command's usage (never runs it)
|
|
11
12
|
*
|
|
12
13
|
* All commands act on the current directory (override with --root <dir>).
|
|
13
|
-
* Each command module exports `run(args)` returning the process exit code
|
|
14
|
+
* Each command module exports `run(args)` returning the process exit code; an
|
|
15
|
+
* unknown option is an error (exit 2), never ignored.
|
|
14
16
|
*/
|
|
15
17
|
import { argv, exit } from 'node:process';
|
|
16
|
-
import { parseArgs } from './args.js';
|
|
18
|
+
import { parseArgs, checkIsHelpRequested } from './args.js';
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
readme: () => import('./commands/readme.js'),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function renderUsage() {
|
|
28
|
-
console.log(`kmwc — Keenmate web-components devkit
|
|
29
|
-
|
|
30
|
-
Usage: kmwc <command> [options]
|
|
31
|
-
|
|
32
|
-
sync [--check] Regenerate Dockerfile, nginx.conf, Makefile, .dockerignore,
|
|
20
|
+
/** Every command: its module, the flags it takes and its usage text. */
|
|
21
|
+
const COMMANDS = {
|
|
22
|
+
sync: {
|
|
23
|
+
load: () => import('./commands/sync.js'),
|
|
24
|
+
flags: { booleans: ['check'] },
|
|
25
|
+
usage: `sync [--check] Regenerate Dockerfile, nginx.conf, Makefile, .dockerignore,
|
|
33
26
|
the CLAUDE.md devkit block, .gitignore entries and missing
|
|
34
|
-
standard scripts. --check: report drift, write nothing
|
|
35
|
-
|
|
27
|
+
standard scripts. --check: report drift, write nothing.`,
|
|
28
|
+
},
|
|
29
|
+
doctor: {
|
|
30
|
+
load: () => import('./commands/doctor.js'),
|
|
31
|
+
flags: { booleans: ['json', 'verbose', 'no-rulebook', 'no-pack'] },
|
|
32
|
+
usage: `doctor [--json] Check this repo against the devkit standard and the
|
|
36
33
|
BlissFramework rulebook (*.checks.sh), and that npm pack ships no
|
|
37
|
-
dev-only files. --verbose, --no-rulebook, --no-pack
|
|
38
|
-
|
|
39
|
-
check-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
dev-only files. --verbose, --no-rulebook, --no-pack.`,
|
|
35
|
+
},
|
|
36
|
+
'check-vars': {
|
|
37
|
+
load: () => import('./commands/check-vars.js'),
|
|
38
|
+
flags: { booleans: ['strict'] },
|
|
39
|
+
usage: `check-vars [--strict] Validate component-variables.manifest.json against the CSS.`,
|
|
40
|
+
},
|
|
41
|
+
'check-release': {
|
|
42
|
+
load: () => import('./commands/check-release.js'),
|
|
43
|
+
flags: { booleans: ['offline'], values: ['tag'] },
|
|
44
|
+
usage: `check-release --tag <t> Gate a publish: latest ⇔ release version, rc ⇔ -rcN,
|
|
45
|
+
version not on npm yet. --offline skips the npm lookup.`,
|
|
46
|
+
},
|
|
47
|
+
new: {
|
|
48
|
+
load: () => import('./commands/new.js'),
|
|
49
|
+
flags: { booleans: ['register', 'minimal', 'adopt', 'offline'], values: ['title', 'tagline', 'tag', 'prefix', 'dir', 'port'] },
|
|
50
|
+
usage: `new <name> Scaffold a new component (e.g. \`kmwc new web-colorpicker --prefix cpk\`).
|
|
51
|
+
--title "…" --tagline "…" --tag … --prefix … --dir … --port …
|
|
52
|
+
--register --offline
|
|
43
53
|
--minimal: empty skeleton (no inputs/events) instead of the sample
|
|
44
|
-
--adopt: an already registered component (migration scratch scaffold)
|
|
45
|
-
|
|
54
|
+
--adopt: an already registered component (migration scratch scaffold)`,
|
|
55
|
+
},
|
|
56
|
+
readme: {
|
|
57
|
+
load: () => import('./commands/readme.js'),
|
|
58
|
+
flags: { booleans: ['check'] },
|
|
59
|
+
usage: `readme [--check] Regenerate the README catalogue from components.json (devkit repo).`,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const GLOBAL_USAGE = `Global: --root <dir> Act on <dir> instead of the current directory.
|
|
64
|
+
--help, -h Show the usage (of one command: kmwc <command> --help).`;
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
function renderUsage(commandName) {
|
|
67
|
+
if (commandName) {
|
|
68
|
+
console.log(`Usage: kmwc ${COMMANDS[commandName].usage}\n\n${GLOBAL_USAGE}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const commandLines = Object.values(COMMANDS).map((command) => ` ${command.usage}`).join('\n');
|
|
72
|
+
console.log(`kmwc — Keenmate web-components devkit\n\nUsage: kmwc <command> [options]\n\n${commandLines}\n\n${GLOBAL_USAGE}`);
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
async function main() {
|
|
51
76
|
const [commandName, ...commandArgs] = argv.slice(2);
|
|
52
77
|
if (!commandName || ['help', '--help', '-h'].includes(commandName)) {
|
|
53
|
-
renderUsage();
|
|
78
|
+
renderUsage(COMMANDS[commandArgs[0]] ? commandArgs[0] : undefined);
|
|
54
79
|
return 0;
|
|
55
80
|
}
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
81
|
+
const command = COMMANDS[commandName];
|
|
82
|
+
if (!command) {
|
|
58
83
|
console.error(`kmwc: unknown command "${commandName}"\n`);
|
|
59
84
|
renderUsage();
|
|
60
85
|
return 2;
|
|
61
86
|
}
|
|
62
|
-
|
|
63
|
-
|
|
87
|
+
// Help never runs the command (`kmwc sync --help` once overwrote a Makefile).
|
|
88
|
+
if (checkIsHelpRequested(commandArgs)) {
|
|
89
|
+
renderUsage(commandName);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
let args;
|
|
93
|
+
try {
|
|
94
|
+
args = parseArgs(commandArgs, command.flags);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(`kmwc ${commandName}: ${error.message}\n`);
|
|
97
|
+
renderUsage(commandName);
|
|
98
|
+
return 2;
|
|
99
|
+
}
|
|
100
|
+
const commandModule = await command.load();
|
|
101
|
+
return (await commandModule.run(args)) ?? 0;
|
|
64
102
|
}
|
|
65
103
|
|
|
66
104
|
main().then(
|
package/src/cli/sync-plan.js
CHANGED
|
@@ -69,14 +69,14 @@ const hasAnyFile = (root, fileNames) => fileNames.some((fileName) => existsSync(
|
|
|
69
69
|
export function getStandardScripts(config) {
|
|
70
70
|
const scripts = {
|
|
71
71
|
dev: 'vite',
|
|
72
|
-
build: 'vite build',
|
|
72
|
+
...(config.library.shouldBuild ? { build: 'vite build' } : {}),
|
|
73
73
|
'build:examples': 'vite build --mode examples',
|
|
74
74
|
'preview:examples': 'vite preview --mode examples',
|
|
75
75
|
'clean:dist': generateRemoveCommand(['dist']),
|
|
76
76
|
clean: generateRemoveCommand(['dist', config.examples.outDir]),
|
|
77
77
|
doctor: 'kmwc doctor',
|
|
78
78
|
};
|
|
79
|
-
if (existsSync(join(config.root, config.cssVariables.manifest))) scripts['check:vars'] = 'kmwc check-vars';
|
|
79
|
+
if (config.cssVariables.isEnabled && existsSync(join(config.root, config.cssVariables.manifest))) scripts['check:vars'] = 'kmwc check-vars';
|
|
80
80
|
if (existsSync(join(config.root, CEM_CONFIG_FILE))) scripts.analyze = 'cem analyze';
|
|
81
81
|
if (hasAnyFile(config.root, PLAYWRIGHT_CONFIG_FILES)) {
|
|
82
82
|
Object.assign(scripts, {
|
package/src/config.d.ts
CHANGED
|
@@ -10,7 +10,12 @@ export interface KmwcPackageConfig {
|
|
|
10
10
|
tag?: string;
|
|
11
11
|
/** Reserved CSS prefix (base-variables.md "Existing prefixes"), e.g. `ms`. */
|
|
12
12
|
prefix?: string;
|
|
13
|
-
library
|
|
13
|
+
/** Registry kind; defaults to the components.json entry's kind (else `component`). A `library` runs only the doctor checks that apply to it. */
|
|
14
|
+
kind?: 'component' | 'library' | 'showcase' | 'docs' | 'test';
|
|
15
|
+
/** The library build (`vite build`). `false` = the repo builds its package itself or ships none: plain `vite build` refuses to run, only `--mode examples` builds. */
|
|
16
|
+
library?: false | {
|
|
17
|
+
/** `false` = same as `library: false`, keeping `entry` for the example pages. */
|
|
18
|
+
shouldBuild?: boolean;
|
|
14
19
|
entry?: string;
|
|
15
20
|
/** UMD global name. */
|
|
16
21
|
globalName?: string;
|
|
@@ -51,14 +56,15 @@ export interface KmwcPackageConfig {
|
|
|
51
56
|
packageJsonPaths?: string[];
|
|
52
57
|
installCommand?: string;
|
|
53
58
|
};
|
|
54
|
-
|
|
59
|
+
/** `false` (or `checkMode: 'off'`): no variables manifest check — sync adds no `check:vars`, `kmwc check-vars` skips. */
|
|
60
|
+
cssVariables?: false | {
|
|
55
61
|
manifest?: string;
|
|
56
62
|
/** Directories (stylesheets scanned) or explicit files (any type, e.g. a built bundle). */
|
|
57
63
|
sources?: string[];
|
|
58
64
|
/** Prefixed names (without `--`) that are runtime internals, not theming knobs. */
|
|
59
65
|
internalVariables?: string[];
|
|
60
66
|
/** `declared`: manifest ⇔ `--x:` declarations; `consumed`: manifest ⇔ `var(--x)` reads. */
|
|
61
|
-
checkMode?: 'declared' | 'consumed';
|
|
67
|
+
checkMode?: 'declared' | 'consumed' | 'off';
|
|
62
68
|
};
|
|
63
69
|
rulebook?: {
|
|
64
70
|
/** Path to BlissFramework `guidelines/web-components` (env KMWC_RULEBOOK_PATH wins). */
|
|
@@ -75,6 +81,7 @@ export interface KmwcConfig {
|
|
|
75
81
|
packageJson: Record<string, any>;
|
|
76
82
|
id: string;
|
|
77
83
|
packageName: string;
|
|
84
|
+
kind: string;
|
|
78
85
|
version: string;
|
|
79
86
|
title: string;
|
|
80
87
|
tagline: string;
|
|
@@ -85,6 +92,7 @@ export interface KmwcConfig {
|
|
|
85
92
|
author: string;
|
|
86
93
|
license: string;
|
|
87
94
|
library: {
|
|
95
|
+
shouldBuild: boolean;
|
|
88
96
|
entry: string;
|
|
89
97
|
globalName: string;
|
|
90
98
|
fileName: string;
|
|
@@ -118,7 +126,8 @@ export interface KmwcConfig {
|
|
|
118
126
|
manifest: string;
|
|
119
127
|
sources: string[];
|
|
120
128
|
internalVariables: string[];
|
|
121
|
-
checkMode: 'declared' | 'consumed';
|
|
129
|
+
checkMode: 'declared' | 'consumed' | 'off';
|
|
130
|
+
isEnabled: boolean;
|
|
122
131
|
};
|
|
123
132
|
rulebook: { path: string; skippedChecks: string[] };
|
|
124
133
|
sync: { skippedFiles: string[] };
|
|
@@ -137,6 +146,7 @@ export const PORT_RANGES: Array<{ kinds: string[]; firstBlock: number; lastBlock
|
|
|
137
146
|
export const PREVIEW_PORT_OFFSET: number;
|
|
138
147
|
export const IMAGE_PORT_OFFSET: number;
|
|
139
148
|
export function getPortsForBlock(portBlock: number): { dev: number; preview: number; image: number };
|
|
149
|
+
export function getRegistryKind(id: string, registry?: { components: Array<{ id: string; kind: string }> }): string;
|
|
140
150
|
export function getPortRange(kind: string): { kinds: string[]; firstBlock: number; lastBlock: number };
|
|
141
151
|
export function getNextPortBlock(registry?: { components: Array<Record<string, any>> }, kind?: string): number;
|
|
142
152
|
export function getPeerModule(root: string, moduleId: string): any;
|
package/src/config.js
CHANGED
|
@@ -139,6 +139,11 @@ export function getRegistry() {
|
|
|
139
139
|
return getJsonFile(join(DEVKIT_ROOT, 'components.json'));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/** The registry kind of `id` (`component`, `library` …); `component` when it isn't registered. */
|
|
143
|
+
export function getRegistryKind(id, registry = getRegistry()) {
|
|
144
|
+
return registry.components.find((entry) => entry.id === id)?.kind || 'component';
|
|
145
|
+
}
|
|
146
|
+
|
|
142
147
|
/** Strip the npm scope: `@keenmate/web-multiselect` → `web-multiselect`. */
|
|
143
148
|
export function getUnscopedName(packageName) {
|
|
144
149
|
return String(packageName || '').replace(/^@[^/]+\//, '');
|
|
@@ -182,13 +187,16 @@ export function getKmwcConfig(root = process.cwd()) {
|
|
|
182
187
|
export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
183
188
|
const packageConfig = packageJson.kmwc || {};
|
|
184
189
|
const id = packageConfig.id || getUnscopedName(packageJson.name) || 'component';
|
|
185
|
-
|
|
190
|
+
// `library: false` — the repo builds its package itself (tsc …) or ships none;
|
|
191
|
+
// Vite serves and builds only the examples site.
|
|
192
|
+
const library = packageConfig.library === false ? { shouldBuild: false } : packageConfig.library || {};
|
|
186
193
|
const examples = packageConfig.examples || {};
|
|
187
194
|
const ports = packageConfig.ports || {};
|
|
188
195
|
const image = packageConfig.image || {};
|
|
189
196
|
const theme = packageConfig.theme || {};
|
|
190
197
|
const analytics = packageConfig.analytics || {};
|
|
191
|
-
|
|
198
|
+
// `cssVariables: false` — no variables manifest to check (no check:vars script).
|
|
199
|
+
const cssVariables = packageConfig.cssVariables === false ? { checkMode: 'off' } : packageConfig.cssVariables || {};
|
|
192
200
|
const rulebook = packageConfig.rulebook || {};
|
|
193
201
|
const devPort = ports.dev ?? 5173;
|
|
194
202
|
|
|
@@ -198,6 +206,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
198
206
|
packageJson,
|
|
199
207
|
id,
|
|
200
208
|
packageName: packageJson.name || id,
|
|
209
|
+
kind: packageConfig.kind || getRegistryKind(id),
|
|
201
210
|
version: packageJson.version || '0.1.0',
|
|
202
211
|
title: packageConfig.title || mapToTitleCase(id),
|
|
203
212
|
tagline: packageConfig.tagline || packageJson.description || '',
|
|
@@ -208,6 +217,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
208
217
|
author: typeof packageJson.author === 'string' ? packageJson.author : packageJson.author?.name || 'Keenmate s.r.o.',
|
|
209
218
|
license: packageJson.license || 'MIT',
|
|
210
219
|
library: {
|
|
220
|
+
shouldBuild: library.shouldBuild ?? true,
|
|
211
221
|
entry: library.entry || 'src/index.ts',
|
|
212
222
|
globalName: library.globalName || mapToPascalCase(id),
|
|
213
223
|
fileName: library.fileName || id,
|
|
@@ -254,6 +264,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
254
264
|
sources: cssVariables.sources || ['src/css'],
|
|
255
265
|
internalVariables: cssVariables.internalVariables || [],
|
|
256
266
|
checkMode: cssVariables.checkMode || 'declared',
|
|
267
|
+
isEnabled: cssVariables.checkMode !== 'off',
|
|
257
268
|
},
|
|
258
269
|
rulebook: {
|
|
259
270
|
path:
|
|
@@ -120,6 +120,9 @@
|
|
|
120
120
|
font-size: 0.9em;
|
|
121
121
|
color: var(--kmwc-accent-color);
|
|
122
122
|
font-family: var(--kmwc-mono-font-family);
|
|
123
|
+
/* A long identifier has no break opportunity — break it anywhere
|
|
124
|
+
rather than widen the page on a phone. */
|
|
125
|
+
overflow-wrap: anywhere;
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
pre code,
|
|
@@ -130,6 +133,32 @@
|
|
|
130
133
|
border-radius: 0;
|
|
131
134
|
color: inherit;
|
|
132
135
|
font-size: inherit;
|
|
136
|
+
overflow-wrap: inherit;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* A key in a keyboard map — the spotlight's key-hint look. */
|
|
140
|
+
kbd {
|
|
141
|
+
display: inline-block;
|
|
142
|
+
padding: 0.1rem 0.35rem;
|
|
143
|
+
background-color: var(--kmwc-elevated-bg);
|
|
144
|
+
color: var(--kmwc-text-color-1);
|
|
145
|
+
border: 1px solid var(--kmwc-border-color);
|
|
146
|
+
border-bottom-width: 2px;
|
|
147
|
+
border-radius: var(--kmwc-radius-sm);
|
|
148
|
+
font-family: var(--kmwc-font-family);
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
font-size: 0.8em;
|
|
151
|
+
line-height: 1.2;
|
|
152
|
+
white-space: nowrap;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* A search match — the spotlight's match highlight. */
|
|
156
|
+
mark {
|
|
157
|
+
background-color: var(--kmwc-accent-light-bg-hover);
|
|
158
|
+
color: var(--kmwc-accent-color);
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
padding: 0 2px;
|
|
161
|
+
border-radius: var(--kmwc-radius-sm);
|
|
133
162
|
}
|
|
134
163
|
|
|
135
164
|
/* ==========================================================================
|
|
@@ -228,6 +257,18 @@
|
|
|
228
257
|
margin-top: 1rem;
|
|
229
258
|
}
|
|
230
259
|
|
|
260
|
+
/* Push an item to the end of a flex row (`.controls`, `.btn-group` …). */
|
|
261
|
+
.push-end {
|
|
262
|
+
margin-inline-start: auto;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Field widths: a native field is ~20 characters wide; these size a short
|
|
266
|
+
one (a page size, a caption) without an inline style. */
|
|
267
|
+
.w-xs { width: 4rem; max-width: 100%; }
|
|
268
|
+
.w-sm { width: 6rem; max-width: 100%; }
|
|
269
|
+
.w-md { width: 9rem; max-width: 100%; }
|
|
270
|
+
.w-lg { width: 12rem; max-width: 100%; }
|
|
271
|
+
|
|
231
272
|
@media (max-width: 768px) {
|
|
232
273
|
.grid,
|
|
233
274
|
.grid-2,
|
|
@@ -74,7 +74,11 @@
|
|
|
74
74
|
cursor: not-allowed;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/* A number is short: 7rem holds six digits and the spin buttons (a
|
|
78
|
+
.form-group still stretches it to the row; .w-* sizes it). */
|
|
77
79
|
:where(input[type="number"]):not(:where(.kmwc-native, .kmwc-native *)) {
|
|
80
|
+
width: 7rem;
|
|
81
|
+
max-width: 100%;
|
|
78
82
|
padding-inline-end: 0.25rem; /* room for the spin buttons */
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
--kmwc-log-info-color: var(--kmwc-info-color);
|
|
30
30
|
--kmwc-log-warn-color: var(--kmwc-warning-color);
|
|
31
31
|
--kmwc-log-err-color: var(--kmwc-danger-color);
|
|
32
|
-
|
|
32
|
+
/* text-color-3 is 2.8:1 on the light panel — under WCAG AA's 4.5. */
|
|
33
|
+
--kmwc-log-muted-color: var(--kmwc-text-color-2);
|
|
33
34
|
--kmwc-log-name-color: var(--kmwc-accent-color);
|
|
34
35
|
--kmwc-log-rule-color: var(--kmwc-border-color);
|
|
35
36
|
font-family: var(--kmwc-mono-font-family);
|
|
@@ -94,7 +95,9 @@
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/* Timeline: a line with cells is a row; the cells keep their width and the
|
|
97
|
-
detail after them wraps in its own column.
|
|
98
|
+
detail after them wraps in its own column. When the cells leave the detail
|
|
99
|
+
less than ~14rem (a phone, a long source name) it drops under them at full
|
|
100
|
+
width instead of being squeezed to a letter per line. */
|
|
98
101
|
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)),
|
|
99
102
|
.event-entry:has(> :is(.event-time, .event-name)) {
|
|
100
103
|
display: flex;
|
|
@@ -103,8 +106,12 @@
|
|
|
103
106
|
border-bottom: 1px solid var(--kmwc-log-rule-color);
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)) {
|
|
110
|
+
flex-wrap: wrap;
|
|
111
|
+
}
|
|
112
|
+
|
|
106
113
|
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)) > :last-child:not(.log-line__name) {
|
|
107
|
-
flex: 1 1
|
|
114
|
+
flex: 1 1 14rem;
|
|
108
115
|
min-width: 0;
|
|
109
116
|
}
|
|
110
117
|
|
|
@@ -115,6 +122,7 @@
|
|
|
115
122
|
.event-time,
|
|
116
123
|
.event-name {
|
|
117
124
|
flex: none;
|
|
125
|
+
max-width: 100%;
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
.log-line__seq,
|
|
@@ -179,7 +187,7 @@
|
|
|
179
187
|
display: flex;
|
|
180
188
|
flex-wrap: wrap;
|
|
181
189
|
gap: 0.4rem;
|
|
182
|
-
margin: 0.
|
|
190
|
+
margin: 0.75rem 0 1rem; /* it follows the demo it measures */
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
.css-readout code { font-size: 0.78rem; }
|
package/src/vite/index.js
CHANGED
|
@@ -199,9 +199,16 @@ export function defineComponentConfig(presetConfig = {}) {
|
|
|
199
199
|
const config = getKmwcConfig(presetConfig.root);
|
|
200
200
|
// The component's own vite — the one running this config (see loadPeerModule).
|
|
201
201
|
const { mergeConfig, searchForWorkspaceRoot } = await loadPeerModule(config.root, 'vite');
|
|
202
|
-
const library = { ...config.library, ...(presetConfig.library || {}) };
|
|
202
|
+
const library = { ...config.library, ...(presetConfig.library === false ? { shouldBuild: false } : presetConfig.library || {}) };
|
|
203
203
|
const isExamplesMode = env.mode === EXAMPLES_MODE;
|
|
204
204
|
const isLibraryBuild = env.command === 'build' && !isExamplesMode;
|
|
205
|
+
if (isLibraryBuild && !library.shouldBuild) {
|
|
206
|
+
// The library build empties dist/ — it would wipe a package the repo builds itself.
|
|
207
|
+
throw new Error(
|
|
208
|
+
`${config.id}: kmwc.library is false — plain \`vite build\` has nothing to build. ` +
|
|
209
|
+
'Build the package with its own script (npm run build), the examples site with `vite build --mode examples`.'
|
|
210
|
+
);
|
|
211
|
+
}
|
|
205
212
|
|
|
206
213
|
const plugins = [createExamplesPlugin({ root: config.root }), ...(presetConfig.plugins || [])];
|
|
207
214
|
|
|
@@ -27,6 +27,8 @@ locally — every local fork is how they drifted apart.
|
|
|
27
27
|
`.table-compact`, …) — don't restyle them per page; `kmwc-native` opts an
|
|
28
28
|
element out. Log lines take the kit tones (`.log-line--ok|info|warn|err|muted`,
|
|
29
29
|
`--action`, timeline cells `.log-line__time` …) — don't colour them per page.
|
|
30
|
+
`<kbd>`, `<mark>`, field widths (`.w-xs` … `.w-lg`) and `.push-end` are kit
|
|
31
|
+
too — no inline styles for them.
|
|
30
32
|
If a second component needs a pattern, it belongs in the devkit.
|
|
31
33
|
- **Example pages are fragments:** no `<html>`/`<head>`/`<header>` — start with
|
|
32
34
|
`<meta name="kmwc:title" content="…">`, the devkit layout adds the rest. Shared text
|