@kerfjs/ui 5.0.0-beta.22 → 5.0.0-beta.23

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/README.md CHANGED
@@ -146,6 +146,13 @@ as a `New` marker.
146
146
  | `PanelHeader` | `@kerfjs/ui/panel-header` | `@kerfjs/ui/panel-header.css` |
147
147
  | `ValueTable`, `ValueTableRow` | `@kerfjs/ui/value-table` | `@kerfjs/ui/value-table.css` |
148
148
 
149
+ Opt-in application layouts keep JavaScript and CSS explicit. For the
150
+ desktop-class multi-panel shell, import `Workbench` from
151
+ `@kerfjs/ui/workbench` and load `@kerfjs/ui/workbench.css`; its supported
152
+ classes and size tokens are cataloged under the `workbench` entry. See
153
+ [`docs/workbench.md`](./docs/workbench.md) for panel ownership and responsive
154
+ replacement guidance.
155
+
149
156
  `PanelHeader` is a plain `Toolbar` used as a panel, dialog, or page heading: an
150
157
  optional icon control group (given a brand fill with a matching border), the
151
158
  title as extra-large `ToolbarText`, the app's trailing action controls, and an
@@ -331,9 +338,10 @@ that convenience import, also import `@kerfjs/ui/styles.css`, which deliberately
331
338
  contains the complete layer. Non-browser/SSR tools resolve the pure `import`
332
339
  condition automatically; `@kerfjs/ui/unstyled` is the explicit CSS-free root
333
340
  entry for a browser build with a custom styling pipeline. The exported
334
- `foundation.css` and component CSS paths remain available for fully manual
335
- delivery. Load application overrides after package styles, or scope `--kui-*`
336
- variables directly on a component instance.
341
+ `@kerfjs/ui/foundation.css` and component CSS paths remain available for fully
342
+ manual delivery. Its complete supported `--kui-*` token surface is cataloged
343
+ under the `foundation` entry. Load application overrides after package styles,
344
+ or scope variables directly on a component instance.
337
345
 
338
346
  ## Pane and content geometry
339
347
 
@@ -141,6 +141,157 @@
141
141
  "composition": "generated-permissive-default"
142
142
  }
143
143
  },
