@keenmate/web-components-devkit 1.0.0-rc07 → 1.0.0-rc08
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 +27 -0
- package/README.md +15 -7
- package/package.json +1 -1
- package/src/cli/commands/doctor.js +49 -0
- package/src/cli/repo-checks.js +117 -0
- package/src/examples/css/content.css +1 -1
- package/src/examples/css/tokens.css +5 -1
- package/templates/claude-block.md +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-rc08] - 2026-11-04 [PUBLISHED]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Doctor: hard-coded colours in page CSS** (warning). A colour literal (`#hex`, `rgb()` / `hsl()` /
|
|
8
|
+
`oklch()`, a named colour) on a colour property in a page `<style>`, a `style="…"` or
|
|
9
|
+
`examples-local.css` stays the same when the theme switch flips the kit to dark. web-treeview's
|
|
10
|
+
migration kept 10 pages' own `<style>` blocks, and 8 of them had search bars, status bars, badges and
|
|
11
|
+
panels that didn't follow dark mode. rc07 only flagged rules that restyle a kit class, so nothing
|
|
12
|
+
reported them. `var()` fallbacks and `light-dark()` pairs pass. A block that shows fixed colours on
|
|
13
|
+
purpose (a theme gallery, an iOS mockup) opts out with `<style data-kmwc-fixed-colors>`, or one
|
|
14
|
+
rule with a `/* kmwc-fixed-colors: why */` comment before it (the playground's e2e fixture red).
|
|
15
|
+
- **Doctor: the loader class matches the installed core** (error). Core 6d6b768 renamed the default
|
|
16
|
+
loader's class from `<prefix>-loader` to `<prefix>__loader`, with no release that emits both. A
|
|
17
|
+
component that renames its selector before the core bump, or forgets to after it, ships an unstyled
|
|
18
|
+
loader. No rulebook check sees that, and a VALIDATION-NOTES `## C-NC-8` hides a leftover `.x-loader`
|
|
19
|
+
(drp has one for other classes). Doctor reads which class the installed core's `render-loader.js`
|
|
20
|
+
draws and fails on a `src/**/*.css` selector for the other one.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **Inline `<code>` in dark mode** was the accent colour on a neutral `#2b2b2b` chip: about 3:1, and
|
|
25
|
+
web-treeview's `#059669` green read dim in prose and tables. The chip is now a step up from the main
|
|
26
|
+
surface (`oklch(from --kmwc-main-bg calc(l + 0.07) …)`, slate on slate), and the text is a new
|
|
27
|
+
`--kmwc-inline-code-color`: the accent hue at a lightness of at least 0.8 in dark, the accent as-is in
|
|
28
|
+
light. An e2e spec checks 4.5:1 for the default, green, navy and amber accents.
|
|
29
|
+
|
|
3
30
|
## [1.0.0-rc07] - 2026-11-03 [PUBLISHED]
|
|
4
31
|
|
|
5
32
|
Found migrating web-treeview — each item is something doctor missed or got wrong there.
|
package/README.md
CHANGED
|
@@ -19,6 +19,11 @@ 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-rc08
|
|
23
|
+
|
|
24
|
+
- **Doctor — hard-coded colours in example pages** — web-treeview's devkit migration kept each page's own `<style>` block, and on 8 of its 14 pages the search bars, status bars, badges and panels kept their light colours when the theme switch went dark. rc07 only flagged page CSS that restyles a kit class, so nothing reported them. Doctor now warns about a colour literal (`#hex`, `rgb()` / `hsl()` / `oklch()`, a named colour) on any colour property in a page `<style>`, a `style="…"` attribute or `examples-local.css`. `var()` fallbacks, `light-dark()` pairs, custom properties and `rgb(from var(…))` pass, because they follow the theme. A block that shows fixed colours on purpose, such as a theme gallery or a mockup, opts out with `<style data-kmwc-fixed-colors>`, and a single rule with a `/* kmwc-fixed-colors: why */` comment before it.
|
|
25
|
+
- **Doctor — the loader class matches the installed core** — Core 6d6b768 renamed its default loader's class from `<prefix>-loader` to `<prefix>__loader`, with no release that emits both. A component that renames its selector before the core bump, or forgets to after it, ships a loader with no styles. No rulebook check sees that, and a `## C-NC-8` heading in VALIDATION-NOTES.md would hide a leftover `.x-loader` from the BEM check. Doctor reads which class the installed core's `dist/dom/render-loader.js` draws and fails on any selector in the component's `src/**/*.css` that styles the other one. The error names the file and says which side should change.
|
|
26
|
+
|
|
22
27
|
## What's New in v1.0.0-rc07
|
|
23
28
|
|
|
24
29
|
- **CLI — `kmwc check-pages` finds what only a browser can see** — Migrating web-treeview turned up pages that pushed a phone sideways: an iOS-Files mockup 420px wide and a theme gallery built on `minmax(500px, 1fr)`. No static check could have caught that. `kmwc check-pages` loads every example page in Chromium at 1440, 390 and 320px. It reports page and console errors, failed requests, custom elements that never get defined, and pages wider than the viewport, naming the widest elements. It looks inside shadow roots and at long words that overflow their box. It ignores layers that can't widen a page: scrolling code blocks, hidden or fixed layers, a closed chapter-nav panel. It starts the repo's dev server, or `--url` points it at one already running (for example the built site on the preview port), and `--widths` changes the widths. `kmwc sync` adds a `check:pages` script and a `make check-pages` target.
|
|
@@ -27,13 +32,6 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
27
32
|
- **check-vars — input variables are not dead** — Some component variables are public knobs that are read with a fallback and never declared. Treeview forwards `--wtv-context-menu-*` to core's private `--_km-menu-*` so a page-wide `--km-menu-*` still applies, and declaring them would break that chain. check-vars called all 26 of them dead. In the default mode a variable now counts as present when it is declared or read, and a read-with-fallback missing from the manifest is reported as undocumented. `cssVariables.sources` also takes a package path (`@keenmate/web-components-core/dist/menu/styles.js`) for the `--base-*` reads of a core module the component renders.
|
|
28
33
|
- **Example pages — fit a 320px phone, and injected scripts land in the right place** — A chapter heading that names an API outside `<code>` now breaks instead of widening the page. On a phone the index title is 2rem. Below 400px, body, card and demo padding shrink, so a 250px inline calendar fits inside a card. `mark` no longer splits a word. The Vite plugin now puts its head block after the real `<head>` tag (not `<header>` or the text "<head>"). It puts the kit script before the last `</body>`, so it can no longer land inside a page's inline script and break it. That's the failure web-multiselect's sed-based Dockerfile caused on its live action-buttons page.
|
|
29
34
|
|
|
30
|
-
## What's New in v1.0.0-rc06
|
|
31
|
-
|
|
32
|
-
- **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.
|
|
33
|
-
- **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.
|
|
34
|
-
- **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.
|
|
35
|
-
- **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.
|
|
36
|
-
|
|
37
35
|
## Catalogue
|
|
38
36
|
|
|
39
37
|
<!-- kmwc:catalogue:start — generated by `npm run readme`, do not edit inside -->
|
|
@@ -416,6 +414,16 @@ generated files:
|
|
|
416
414
|
selector (`.kbd`, `kbd`, `mark`, `.pill`, `.card` …) and single-declaration
|
|
417
415
|
inline styles a kit class covers (`width` on a field → `.w-*`,
|
|
418
416
|
`margin-left: auto` → `.push-end`, `margin-top: 1rem` → `.mt-1`) are warnings.
|
|
417
|
+
So is a hard-coded colour (`#hex`, `rgb()`, `white` …) in page CSS or a `style="…"`:
|
|
418
|
+
it stays put when the theme switch flips the kit to dark. `var()` fallbacks and
|
|
419
|
+
`light-dark()` pairs follow the theme. Fixed colours on purpose (a theme gallery, a
|
|
420
|
+
mockup, a test fixture) go in a `<style data-kmwc-fixed-colors>` block, or one rule
|
|
421
|
+
gets a `/* kmwc-fixed-colors: why */` comment right before it.
|
|
422
|
+
- **Loader class** — the component CSS styles the class the *installed* core gives
|
|
423
|
+
its default loader (`<prefix>__loader` from core 6d6b768 on, `<prefix>-loader`
|
|
424
|
+
before). Renaming the selector on one side of the core bump leaves the loader
|
|
425
|
+
unstyled, and a VALIDATION-NOTES `## C-NC-8` would hide the old class from the
|
|
426
|
+
rulebook.
|
|
419
427
|
|
|
420
428
|
`kmwc check-pages` is the browser half: it loads every example page and reports
|
|
421
429
|
what doctor can't see in the source — a script error, a 404, a fixed width that
|
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-rc08",
|
|
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",
|
|
@@ -34,6 +34,11 @@ import {
|
|
|
34
34
|
loadSharedPlausibleSiteIds,
|
|
35
35
|
loadKitClassNames,
|
|
36
36
|
getKitDuplicates,
|
|
37
|
+
FIXED_COLORS_ATTRIBUTE,
|
|
38
|
+
FIXED_COLORS_MARKER,
|
|
39
|
+
getHardcodedColors,
|
|
40
|
+
loadCoreLoader,
|
|
41
|
+
loadStyledLoaderClasses,
|
|
37
42
|
} from '../repo-checks.js';
|
|
38
43
|
|
|
39
44
|
const DEVKIT_PACKAGE_NAME = '@keenmate/web-components-devkit';
|
|
@@ -197,6 +202,23 @@ export function checkComponent(config, doctorConfig = {}) {
|
|
|
197
202
|
}
|
|
198
203
|
if (pageFiles.length && !kitDuplicateSources.length) addPass('pages', 'no page CSS re-implements the kit');
|
|
199
204
|
|
|
205
|
+
// Page CSS colours that stay put when the theme switch flips the kit to dark.
|
|
206
|
+
const hardcodedColorSources = [
|
|
207
|
+
...pageFiles.map((pageFile) => ({ file: pageFile, hardcodedColors: getHardcodedColors(readFileSync(join(config.root, pageFile), 'utf8')) })),
|
|
208
|
+
...(existsSync(localStylesheetPath)
|
|
209
|
+
? [{ file: LOCAL_STYLESHEET_FILE, hardcodedColors: getHardcodedColors(readFileSync(localStylesheetPath, 'utf8'), { isStylesheet: true }) }]
|
|
210
|
+
: []),
|
|
211
|
+
].filter((source) => source.hardcodedColors.length);
|
|
212
|
+
for (const { file, hardcodedColors } of hardcodedColorSources) {
|
|
213
|
+
const shownColors = hardcodedColors.slice(0, 3).map(({ found }) => found).join('; ');
|
|
214
|
+
addWarning(
|
|
215
|
+
'pages',
|
|
216
|
+
`${file}: ${hardcodedColors.length} hard-coded colour(s) that don't follow dark mode — ${shownColors}${hardcodedColors.length > 3 ? ' …' : ''}`,
|
|
217
|
+
`use the kit's --kmwc-* tokens or light-dark(); fixed colours on purpose: <style ${FIXED_COLORS_ATTRIBUTE}>, or /* ${FIXED_COLORS_MARKER} */ before one rule`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
if (pageFiles.length && !hardcodedColorSources.length) addPass('pages', 'page CSS colours follow the theme');
|
|
221
|
+
|
|
200
222
|
// ── generated files ──────────────────────────────────────────────────────
|
|
201
223
|
if (config.hasPackageConfig) {
|
|
202
224
|
for (const planItem of getSyncPlan(config)) {
|
|
@@ -252,6 +274,9 @@ export function checkComponent(config, doctorConfig = {}) {
|
|
|
252
274
|
if (!outputMismatches.length) addPass('library', 'package.json targets match the library build outputs');
|
|
253
275
|
}
|
|
254
276
|
|
|
277
|
+
// ── core's default loader class ──────────────────────────────────────────
|
|
278
|
+
if (config.prefix && config.kind !== LIBRARY_KIND) checkLoaderClass(config, { addPass, addWarning, addError });
|
|
279
|
+
|
|
255
280
|
// ── analytics ────────────────────────────────────────────────────────────
|
|
256
281
|
const plausibleSiteId = loadPlausibleSiteId(config.root);
|
|
257
282
|
const sharingRepoIds = loadSharedPlausibleSiteIds(config, getRegistry(), plausibleSiteId);
|
|
@@ -299,6 +324,30 @@ function checkPorts(config, portBlock, { addPass, addWarning, addError }) {
|
|
|
299
324
|
}
|
|
300
325
|
}
|
|
301
326
|
|
|
327
|
+
/**
|
|
328
|
+
* The CSS styles the loader class the INSTALLED core draws. Core 6d6b768 renamed
|
|
329
|
+
* `<prefix>-loader` → `<prefix>__loader` with no overlap release; renaming the CSS
|
|
330
|
+
* before (or after) the core bump leaves the default loader unstyled, and no rulebook
|
|
331
|
+
* check sees that (a VALIDATION-NOTES `## C-NC-8` even hides a leftover old class).
|
|
332
|
+
*/
|
|
333
|
+
function checkLoaderClass(config, { addPass, addError }) {
|
|
334
|
+
const coreLoader = loadCoreLoader(config.root);
|
|
335
|
+
if (!coreLoader?.separator) return;
|
|
336
|
+
const coreClassName = `${config.prefix}${coreLoader.separator}loader`;
|
|
337
|
+
const styledLoaderClasses = loadStyledLoaderClasses(config.root, config.prefix);
|
|
338
|
+
const mismatchedClasses = styledLoaderClasses.filter(({ separator }) => separator !== coreLoader.separator);
|
|
339
|
+
for (const { file, className } of mismatchedClasses) {
|
|
340
|
+
addError(
|
|
341
|
+
'loader',
|
|
342
|
+
`${file} styles .${className}, but core ${coreLoader.version} draws the default loader as <span class="${coreClassName}"> — it renders unstyled`,
|
|
343
|
+
coreLoader.separator === '__'
|
|
344
|
+
? `rename .${className} → .${coreClassName} (CSS, tests, docs; keep the --${config.prefix}-loader-* tokens)`
|
|
345
|
+
: `keep .${coreClassName} until the component builds on a core that draws <prefix>__loader, then rename in the same change`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (styledLoaderClasses.length && !mismatchedClasses.length) addPass('loader', `CSS styles .${coreClassName}, the class core ${coreLoader.version} draws`);
|
|
349
|
+
}
|
|
350
|
+
|
|
302
351
|
/** Packed paths that break PACKAGE_LEAK_RULES → [{ path, label }]. Pure — exported for tests. */
|
|
303
352
|
export function getPackageLeaks(packedPaths) {
|
|
304
353
|
return packedPaths
|
package/src/cli/repo-checks.js
CHANGED
|
@@ -247,3 +247,120 @@ export function getKitDuplicates(pageText, kitClassNames, { isStylesheet = false
|
|
|
247
247
|
}
|
|
248
248
|
return duplicates;
|
|
249
249
|
}
|
|
250
|
+
|
|
251
|
+
// ── hard-coded colours in page CSS ───────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Fixed colours on purpose (a theme gallery, a mockup, a test fixture): a page `<style>`
|
|
255
|
+
* block with the attribute, or one rule right after a `/* kmwc-fixed-colors *\/` comment.
|
|
256
|
+
*/
|
|
257
|
+
export const FIXED_COLORS_ATTRIBUTE = 'data-kmwc-fixed-colors';
|
|
258
|
+
export const FIXED_COLORS_MARKER = 'kmwc-fixed-colors';
|
|
259
|
+
const MARKED_RULE_PATTERN = new RegExp(`/\\*[^*]*${FIXED_COLORS_MARKER}[\\s\\S]*?\\*/\\s*[^{}]*\\{[^{}]*\\}`, 'g');
|
|
260
|
+
const COLOR_PROPERTY_PATTERN = /color|background|border|outline|shadow|fill|stroke|text-decoration/i;
|
|
261
|
+
const COLOR_LITERAL_PATTERN =
|
|
262
|
+
/#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch)\(\s*(?!from\b)|\b(?:white|black|red|green|blue|gray|grey|silver|orange|yellow|purple|pink|navy|teal|maroon|olive|lime|aqua|fuchsia|gold|crimson)\b/i;
|
|
263
|
+
|
|
264
|
+
/** A declaration value with every `var(…)` collapsed — a literal fallback never shows while the kit token exists. */
|
|
265
|
+
function mapWithoutVarFallbacks(value) {
|
|
266
|
+
let previous;
|
|
267
|
+
let current = value;
|
|
268
|
+
do {
|
|
269
|
+
previous = current;
|
|
270
|
+
current = current.replace(/var\([^()]*\)/gi, 'var()');
|
|
271
|
+
} while (current !== previous);
|
|
272
|
+
return current;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** True when a colour declaration's value is a literal that ignores dark mode. Pure. */
|
|
276
|
+
export function checkIsHardcodedColor(property, value) {
|
|
277
|
+
if (!COLOR_PROPERTY_PATTERN.test(property) || property.startsWith('--')) return false;
|
|
278
|
+
const plainValue = mapWithoutVarFallbacks(value);
|
|
279
|
+
return !/light-dark\(/i.test(plainValue) && COLOR_LITERAL_PATTERN.test(plainValue);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Hard-coded colours in one page's `<style>` blocks and `style="…"` attributes (or in
|
|
284
|
+
* examples-local.css): they stay the same in dark mode while the kit around them flips.
|
|
285
|
+
* `var()` fallbacks and `light-dark()` pairs follow the theme; a `<style data-kmwc-fixed-colors>`
|
|
286
|
+
* block and a rule marked `/* kmwc-fixed-colors *\/` are skipped. Returns [{ found }]. Pure.
|
|
287
|
+
*/
|
|
288
|
+
export function getHardcodedColors(pageText, { isStylesheet = false } = {}) {
|
|
289
|
+
const styleText = isStylesheet
|
|
290
|
+
? pageText
|
|
291
|
+
: [...pageText.matchAll(/<style\b([^>]*)>([\s\S]*?)<\/style>/gi)]
|
|
292
|
+
.filter((match) => !match[1].includes(FIXED_COLORS_ATTRIBUTE))
|
|
293
|
+
.map((match) => match[2])
|
|
294
|
+
.join('\n');
|
|
295
|
+
const hardcodedColors = [];
|
|
296
|
+
const checkedText = styleText.replace(MARKED_RULE_PATTERN, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
297
|
+
for (const [, selector, body] of checkedText.matchAll(/([^{};@]+?)\s*\{([^{}]*)\}/g)) {
|
|
298
|
+
for (const declaration of body.split(';')) {
|
|
299
|
+
const [property, ...valueParts] = declaration.split(':');
|
|
300
|
+
const value = valueParts.join(':').trim();
|
|
301
|
+
if (value && checkIsHardcodedColor(property.trim(), value)) hardcodedColors.push({ found: `${selector.trim().replace(/\s+/g, ' ')} { ${property.trim()}: ${value} }` });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (isStylesheet) return hardcodedColors;
|
|
305
|
+
for (const [, inlineStyle] of pageText.matchAll(/\sstyle="([^"]*)"/g)) {
|
|
306
|
+
for (const declaration of inlineStyle.split(';')) {
|
|
307
|
+
const [property, ...valueParts] = declaration.split(':');
|
|
308
|
+
const value = valueParts.join(':').trim();
|
|
309
|
+
if (value && checkIsHardcodedColor(property.trim(), value)) hardcodedColors.push({ found: `style="${property.trim()}: ${value}"` });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return hardcodedColors;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── core's default loader class ──────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
const CORE_PACKAGE_PATH = join('node_modules', '@keenmate', 'web-components-core');
|
|
318
|
+
const CORE_RENDER_LOADER_FILE = join('dist', 'dom', 'render-loader.js');
|
|
319
|
+
const SOURCE_DIR = 'src';
|
|
320
|
+
const SKIPPED_SOURCE_DIRS = new Set(['node_modules', 'dist']);
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The separator in the class core's default loader gets: `'__'` (`<prefix>__loader`),
|
|
324
|
+
* `'-'` (`<prefix>-loader`, before core 6d6b768), or null when the build doesn't say. Pure.
|
|
325
|
+
*/
|
|
326
|
+
export function getCoreLoaderSeparator(renderLoaderText) {
|
|
327
|
+
const match =
|
|
328
|
+
renderLoaderText.match(/\$\{\s*[\w$.]+\s*\}(__|-)loader(?![\w-])/) || renderLoaderText.match(/\+\s*["'`](__|-)loader["'`]/);
|
|
329
|
+
return match ? match[1] : null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** The loader classes a stylesheet styles: [{ className, separator }]. Pure. */
|
|
333
|
+
export function getStyledLoaderClasses(cssText, prefix) {
|
|
334
|
+
const pattern = new RegExp(`\.(${prefix}(__|-)loader)(?![\w-])`, 'g');
|
|
335
|
+
const plainText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
336
|
+
const classes = new Map([...plainText.matchAll(pattern)].map((match) => [match[1], match[2]]));
|
|
337
|
+
return [...classes].map(([className, separator]) => ({ className, separator }));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** The installed core (walking up for hoisted installs): { version, separator } or null. */
|
|
341
|
+
export function loadCoreLoader(root) {
|
|
342
|
+
for (let directory = resolve(root); ; directory = resolve(directory, '..')) {
|
|
343
|
+
const packageText = readText(join(directory, CORE_PACKAGE_PATH, 'package.json'));
|
|
344
|
+
if (packageText) {
|
|
345
|
+
const renderLoaderText = readText(join(directory, CORE_PACKAGE_PATH, CORE_RENDER_LOADER_FILE));
|
|
346
|
+
return { version: JSON.parse(packageText).version, separator: renderLoaderText ? getCoreLoaderSeparator(renderLoaderText) : null };
|
|
347
|
+
}
|
|
348
|
+
if (resolve(directory, '..') === directory) return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getSourceStylesheets(directory, relativeDirectory = SOURCE_DIR) {
|
|
353
|
+
if (!existsSync(directory)) return [];
|
|
354
|
+
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
355
|
+
const relativePath = `${relativeDirectory}/${entry.name}`;
|
|
356
|
+
if (entry.isDirectory()) return SKIPPED_SOURCE_DIRS.has(entry.name) ? [] : getSourceStylesheets(join(directory, entry.name), relativePath);
|
|
357
|
+
return entry.name.endsWith('.css') ? [relativePath] : [];
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** Loader classes styled in the component's src CSS: [{ file, className, separator }]. */
|
|
362
|
+
export function loadStyledLoaderClasses(root, prefix) {
|
|
363
|
+
return getSourceStylesheets(join(root, SOURCE_DIR)).flatMap((file) =>
|
|
364
|
+
getStyledLoaderClasses(readFileSync(join(root, file), 'utf8'), prefix).map((styledClass) => ({ file, ...styledClass }))
|
|
365
|
+
);
|
|
366
|
+
}
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
padding: 0.2rem 0.4rem;
|
|
119
119
|
border-radius: 3px;
|
|
120
120
|
font-size: 0.9em;
|
|
121
|
-
color: var(--kmwc-
|
|
121
|
+
color: var(--kmwc-inline-code-color);
|
|
122
122
|
font-family: var(--kmwc-mono-font-family);
|
|
123
123
|
/* A long identifier has no break opportunity — break it anywhere
|
|
124
124
|
rather than widen the page on a phone. */
|
|
@@ -118,7 +118,11 @@
|
|
|
118
118
|
--kmwc-console-info-color: #7dd3fc;
|
|
119
119
|
--kmwc-console-warn-color: #fcd34d;
|
|
120
120
|
--kmwc-console-err-color: #fca5a5;
|
|
121
|
-
|
|
121
|
+
/* Dark: the chip is a step up from the main surface (slate on a slate page, not a
|
|
122
|
+
neutral grey), and the text keeps the accent hue but at least 0.8 lightness, so a
|
|
123
|
+
mid-dark accent (treeview's #059669) stays readable on it. */
|
|
124
|
+
--kmwc-inline-code-bg: light-dark(#edf2f7, oklch(from var(--kmwc-main-bg) calc(l + 0.07) c h));
|
|
125
|
+
--kmwc-inline-code-color: light-dark(var(--kmwc-accent-color), oklch(from var(--kmwc-accent-color) max(l, 0.8) c h));
|
|
122
126
|
|
|
123
127
|
/* ======================================================================
|
|
124
128
|
COLORS — SEMANTIC
|
|
@@ -28,7 +28,10 @@ locally — every local fork is how they drifted apart.
|
|
|
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
30
|
`<kbd>`, `<mark>`, field widths (`.w-xs` … `.w-lg`) and `.push-end` are kit
|
|
31
|
-
too — no inline styles for them.
|
|
31
|
+
too — no inline styles for them. Page colours come from the kit's `--kmwc-*`
|
|
32
|
+
tokens or `light-dark()` — a literal doesn't follow dark mode (doctor warns);
|
|
33
|
+
a demo that shows fixed colours on purpose goes in `<style data-kmwc-fixed-colors>`
|
|
34
|
+
(one rule: a `/* kmwc-fixed-colors: why */` comment before it).
|
|
32
35
|
If a second component needs a pattern, it belongs in the devkit.
|
|
33
36
|
- **Example pages are fragments:** no `<html>`/`<head>`/`<header>` — start with
|
|
34
37
|
`<meta name="kmwc:title" content="…">`, the devkit layout adds the rest. Shared text
|