144
+ {
145
+ "key": "@kerfjs/ui:foundation",
146
+ "package": "@kerfjs/ui",
147
+ "id": "foundation",
148
+ "name": "Foundation tokens",
149
+ "kind": "composition",
150
+ "source": "kerf",
151
+ "parents": { "mode": "any", "entries": [] },
152
+ "contexts": ["application-ui"],
153
+ "zones": [],
154
+ "children": { "mode": "any", "concepts": [], "requiredConcepts": [] },
155
+ "state": [
156
+ {
157
+ "id": "application theme overrides",
158
+ "owner": "application",
159
+ "required": false
160
+ },
161
+ {
162
+ "id": "responsive policy at the cataloged device-class breakpoints",
163
+ "owner": "application",
164
+ "required": false
165
+ }
166
+ ],
167
+ "wiring": { "required": false, "helpers": [], "obligations": [] },
168
+ "responsive": { "owner": "not-applicable", "behaviors": [] },
169
+ "layout": {
170
+ "roles": ["foundation"],
171
+ "geometry": { "margin": "none", "border": "none", "padding": "none" }
172
+ },
173
+ "accessibility": {
174
+ "obligations": [
175
+ "Preserve semantic contrast relationships and the visible focus ring when overriding foundation colors."
176
+ ]
177
+ },
178
+ "boundaries": {
179
+ "rootClass": null,
180
+ "publicClasses": [],
181
+ "publicTokens": [
182
+ "--kui-font-sans",
183
+ "--kui-font-mono",
184
+ "--kui-font-2xs",
185
+ "--kui-font-xs",
186
+ "--kui-font-s",
187
+ "--kui-font-m",
188
+ "--kui-font-l",
189
+ "--kui-font-2xl",
190
+ "--kui-font-weight-semibold",
191
+ "--kui-space-none",
192
+ "--kui-space-2xs",
193
+ "--kui-space-xs",
194
+ "--kui-space-s",
195
+ "--kui-space-m",
196
+ "--kui-space-l",
197
+ "--kui-space-xl",
198
+ "--kui-bp-mobile",
199
+ "--kui-bp-tablet",
200
+ "--kui-bp-desktop",
201
+ "--kui-bp-xl-desktop",
202
+ "--kui-layout-inline-margin",
203
+ "--kui-layout-item-border-width",
204
+ "--kui-layout-item-padding",
205
+ "--kui-layout-item-gap",
206
+ "--kui-layout-content-gap",
207
+ "--kui-layout-control-gap",
208
+ "--kui-layout-metadata-gap",
209
+ "--kui-layout-rounded-radius",
210
+ "--kui-layout-pill-radius",
211
+ "--kui-toolbar-group-inner-size",
212
+ "--kui-toolbar-group-size",
213
+ "--kui-toolbar-gap",
214
+ "--kui-radius-s",
215
+ "--kui-radius-m",
216
+ "--kui-radius-l",
217
+ "--kui-radius-circle",
218
+ "--kui-radius-pill",
219
+ "--kui-disclosure-icon-scale",
220
+ "--kui-color-surface",
221
+ "--kui-color-surface-raised",
222
+ "--kui-color-surface-lowered",
223
+ "--kui-color-text",
224
+ "--kui-color-text-quiet",
225
+ "--kui-color-text-link",
226
+ "--kui-color-neutral-fill-quiet",
227
+ "--kui-color-neutral-fill-normal",
228
+ "--kui-color-neutral-fill-loud",
229
+ "--kui-color-neutral-border-quiet",
230
+ "--kui-color-neutral-border-normal",
231
+ "--kui-color-neutral-border-loud",
232
+ "--kui-color-neutral-on-quiet",
233
+ "--kui-color-neutral-on-normal",
234
+ "--kui-color-neutral-on-loud",
235
+ "--kui-color-brand-fill-loud",
236
+ "--kui-color-brand-fill-quiet",
237
+ "--kui-color-brand-fill-normal",
238
+ "--kui-color-brand-border-quiet",
239
+ "--kui-color-brand-border-normal",
240
+ "--kui-color-brand-border-loud",
241
+ "--kui-color-brand-on-quiet",
242
+ "--kui-color-brand-on-normal",
243
+ "--kui-color-brand-on-loud",
244
+ "--kui-color-brand-on-fill",
245
+ "--kui-color-success-fill-loud",
246
+ "--kui-color-success-fill-quiet",
247
+ "--kui-color-success-fill-normal",
248
+ "--kui-color-success-border-quiet",
249
+ "--kui-color-success-border-normal",
250
+ "--kui-color-success-border-loud",
251
+ "--kui-color-success-on-quiet",
252
+ "--kui-color-success-on-normal",
253
+ "--kui-color-success-on-loud",
254
+ "--kui-color-success-on-fill",
255
+ "--kui-color-warning-fill-loud",
256
+ "--kui-color-warning-fill-quiet",
257
+ "--kui-color-warning-fill-normal",
258
+ "--kui-color-warning-border-quiet",
259
+ "--kui-color-warning-border-normal",
260
+ "--kui-color-warning-border-loud",
261
+ "--kui-color-warning-on-quiet",
262
+ "--kui-color-warning-on-normal",
263
+ "--kui-color-warning-on-loud",
264
+ "--kui-color-warning-on-fill",
265
+ "--kui-color-danger-fill-loud",
266
+ "--kui-color-danger-fill-quiet",
267
+ "--kui-color-danger-fill-normal",
268
+ "--kui-color-danger-border-quiet",
269
+ "--kui-color-danger-border-normal",
270
+ "--kui-color-danger-border-loud",
271
+ "--kui-color-danger-on-quiet",
272
+ "--kui-color-danger-on-normal",
273
+ "--kui-color-danger-on-loud",
274
+ "--kui-color-border",
275
+ "--kui-color-border-quiet",
276
+ "--kui-color-accent",
277
+ "--kui-color-accent-text",
278
+ "--kui-color-success",
279
+ "--kui-color-success-text",
280
+ "--kui-color-warning",
281
+ "--kui-color-warning-text",
282
+ "--kui-color-danger",
283
+ "--kui-color-danger-text",
284
+ "--kui-focus-ring",
285
+ "--kui-shadow-s",
286
+ "--kui-shadow-l"
287
+ ]
288
+ },
289
+ "diagnostics": [],
290
+ "provenance": {
291
+ "selection": "ai/component-catalog.json",
292
+ "composition": "generated-permissive-default"
293
+ }
294
+ },
144
295
  {
145
296
  "key": "@kerfjs/ui:layout",
146
297
  "package": "@kerfjs/ui",
@@ -282,6 +433,78 @@
282
433
  "composition": "generated-permissive-default"
283
434
  }
284
435
  },
436
+ {
437
+ "key": "@kerfjs/ui:workbench",
438
+ "package": "@kerfjs/ui",
439
+ "id": "workbench",
440
+ "name": "Workbench",
441
+ "kind": "component",
442
+ "source": "kerf",
443
+ "parents": { "mode": "any", "entries": [] },
444
+ "contexts": ["application-ui"],
445
+ "zones": [],
446
+ "children": { "mode": "any", "concepts": [], "requiredConcepts": [] },
447
+ "state": [
448
+ {
449
+ "id": "panel presence and content",
450
+ "owner": "application",
451
+ "required": false
452
+ },
453
+ { "id": "collapsed state", "owner": "application", "required": false },
454
+ { "id": "panel sizes", "owner": "application", "required": false },
455
+ {
456
+ "id": "responsive replacement below desktop",
457
+ "owner": "application",
458
+ "required": false
459
+ },
460
+ {
461
+ "id": "collapse controls and focus policy",
462
+ "owner": "application",
463
+ "required": false
464
+ }
465
+ ],
466
+ "wiring": { "required": false, "helpers": [], "obligations": [] },
467
+ "responsive": { "owner": "not-applicable", "behaviors": [] },
468
+ "layout": {
469
+ "roles": ["structure"],
470
+ "geometry": {
471
+ "margin": "none",
472
+ "border": "self",
473
+ "padding": "child",
474
+ "notes": [
475
+ "Workbench owns the rail and drawer separators; each region's content owns its internal padding and scroll behavior."
476
+ ]
477
+ }
478
+ },
479
+ "accessibility": {
480
+ "obligations": [
481
+ "Give the Workbench and each meaningful rail or drawer an accessible label.",
482
+ "Keep collapse controls outside the disappearing region and move focus when a controlled rerender replaces a collapse control with its restore control."
483
+ ]
484
+ },
485
+ "boundaries": {
486
+ "rootClass": "kui-workbench",
487
+ "publicClasses": [
488
+ "kui-workbench",
489
+ "kui-workbench__rail",
490
+ "kui-workbench__rail--left",
491
+ "kui-workbench__rail--right",
492
+ "kui-workbench__center",
493
+ "kui-workbench__main",
494
+ "kui-workbench__drawer",
495
+ "kui-workbench__panel-content"
496
+ ],
497
+ "publicTokens": [
498
+ "--kui-workbench-rail-width",
499
+ "--kui-workbench-drawer-height"
500
+ ]
501
+ },
502
+ "diagnostics": [],
503
+ "provenance": {
504
+ "selection": "ai/component-catalog.json",
505
+ "composition": "generated-permissive-default"
506
+ }
507
+ },
285
508
  {
286
509
  "key": "@kerfjs/ui:sunken-panel",
287
510
  "package": "@kerfjs/ui",
@@ -248,6 +248,160 @@
248
248
  "recipe": "docs/webawesome-theme.md#component-selection-guidance"
249
249
  }
250
250
  },
251
+ {
252
+ "id": "foundation",
253
+ "name": "Foundation tokens",
254
+ "category": "Foundation",
255
+ "kind": "composition",
256
+ "source": "kerf",
257
+ "description": "The complete public typography, spacing, breakpoint, geometry, radius, color, focus, and shadow token surface shipped by foundation.css.",
258
+ "uses": [],
259
+ "purpose": "Provide the shared semantic token foundation for application themes and every styled Kerf UI component.",
260
+ "publicExports": [],
261
+ "useWhen": [
262
+ "Theme a Kerf application globally or reuse the shipped semantic typography, spacing, geometry, color, focus, and shadow values in application-owned composition CSS."
263
+ ],
264
+ "avoidWhen": [
265
+ "Do not replace a component-specific prop or token with a broad foundation override when only one component instance should change."
266
+ ],
267
+ "alternatives": [],
268
+ "appOwns": [
269
+ "application theme overrides",
270
+ "responsive policy at the cataloged device-class breakpoints"
271
+ ],
272
+ "variants": [
273
+ "light",
274
+ "dark"
275
+ ],
276
+ "accessibility": [
277
+ "Preserve semantic contrast relationships and the visible focus ring when overriding foundation colors."
278
+ ],
279
+ "geometry": {
280
+ "margin": "none",
281
+ "border": "none",
282
+ "padding": "none"
283
+ },
284
+ "delivery": {
285
+ "manualCssImport": "@kerfjs/ui/foundation.css",
286
+ "sideEffects": [
287
+ "css"
288
+ ]
289
+ },
290
+ "wiring": [],
291
+ "publicClasses": [],
292
+ "publicTokens": [
293
+ "--kui-font-sans",
294
+ "--kui-font-mono",
295
+ "--kui-font-2xs",
296
+ "--kui-font-xs",
297
+ "--kui-font-s",
298
+ "--kui-font-m",
299
+ "--kui-font-l",
300
+ "--kui-font-2xl",
301
+ "--kui-font-weight-semibold",
302
+ "--kui-space-none",
303
+ "--kui-space-2xs",
304
+ "--kui-space-xs",
305
+ "--kui-space-s",
306
+ "--kui-space-m",
307
+ "--kui-space-l",
308
+ "--kui-space-xl",
309
+ "--kui-bp-mobile",
310
+ "--kui-bp-tablet",
311
+ "--kui-bp-desktop",
312
+ "--kui-bp-xl-desktop",
313
+ "--kui-layout-inline-margin",
314
+ "--kui-layout-item-border-width",
315
+ "--kui-layout-item-padding",
316
+ "--kui-layout-item-gap",
317
+ "--kui-layout-content-gap",
318
+ "--kui-layout-control-gap",
319
+ "--kui-layout-metadata-gap",
320
+ "--kui-layout-rounded-radius",
321
+ "--kui-layout-pill-radius",
322
+ "--kui-toolbar-group-inner-size",
323
+ "--kui-toolbar-group-size",
324
+ "--kui-toolbar-gap",
325
+ "--kui-radius-s",
326
+ "--kui-radius-m",
327
+ "--kui-radius-l",
328
+ "--kui-radius-circle",
329
+ "--kui-radius-pill",
330
+ "--kui-disclosure-icon-scale",
331
+ "--kui-color-surface",
332
+ "--kui-color-surface-raised",
333
+ "--kui-color-surface-lowered",
334
+ "--kui-color-text",
335
+ "--kui-color-text-quiet",
336
+ "--kui-color-text-link",
337
+ "--kui-color-neutral-fill-quiet",
338
+ "--kui-color-neutral-fill-normal",
339
+ "--kui-color-neutral-fill-loud",
340
+ "--kui-color-neutral-border-quiet",
341
+ "--kui-color-neutral-border-normal",
342
+ "--kui-color-neutral-border-loud",
343
+ "--kui-color-neutral-on-quiet",
344
+ "--kui-color-neutral-on-normal",
345
+ "--kui-color-neutral-on-loud",
346
+ "--kui-color-brand-fill-loud",
347
+ "--kui-color-brand-fill-quiet",
348
+ "--kui-color-brand-fill-normal",
349
+ "--kui-color-brand-border-quiet",
350
+ "--kui-color-brand-border-normal",
351
+ "--kui-color-brand-border-loud",
352
+ "--kui-color-brand-on-quiet",
353
+ "--kui-color-brand-on-normal",
354
+ "--kui-color-brand-on-loud",
355
+ "--kui-color-brand-on-fill",
356
+ "--kui-color-success-fill-loud",
357
+ "--kui-color-success-fill-quiet",
358
+ "--kui-color-success-fill-normal",
359
+ "--kui-color-success-border-quiet",
360
+ "--kui-color-success-border-normal",
361
+ "--kui-color-success-border-loud",
362
+ "--kui-color-success-on-quiet",
363
+ "--kui-color-success-on-normal",
364
+ "--kui-color-success-on-loud",
365
+ "--kui-color-success-on-fill",
366
+ "--kui-color-warning-fill-loud",
367
+ "--kui-color-warning-fill-quiet",
368
+ "--kui-color-warning-fill-normal",
369
+ "--kui-color-warning-border-quiet",
370
+ "--kui-color-warning-border-normal",
371
+ "--kui-color-warning-border-loud",
372
+ "--kui-color-warning-on-quiet",
373
+ "--kui-color-warning-on-normal",
374
+ "--kui-color-warning-on-loud",
375
+ "--kui-color-warning-on-fill",
376
+ "--kui-color-danger-fill-loud",
377
+ "--kui-color-danger-fill-quiet",
378
+ "--kui-color-danger-fill-normal",
379
+ "--kui-color-danger-border-quiet",
380
+ "--kui-color-danger-border-normal",
381
+ "--kui-color-danger-border-loud",
382
+ "--kui-color-danger-on-quiet",
383
+ "--kui-color-danger-on-normal",
384
+ "--kui-color-danger-on-loud",
385
+ "--kui-color-border",
386
+ "--kui-color-border-quiet",
387
+ "--kui-color-accent",
388
+ "--kui-color-accent-text",
389
+ "--kui-color-success",
390
+ "--kui-color-success-text",
391
+ "--kui-color-warning",
392
+ "--kui-color-warning-text",
393
+ "--kui-color-danger",
394
+ "--kui-color-danger-text",
395
+ "--kui-focus-ring",
396
+ "--kui-shadow-s",
397
+ "--kui-shadow-l"
398
+ ],
399
+ "links": {
400
+ "catalogRoute": "?component=foundation",
401
+ "documentation": "docs/component-selection.md",
402
+ "recipe": "docs/component-selection.md#foundation-tokens"
403
+ }
404
+ },
251
405
  {
252
406
  "id": "layout",
253
407
  "name": "Application layout",
@@ -401,6 +555,89 @@
401
555
  "recipe": "docs/layout.md#anatomy"
402
556
  }
403
557
  },
558
+ {
559
+ "id": "workbench",
560
+ "name": "Workbench",
561
+ "category": "Structure",
562
+ "kind": "component",
563
+ "source": "kerf",
564
+ "description": "A desktop-class multi-panel workspace with independently collapsible left and right rails and a bottom drawer around one central work area.",
565
+ "uses": [],
566
+ "purpose": "Arrange a complex tool or editor as a stable central work area with optional peripheral rails and a bottom drawer.",
567
+ "publicExports": [
568
+ "Workbench",
569
+ "WorkbenchPanel",
570
+ "WorkbenchProps"
571
+ ],
572
+ "useWhen": [
573
+ "A desktop-class tool needs a central work area plus independently collapsible navigator, inspector, or console regions."
574
+ ],
575
+ "avoidWhen": [
576
+ "Do not shrink the full multi-panel shell onto compact devices; present peripheral content through NavStack or overlay drawers instead.",
577
+ "Do not use Workbench for one standalone collapsible rail or drawer; use CollapsiblePanel."
578
+ ],
579
+ "alternatives": [
580
+ {
581
+ "id": "pane",
582
+ "relationship": "composes-with",
583
+ "when": "Use Pane inside Workbench regions when fixed header/footer chrome must surround one scroll owner."
584
+ }
585
+ ],
586
+ "appOwns": [
587
+ "panel presence and content",
588
+ "collapsed state",
589
+ "panel sizes",
590
+ "responsive replacement below desktop",
591
+ "collapse controls and focus policy"
592
+ ],
593
+ "variants": [
594
+ "main only",
595
+ "left rail",
596
+ "right rail",
597
+ "bottom drawer",
598
+ "all panels",
599
+ "independently collapsed panels"
600
+ ],
601
+ "accessibility": [
602
+ "Give the Workbench and each meaningful rail or drawer an accessible label.",
603
+ "Keep collapse controls outside the disappearing region and move focus when a controlled rerender replaces a collapse control with its restore control."
604
+ ],
605
+ "geometry": {
606
+ "margin": "none",
607
+ "border": "self",
608
+ "padding": "child",
609
+ "notes": [
610
+ "Workbench owns the rail and drawer separators; each region's content owns its internal padding and scroll behavior."
611
+ ]
612
+ },
613
+ "delivery": {
614
+ "moduleImport": "@kerfjs/ui/workbench",
615
+ "manualCssImport": "@kerfjs/ui/workbench.css",
616
+ "sideEffects": [
617
+ "manual-css"
618
+ ]
619
+ },
620
+ "wiring": [],
621
+ "publicClasses": [
622
+ "kui-workbench",
623
+ "kui-workbench__rail",
624
+ "kui-workbench__rail--left",
625
+ "kui-workbench__rail--right",
626
+ "kui-workbench__center",
627
+ "kui-workbench__main",
628
+ "kui-workbench__drawer",
629
+ "kui-workbench__panel-content"
630
+ ],
631
+ "publicTokens": [
632
+ "--kui-workbench-rail-width",
633
+ "--kui-workbench-drawer-height"
634
+ ],
635
+ "links": {
636
+ "catalogRoute": "?component=workbench",
637
+ "documentation": "docs/workbench.md",
638
+ "recipe": "docs/workbench.md#state-lives-in-the-app"
639
+ }
640
+ },
404
641
  {
405
642
  "id": "sunken-panel",
406
643
  "name": "SunkenPanel",
@@ -76,6 +76,7 @@
76
76
  "type": "object",
77
77
  "required": ["sideEffects"],
78
78
  "properties": {
79
+ "moduleImport": { "type": "string" },
79
80
  "browserImport": { "type": "string" },
80
81
  "manualCssImport": { "type": "string" },
81
82
  "registrationImport": { "type": "string" },
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.22` and `kerfjs@5.0.0-beta.22`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.23` and `kerfjs@5.0.0-beta.23`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/disclosure-arrow`
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Web Awesome JSX signatures for the UI authoring corpus
2
2
 
3
- Generated from the emitted `@kerfjs/ui@5.0.0-beta.22` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
3
+ Generated from the emitted `@kerfjs/ui@5.0.0-beta.23` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
4
4
 
5
5
  ```ts
6
6
  import { KerfCustomElement } from 'kerfjs/jsx-runtime';
@@ -13,6 +13,7 @@ import postcss from 'postcss';
13
13
  import ts from 'typescript';
14
14
 
15
15
  import { loadApplicationUiProfile } from '../ai/application-ui-profile.mjs';
16
+ import { isUiTraversalExcluded } from '../traversal-exclusions.mjs';
16
17
 
17
18
  export const UI_ANALYSIS_SCHEMA_VERSION = 1;
18
19
 
@@ -36,16 +37,10 @@ const spacingProperties = /^(?:margin|padding|gap|inset)(?:-|$)/;
36
37
  const dimensionProperties =
37
38
  /^(?:width|height|min-width|max-width|min-height|max-height)$/;
38
39
  const approvedSpacing = new Set([0, 4, 8, 16, 24]);
39
- const ignoredDirectories = new Set([
40
- '.git',
41
- 'coverage',
42
- 'dist',
43
- 'node_modules',
44
- ]);
45
-
46
40
  async function collectFiles(root, paths) {
47
41
  const files = [];
48
42
  const visit = async (path) => {
43
+ if (isUiTraversalExcluded(root, path)) return;
49
44
  const details = await stat(path);
50
45
  if (details.isFile()) {
51
46
  if (sourceExtensions.has(extname(path)) || path.endsWith('.css'))
@@ -54,7 +49,6 @@ async function collectFiles(root, paths) {
54
49
  }
55
50
  const entries = await readdir(path, { withFileTypes: true });
56
51
  for (const entry of entries) {
57
- if (entry.isDirectory() && ignoredDirectories.has(entry.name)) continue;
58
52
  const child = resolve(path, entry.name);
59
53
  if (entry.isDirectory()) await visit(child);
60
54
  else if (
@@ -24,6 +24,18 @@ fix the surrounding layout instead of overriding a control. See
24
24
  [`design-philosophy.md`](./design-philosophy.md) "Reach for the primitive, not for
25
25
  CSS".
26
26
 
27
+ ## Foundation tokens
28
+
29
+ Import `@kerfjs/ui/foundation.css` when application-owned composition CSS needs
30
+ the same semantic typography, spacing, device breakpoints, geometry, radii,
31
+ colors, focus ring, or shadows as Kerf components. Its complete supported
32
+ `--kui-*` surface is cataloged by the `foundation` entry; tools may treat that
33
+ entry as the public allowlist rather than inferring stability from CSS text.
34
+ Override these tokens globally for an application theme or on the narrowest
35
+ useful subtree. Prefer a component prop or component-specific token when the
36
+ change belongs to one component or instance, and preserve the documented
37
+ foreground/background contrast relationships and visible focus ring.
38
+
27
39
  ## Production recipes
28
40
 
29
41
  Use the [complete recipe guide](./recipes.md) when several primitives form one
@@ -28,6 +28,11 @@ its reachable project-local styles into the report, including quoted or
28
28
  unquoted `url()` imports; external package styles remain outside the consumer
29
29
  boundary.
30
30
 
31
+ Recursive discovery excludes generated and tool-owned trees, including any
32
+ nested `.claude/worktrees` checkout. Those checkouts are separate repositories,
33
+ not application source, and cannot contribute files, diagnostics, profile
34
+ policy, or analysis inputs to the containing application.
35
+
31
36
  ## Diagnostics and exit behavior
32
37
 
33
38
  | Rule | Kind | Meaning |
package/docs/ui-doctor.md CHANGED
@@ -15,7 +15,7 @@ The default terminal output is short and repair-oriented. `--format json` emits
15
15
 
16
16
  The catalog stage discovers the package-default application UI profile and workspace/root-to-leaf directory layers. It validates referenced selection/composition catalogs and, when a workspace declares `package.json#kerfComponentCatalog`, invokes the installed `create-kerf-component` catalog checker to validate metadata, source exports, schema conformance, and generated-output drift. This checker reads source text; it does not import application modules.
17
17
 
18
- TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
18
+ TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). This is deliberately an isolated Kerf lint pass rather than the consumer's complete ESLint configuration. Inline directives for consumer-owned plugins, such as `@typescript-eslint`, therefore do not produce false "rule definition not found" diagnostics; the consumer's normal ESLint command remains authoritative for those rules. Unknown `kerfjs/*` directives still fail the doctor pass. Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
19
19
 
20
20
  The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
21
21
 
@@ -25,6 +25,11 @@ An unavailable or failed stage does not prevent independent stages from reportin
25
25
 
26
26
  `--full` is the default and analyzes the selected package. `--changed` reads tracked and untracked paths from Git unless one or more `--path` values are supplied. An empty changed set is a configuration error instead of a false-clean success. With `--package`, workspace-relative Git paths are converted to package-relative paths before TypeScript, ESLint, and analyzer selection; paths outside the selected package are ignored.
27
27
 
28
+ Full traversal treats nested `.claude/worktrees` checkouts as tool-owned
29
+ repositories rather than application source. Their files are excluded
30
+ consistently from TypeScript, ESLint, analyzer discovery, and cache inputs, even
31
+ when the nested checkout lives below another application directory.
32
+
28
33
  TypeScript constructs the selected package's program so compiler options retain their real meaning, while its root inputs are narrowed to changed source files. Use `--full` for release gates.
29
34
 
30
35
  ## Configuration and suppressions
package/docs/ux-demo.md CHANGED
@@ -10,7 +10,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
10
10
  - The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
11
11
  - The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
12
12
  - Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
13
- - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<browser-subpath>.tsx` from their canonical `delivery.browserImport`.
13
+ - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
14
14
  - Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
15
15
  - The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
16
16
  - The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `PanelHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
package/docs/workbench.md CHANGED
@@ -49,6 +49,19 @@ Each `WorkbenchPanel` takes `content`, an optional `collapsed`, an optional
49
49
  `--kui-workbench-rail-width` 280px, `--kui-workbench-drawer-height` 220px), and
50
50
  an optional `label`.
51
51
 
52
+ ## Public styling boundary
53
+
54
+ Import `@kerfjs/ui/workbench.css` after the component subpath. Applications may
55
+ set `--kui-workbench-rail-width` and `--kui-workbench-drawer-height` on a
56
+ Workbench instance. The supported composition classes are `.kui-workbench`,
57
+ `.kui-workbench__rail`, `.kui-workbench__rail--left`,
58
+ `.kui-workbench__rail--right`, `.kui-workbench__center`,
59
+ `.kui-workbench__main`, `.kui-workbench__drawer`, and
60
+ `.kui-workbench__panel-content`; these exact hooks are cataloged for tools that
61
+ must classify public application selectors. Prefer the component props and two
62
+ size tokens before selecting internal anatomy, and do not target its data
63
+ attributes or descendant tags as styling contracts.
64
+
52
65
  ## How the collapse animates
53
66
 
54
67
  Collapsing snaps the panel's flex track to zero in a single reflow (so the work
@@ -0,0 +1,8 @@
1
+ export interface EslintDiagnosticLike {
2
+ ruleId?: string | null;
3
+ message: string;
4
+ }
5
+
6
+ export function isForeignRuleDefinitionDiagnostic(
7
+ item: EslintDiagnosticLike,
8
+ ): boolean;
@@ -0,0 +1,7 @@
1
+ export function isForeignRuleDefinitionDiagnostic(item) {
2
+ return (
3
+ typeof item.ruleId === 'string' &&
4
+ !item.ruleId.startsWith('kerfjs/') &&
5
+ item.message === `Definition for rule '${item.ruleId}' was not found.`
6
+ );
7
+ }
package/doctor/index.mjs CHANGED
@@ -23,6 +23,8 @@ import { createRequire } from 'node:module';
23
23
  import { loadApplicationUiProfile } from '../ai/application-ui-profile.mjs';
24
24
  import { analyzeUiProject, UI_ANALYSIS_RULES } from '../analyzer/index.mjs';
25
25
  import { evaluateUi, UI_EVALUATION_RULES } from '../evaluator/index.mjs';
26
+ import { isForeignRuleDefinitionDiagnostic } from './eslint-diagnostics.mjs';
27
+ import { isUiTraversalExcluded } from '../traversal-exclusions.mjs';
26
28
 
27
29
  export const UI_DOCTOR_SCHEMA_VERSION = 1;
28
30
  export const UI_DOCTOR_EXIT = Object.freeze({
@@ -54,14 +56,6 @@ const sourceExtensions = new Set([
54
56
  '.cts',
55
57
  '.css',
56
58
  ]);
57
- const ignoredDirectories = new Set([
58
- '.git',
59
- '.kerf-cache',
60
- 'coverage',
61
- 'dist',
62
- 'kerf-ui-evidence',
63
- 'node_modules',
64
- ]);
65
59
  const defaultStages = Object.freeze({
66
60
  catalog: true,
67
61
  typescript: true,
@@ -361,6 +355,7 @@ export async function resolveUiDoctorPackage(root, selector) {
361
355
  async function collectInputs(root, paths) {
362
356
  const result = [];
363
357
  const visit = async (path) => {
358
+ if (isUiTraversalExcluded(root, path)) return;
364
359
  let details;
365
360
  try {
366
361
  details = await stat(path);
@@ -381,7 +376,6 @@ async function collectInputs(root, paths) {
381
376
  return;
382
377
  }
383
378
  for (const entry of await readdir(path, { withFileTypes: true })) {
384
- if (entry.isDirectory() && ignoredDirectories.has(entry.name)) continue;
385
379
  await visit(resolve(path, entry.name));
386
380
  }
387
381
  };
@@ -777,6 +771,9 @@ async function runTypeScript({ packageRoot, paths }) {
777
771
  { ...ts.default.sys, onUnRecoverableConfigFileDiagnostic: () => {} },
778
772
  );
779
773
  if (!parsed) throw new Error('TypeScript could not parse tsconfig.json.');
774
+ parsed.fileNames = parsed.fileNames.filter(
775
+ (file) => !isUiTraversalExcluded(packageRoot, file),
776
+ );
780
777
  if (paths?.length) {
781
778
  const selected = new Set(paths.map((path) => resolve(packageRoot, path)));
782
779
  parsed.fileNames = parsed.fileNames.filter((file) => selected.has(file));
@@ -850,7 +847,7 @@ async function runEslint({
850
847
  },
851
848
  },
852
849
  };
853
- const configs = [base];
850
+ const configs = [{ ignores: ['**/.claude/worktrees/**'] }, base];
854
851
  if (usesTypeScript) {
855
852
  const parserModule = await importFrom(
856
853
  packageRoot,
@@ -879,6 +876,7 @@ async function runEslint({
879
876
  const diagnostics = [];
880
877
  for (const result of results)
881
878
  for (const item of result.messages) {
879
+ if (isForeignRuleDefinitionDiagnostic(item)) continue;
882
880
  const code = item.message.match(/\bKUI-[A-Z]\d{3}\b/)?.[0];
883
881
  diagnostics.push(
884
882
  normalizedDiagnostic({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.22",
3
+ "version": "5.0.0-beta.23",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -301,6 +301,7 @@
301
301
  "analyzer",
302
302
  "doctor",
303
303
  "evaluator",
304
+ "traversal-exclusions.mjs",
304
305
  "docs",
305
306
  "ux-demo/recipes",
306
307
  "llms.txt",
@@ -0,0 +1,21 @@
1
+ import { relative, sep } from 'node:path';
2
+
3
+ const ignoredDirectoryNames = new Set([
4
+ '.git',
5
+ '.kerf-cache',
6
+ 'coverage',
7
+ 'dist',
8
+ 'kerf-ui-evidence',
9
+ 'node_modules',
10
+ ]);
11
+
12
+ export function isUiTraversalExcluded(root, candidate) {
13
+ const segments = relative(root, candidate).split(sep);
14
+ return (
15
+ segments.some((segment) => ignoredDirectoryNames.has(segment)) ||
16
+ segments.some(
17
+ (segment, index) =>
18
+ segment === '.claude' && segments[index + 1] === 'worktrees',
19
+ )
20
+ );
21
+ }