@kerfjs/ui 5.0.0-beta.72 → 5.0.0-beta.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/ai/component-catalog.json +3 -1
  2. package/ai/component-composition.json +1 -0
  3. package/ai/components/foundation.md +1 -1
  4. package/ai/components/list-header.md +2 -1
  5. package/ai/public-api-signatures-v1.md +5 -2
  6. package/ai/skill.md +2 -0
  7. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  8. package/analyzer/component-ownership.mjs +41 -13
  9. package/analyzer/index.mjs +40 -11
  10. package/dist/app-tab.js +1 -1
  11. package/dist/badge.js +1 -1
  12. package/dist/catalog.js +64 -26
  13. package/dist/catalog.js.map +1 -1
  14. package/dist/{chunk-K67Y2JNF.js → chunk-2IGHGE3M.js} +3 -3
  15. package/dist/{chunk-K67Y2JNF.js.map → chunk-2IGHGE3M.js.map} +1 -1
  16. package/dist/{chunk-RNQNEENY.js → chunk-2RMDP7HQ.js} +2 -2
  17. package/dist/{chunk-RNQNEENY.js.map → chunk-2RMDP7HQ.js.map} +1 -1
  18. package/dist/{chunk-3UE24JSC.js → chunk-33EOFAUN.js} +4 -4
  19. package/dist/{chunk-3UE24JSC.js.map → chunk-33EOFAUN.js.map} +1 -1
  20. package/dist/{chunk-47CKKLBE.js → chunk-3DOHW3K4.js} +2 -2
  21. package/dist/{chunk-47CKKLBE.js.map → chunk-3DOHW3K4.js.map} +1 -1
  22. package/dist/{chunk-J3KGJ6P6.js → chunk-4NG4Y2Q6.js} +1 -1
  23. package/dist/{chunk-J3KGJ6P6.js.map → chunk-4NG4Y2Q6.js.map} +1 -1
  24. package/dist/{chunk-NC4CFOID.js → chunk-6LAQWWEX.js} +3 -3
  25. package/dist/{chunk-NC4CFOID.js.map → chunk-6LAQWWEX.js.map} +1 -1
  26. package/dist/{chunk-Q6YU3BT7.js → chunk-7KHKLG5C.js} +2 -2
  27. package/dist/{chunk-Q6YU3BT7.js.map → chunk-7KHKLG5C.js.map} +1 -1
  28. package/dist/{chunk-MP2GDBQZ.js → chunk-ALGYHYXP.js} +1 -1
  29. package/dist/{chunk-MP2GDBQZ.js.map → chunk-ALGYHYXP.js.map} +1 -1
  30. package/dist/{chunk-QJ5VFCW3.js → chunk-BGD7EHUE.js} +2 -2
  31. package/dist/{chunk-QJ5VFCW3.js.map → chunk-BGD7EHUE.js.map} +1 -1
  32. package/dist/{chunk-UIDKY4UB.js → chunk-DPAUKNB6.js} +2 -2
  33. package/dist/{chunk-UIDKY4UB.js.map → chunk-DPAUKNB6.js.map} +1 -1
  34. package/dist/{chunk-KVBRMEPM.js → chunk-I6FTU27I.js} +1 -1
  35. package/dist/{chunk-KVBRMEPM.js.map → chunk-I6FTU27I.js.map} +1 -1
  36. package/dist/{chunk-R5F5XUJ7.js → chunk-IJNYCPAU.js} +2 -2
  37. package/dist/{chunk-R5F5XUJ7.js.map → chunk-IJNYCPAU.js.map} +1 -1
  38. package/dist/{chunk-P7D3EMXM.js → chunk-KFJSNWO4.js} +2 -2
  39. package/dist/{chunk-P7D3EMXM.js.map → chunk-KFJSNWO4.js.map} +1 -1
  40. package/dist/{chunk-MEY33VWB.js → chunk-KZRR7NNV.js} +1 -1
  41. package/dist/{chunk-MEY33VWB.js.map → chunk-KZRR7NNV.js.map} +1 -1
  42. package/dist/{chunk-VLYIPBCQ.js → chunk-L67OYMGE.js} +3 -3
  43. package/dist/{chunk-VLYIPBCQ.js.map → chunk-L67OYMGE.js.map} +1 -1
  44. package/dist/{chunk-GLDWRCAM.js → chunk-RT5WKYX3.js} +3 -3
  45. package/dist/{chunk-GLDWRCAM.js.map → chunk-RT5WKYX3.js.map} +1 -1
  46. package/dist/{chunk-M6DEEBEF.js → chunk-RU2EMR56.js} +7 -3
  47. package/dist/chunk-RU2EMR56.js.map +1 -0
  48. package/dist/{chunk-KLJKU6ZL.js → chunk-TDN5EE3S.js} +1 -1
  49. package/dist/chunk-TDN5EE3S.js.map +1 -0
  50. package/dist/{chunk-AHEG27M7.js → chunk-TT5ROTQK.js} +1 -1
  51. package/dist/{chunk-AHEG27M7.js.map → chunk-TT5ROTQK.js.map} +1 -1
  52. package/dist/{chunk-TJK44RQC.js → chunk-V4WLBOVJ.js} +2 -2
  53. package/dist/{chunk-TJK44RQC.js.map → chunk-V4WLBOVJ.js.map} +1 -1
  54. package/dist/{chunk-IA7TMD4Y.js → chunk-ZLKZFKLW.js} +2 -2
  55. package/dist/{chunk-IA7TMD4Y.js.map → chunk-ZLKZFKLW.js.map} +1 -1
  56. package/dist/{chunk-7OKX3EF6.js → chunk-ZSJOLL3S.js} +3 -3
  57. package/dist/{chunk-7OKX3EF6.js.map → chunk-ZSJOLL3S.js.map} +1 -1
  58. package/dist/collapsible-panel.js +2 -2
  59. package/dist/empty-state.js +1 -1
  60. package/dist/index.js +17 -17
  61. package/dist/list-action-row.js +1 -1
  62. package/dist/list-header.d.ts +4 -1
  63. package/dist/list-header.js +1 -1
  64. package/dist/list-item.js +1 -1
  65. package/dist/loading-spinner.js +1 -1
  66. package/dist/nav-stack.js +1 -1
  67. package/dist/popup-menu.js +1 -1
  68. package/dist/resizable-region.js +1 -1
  69. package/dist/select.js +1 -1
  70. package/dist/skeleton.js +1 -1
  71. package/dist/spacer.js +1 -1
  72. package/dist/split-view.js +2 -2
  73. package/dist/state-banner.js +1 -1
  74. package/dist/styles/catalog/components/catalog.css +38 -0
  75. package/dist/styles/foundation.css +1 -0
  76. package/dist/styles/text.css +27 -0
  77. package/dist/tab-scaffold.js +1 -1
  78. package/dist/toolbar-control-group.js +1 -1
  79. package/dist/toolbar-text.js +1 -1
  80. package/dist/value-table.js +1 -1
  81. package/dist/wire-catalog.js +81 -1
  82. package/dist/wire-catalog.js.map +1 -1
  83. package/dist/wire-resizable-regions.js +1 -1
  84. package/dist/wire-workbench.js +1 -1
  85. package/dist/workbench.js +1 -1
  86. package/docs/catalog.md +13 -3
  87. package/docs/design/templates/list-header/size-l-dark.svg +1 -0
  88. package/docs/design/templates/list-header/size-l.svg +1 -0
  89. package/docs/design/templates/list-header/size-m-dark.svg +1 -0
  90. package/docs/design/templates/list-header/size-m.svg +1 -0
  91. package/docs/design/templates/list-header/size-xl-dark.svg +1 -0
  92. package/docs/design/templates/list-header/size-xl.svg +1 -0
  93. package/docs/design/templates/list-header/size-xs-dark.svg +1 -0
  94. package/docs/design/templates/list-header/size-xs.svg +1 -0
  95. package/docs/design/templates/list-header-dark.svg +1 -1
  96. package/docs/design/templates/list-header.svg +1 -1
  97. package/docs/layout.md +7 -0
  98. package/docs/ui-analyzer.md +4 -0
  99. package/docs/ui-doctor.md +8 -2
  100. package/docs/ux-demo.md +5 -2
  101. package/llms.txt +4 -1
  102. package/package.json +1 -1
  103. package/ux-demo/recipes/composer-form.tsx +2 -2
  104. package/dist/chunk-KLJKU6ZL.js.map +0 -1
  105. package/dist/chunk-M6DEEBEF.js.map +0 -1
@@ -342,6 +342,7 @@
342
342
  "--kui-font-s",
343
343
  "--kui-font-m",
344
344
  "--kui-font-l",
345
+ "--kui-font-xl",
345
346
  "--kui-font-2xl",
346
347
  "--kui-font-weight-semibold",
347
348
  "--kui-space-none",
@@ -2869,7 +2870,7 @@
2869
2870
  "category": "Navigation",
2870
2871
  "kind": "component",
2871
2872
  "source": "kerf",
2872
- "description": "Configurable section headings with full-width or shrink-wrapped inline presentation, semantic count/status indicators, density, group dividers, a logical-end action, disclosure states, and protected native-popover trigger metadata.",
2873
+ "description": "Configurable section headings with five independent label sizes, full-width or shrink-wrapped inline presentation, semantic count/status indicators, density, group dividers, a logical-end action, disclosure states, and protected native-popover trigger metadata.",
2873
2874
  "uses": [
2874
2875
  "badge",
2875
2876
  "lucide-icon",
@@ -2904,6 +2905,7 @@
2904
2905
  ],
2905
2906
  "variants": [
2906
2907
  "heading",
2908
+ "label sizes xs, sm (default), m, l, xl",
2907
2909
  "count",
2908
2910
  "badge",
2909
2911
  "action",
@@ -282,6 +282,7 @@
282
282
  "--kui-font-s",
283
283
  "--kui-font-m",
284
284
  "--kui-font-l",
285
+ "--kui-font-xl",
285
286
  "--kui-font-2xl",
286
287
  "--kui-font-weight-semibold",
287
288
  "--kui-space-none",
@@ -55,7 +55,7 @@ Margin: none · border: none · padding: none (layout role: foundation). `self`
55
55
 
56
56
  The component owns its own styles. Configure it through its props and variants; do not override its internals.
57
57
 
58
- Public tokens it reads: `--kui-font-sans`, `--kui-font-mono`, `--kui-font-2xs`, `--kui-font-xs`, `--kui-font-s`, `--kui-font-m`, `--kui-font-l`, `--kui-font-2xl`, `--kui-font-weight-semibold`, `--kui-space-none`, `--kui-space-2xs`, `--kui-space-xs`, `--kui-space-s`, `--kui-space-m`, `--kui-space-l`, `--kui-space-xl`, `--kui-bp-mobile`, `--kui-bp-tablet`, `--kui-bp-desktop`, `--kui-bp-xl-desktop`, `--kui-layout-inline-margin`, `--kui-layout-item-border-width`, `--kui-layout-item-padding`, `--kui-layout-item-gap`, `--kui-layout-content-gap`, `--kui-layout-control-gap`, `--kui-layout-metadata-gap`, `--kui-layout-highlight-inset`, `--kui-layout-rounded-radius`, `--kui-layout-pill-radius`, `--kui-toolbar-group-inner-size`, `--kui-toolbar-group-size`, `--kui-toolbar-gap`, `--kui-safe-area-block-start`, `--kui-safe-area-block-end`, `--kui-safe-area-inline-start`, `--kui-safe-area-inline-end`, `--kui-radius-s`, `--kui-radius-m`, `--kui-radius-l`, `--kui-radius-circle`, `--kui-radius-pill`, `--kui-disclosure-icon-scale`, `--kui-color-surface`, `--kui-color-surface-raised`, `--kui-color-surface-lowered`, `--kui-color-text`, `--kui-color-text-quiet`, `--kui-color-text-link`, `--kui-color-neutral-fill-quiet`, `--kui-color-neutral-fill-normal`, `--kui-color-neutral-fill-loud`, `--kui-color-neutral-border-quiet`, `--kui-color-neutral-border-normal`, `--kui-color-neutral-border-loud`, `--kui-color-neutral-on-quiet`, `--kui-color-neutral-on-normal`, `--kui-color-neutral-on-loud`, `--kui-color-brand-fill-loud`, `--kui-color-brand-fill-quiet`, `--kui-color-brand-fill-normal`, `--kui-color-brand-border-quiet`, `--kui-color-brand-border-normal`, `--kui-color-brand-border-loud`, `--kui-color-brand-on-quiet`, `--kui-color-brand-on-normal`, `--kui-color-brand-on-loud`, `--kui-color-brand-on-fill`, `--kui-color-pop-fill-loud`, `--kui-color-pop-fill-quiet`, `--kui-color-pop-fill-normal`, `--kui-color-pop-border-quiet`, `--kui-color-pop-border-normal`, `--kui-color-pop-border-loud`, `--kui-color-pop-on-quiet`, `--kui-color-pop-on-normal`, `--kui-color-pop-on-loud`, `--kui-color-pop-on-fill`, `--kui-color-success-fill-loud`, `--kui-color-success-fill-quiet`, `--kui-color-success-fill-normal`, `--kui-color-success-border-quiet`, `--kui-color-success-border-normal`, `--kui-color-success-border-loud`, `--kui-color-success-on-quiet`, `--kui-color-success-on-normal`, `--kui-color-success-on-loud`, `--kui-color-success-on-fill`, `--kui-color-warning-fill-loud`, `--kui-color-warning-fill-quiet`, `--kui-color-warning-fill-normal`, `--kui-color-warning-border-quiet`, `--kui-color-warning-border-normal`, `--kui-color-warning-border-loud`, `--kui-color-warning-on-quiet`, `--kui-color-warning-on-normal`, `--kui-color-warning-on-loud`, `--kui-color-warning-on-fill`, `--kui-color-danger-fill-loud`, `--kui-color-danger-fill-quiet`, `--kui-color-danger-fill-normal`, `--kui-color-danger-border-quiet`, `--kui-color-danger-border-normal`, `--kui-color-danger-border-loud`, `--kui-color-danger-on-quiet`, `--kui-color-danger-on-normal`, `--kui-color-danger-on-loud`, `--kui-color-danger-on-fill`, `--kui-focus-ring`, `--kui-shadow-s`, `--kui-shadow-l`. Set a token only where its public contract allows; prefer a prop.
58
+ Public tokens it reads: `--kui-font-sans`, `--kui-font-mono`, `--kui-font-2xs`, `--kui-font-xs`, `--kui-font-s`, `--kui-font-m`, `--kui-font-l`, `--kui-font-xl`, `--kui-font-2xl`, `--kui-font-weight-semibold`, `--kui-space-none`, `--kui-space-2xs`, `--kui-space-xs`, `--kui-space-s`, `--kui-space-m`, `--kui-space-l`, `--kui-space-xl`, `--kui-bp-mobile`, `--kui-bp-tablet`, `--kui-bp-desktop`, `--kui-bp-xl-desktop`, `--kui-layout-inline-margin`, `--kui-layout-item-border-width`, `--kui-layout-item-padding`, `--kui-layout-item-gap`, `--kui-layout-content-gap`, `--kui-layout-control-gap`, `--kui-layout-metadata-gap`, `--kui-layout-highlight-inset`, `--kui-layout-rounded-radius`, `--kui-layout-pill-radius`, `--kui-toolbar-group-inner-size`, `--kui-toolbar-group-size`, `--kui-toolbar-gap`, `--kui-safe-area-block-start`, `--kui-safe-area-block-end`, `--kui-safe-area-inline-start`, `--kui-safe-area-inline-end`, `--kui-radius-s`, `--kui-radius-m`, `--kui-radius-l`, `--kui-radius-circle`, `--kui-radius-pill`, `--kui-disclosure-icon-scale`, `--kui-color-surface`, `--kui-color-surface-raised`, `--kui-color-surface-lowered`, `--kui-color-text`, `--kui-color-text-quiet`, `--kui-color-text-link`, `--kui-color-neutral-fill-quiet`, `--kui-color-neutral-fill-normal`, `--kui-color-neutral-fill-loud`, `--kui-color-neutral-border-quiet`, `--kui-color-neutral-border-normal`, `--kui-color-neutral-border-loud`, `--kui-color-neutral-on-quiet`, `--kui-color-neutral-on-normal`, `--kui-color-neutral-on-loud`, `--kui-color-brand-fill-loud`, `--kui-color-brand-fill-quiet`, `--kui-color-brand-fill-normal`, `--kui-color-brand-border-quiet`, `--kui-color-brand-border-normal`, `--kui-color-brand-border-loud`, `--kui-color-brand-on-quiet`, `--kui-color-brand-on-normal`, `--kui-color-brand-on-loud`, `--kui-color-brand-on-fill`, `--kui-color-pop-fill-loud`, `--kui-color-pop-fill-quiet`, `--kui-color-pop-fill-normal`, `--kui-color-pop-border-quiet`, `--kui-color-pop-border-normal`, `--kui-color-pop-border-loud`, `--kui-color-pop-on-quiet`, `--kui-color-pop-on-normal`, `--kui-color-pop-on-loud`, `--kui-color-pop-on-fill`, `--kui-color-success-fill-loud`, `--kui-color-success-fill-quiet`, `--kui-color-success-fill-normal`, `--kui-color-success-border-quiet`, `--kui-color-success-border-normal`, `--kui-color-success-border-loud`, `--kui-color-success-on-quiet`, `--kui-color-success-on-normal`, `--kui-color-success-on-loud`, `--kui-color-success-on-fill`, `--kui-color-warning-fill-loud`, `--kui-color-warning-fill-quiet`, `--kui-color-warning-fill-normal`, `--kui-color-warning-border-quiet`, `--kui-color-warning-border-normal`, `--kui-color-warning-border-loud`, `--kui-color-warning-on-quiet`, `--kui-color-warning-on-normal`, `--kui-color-warning-on-loud`, `--kui-color-warning-on-fill`, `--kui-color-danger-fill-loud`, `--kui-color-danger-fill-quiet`, `--kui-color-danger-fill-normal`, `--kui-color-danger-border-quiet`, `--kui-color-danger-border-normal`, `--kui-color-danger-border-loud`, `--kui-color-danger-on-quiet`, `--kui-color-danger-on-normal`, `--kui-color-danger-on-loud`, `--kui-color-danger-on-fill`, `--kui-focus-ring`, `--kui-shadow-s`, `--kui-shadow-l`. Set a token only where its public contract allows; prefer a prop.
59
59
 
60
60
  Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
61
61
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  `@kerfjs/ui:list-header` · Kerf UI · component · Navigation
6
6
 
7
- Configurable section headings with full-width or shrink-wrapped inline presentation, semantic count/status indicators, density, group dividers, a logical-end action, disclosure states, and protected native-popover trigger metadata.
7
+ Configurable section headings with five independent label sizes, full-width or shrink-wrapped inline presentation, semantic count/status indicators, density, group dividers, a logical-end action, disclosure states, and protected native-popover trigger metadata.
8
8
 
9
9
  ## When to use
10
10
 
@@ -34,6 +34,7 @@ Exact prop names and types: [`@kerfjs/ui/list-header`](../public-api-signatures-
34
34
  **Variants (configure, do not restyle):**
35
35
 
36
36
  - heading
37
+ - label sizes xs, sm (default), m, l, xl
37
38
  - count
38
39
  - badge
39
40
  - action
@@ -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.72` and `kerfjs@5.0.0-beta.72`. 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.74` and `kerfjs@5.0.0-beta.74`. 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/css-values`
6
6
 
@@ -527,6 +527,7 @@ type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undef
527
527
  'data-has-badge'?: never;
528
528
  'data-has-count'?: never;
529
529
  'data-density'?: never;
530
+ 'data-size'?: never;
530
531
  'data-divider'?: never;
531
532
  'data-inline'?: never;
532
533
  'data-width'?: never;
@@ -543,6 +544,8 @@ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | un
543
544
  }>;
544
545
  interface ListHeaderBaseProps {
545
546
  label: string;
547
+ /** Visual label size, independent of the native heading level. Default `sm`. */
548
+ size?: 'xs' | 'sm' | 'm' | 'l' | 'xl';
546
549
  density?: 'standard' | 'compact';
547
550
  divider?: 'none' | 'before' | 'after' | 'both';
548
551
  /** Shrink-wrap the header without its default outer margin, border, or padding. */
@@ -614,7 +617,7 @@ type ListHeaderIndicatorProps = {
614
617
  status?: SafeHtml;
615
618
  };
616
619
  type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps & ListHeaderModeProps;
617
- declare function ListHeader({ label, headingLevel, count, countLabel, badge, status, density, divider, inline, width, indicatorTone, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, slot, }: ListHeaderProps): SafeHtml;
620
+ declare function ListHeader({ label, size, headingLevel, count, countLabel, badge, status, density, divider, inline, width, indicatorTone, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, slot, }: ListHeaderProps): SafeHtml;
618
621
 
619
622
  export { ListHeader, type ListHeaderProps };
620
623
  ```
package/ai/skill.md CHANGED
@@ -253,6 +253,8 @@ Use `--kui-resizable-region-background` for a region surface and `contentOverflo
253
253
 
254
254
  Use `ListItem density="spacious"` for larger option rows, `multiline` plus `multilineIconAlign="center"` when its icon should center on wrapped copy, `divider` for row separators, and `state="drag-target"` for the drop target treatment. Public `--kui-list-item-*` tokens adjust row, icon, label, and trailing geometry and color; ListHeader height, title padding, and border tokens adjust section density.
255
255
 
256
+ Set `ListHeader.size` to `xs`, `sm`, `m`, `l`, or `xl` for label scale. The default `sm` remains the small uppercase style shared with `FieldLabel`; `xs` is 2xs and capitalized, while `m`/`l`/`xl` match themed h5/h4/h3 typography. Choose `headingLevel` separately from the document outline.
257
+
256
258
  For a filled command list, scope `--kui-list-item-background`, `--kui-list-item-color`, `--kui-list-item-border`, `--kui-list-item-hover-background`, `--kui-list-item-hover-border`, `--kui-list-item-selected-background`, `--kui-list-item-selected-color`, and `--kui-list-item-selected-border` at the List or ListItem boundary. Keep global neutral and brand border colors semantic.
257
259
 
258
260
  Use `Toolbar responsive="trailing-priority"` when an expanded trailing search needs a full second row below its heading at `responsiveAt`. Configure a leading minimum width, trailing gap and justification, or zone inline padding with the public `--kui-toolbar-*` tokens rather than selectors into toolbar zones.
@@ -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.72` 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.74` 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';
@@ -55,6 +55,8 @@ function isWebAwesomeTagEntry(entry) {
55
55
  export function componentOwnershipFacts(entries) {
56
56
  const classOwners = new Map();
57
57
  const blockOwners = new Map();
58
+ const classCoOwners = new Map();
59
+ const blockCoOwners = new Map();
58
60
  const dataComponentOwners = new Map();
59
61
  const tagOwners = new Map();
60
62
  const descendantRootAttributeOwners = [];
@@ -79,6 +81,23 @@ export function componentOwnershipFacts(entries) {
79
81
  styles.has(resolve(right.catalogDirectory, path)),
80
82
  );
81
83
  };
84
+ const recordOwner = (owners, coOwners, key, entry) => {
85
+ const previous = owners.get(key);
86
+ if (!owners.has(key)) owners.set(key, entry);
87
+ else if (
88
+ previous?.key !== entry.key &&
89
+ previous &&
90
+ sharesStylesheet(previous, entry)
91
+ ) {
92
+ owners.set(key, null);
93
+ coOwners.set(key, [previous, entry]);
94
+ } else if (
95
+ previous === null &&
96
+ coOwners.get(key).some((owner) => sharesStylesheet(owner, entry)) &&
97
+ !coOwners.get(key).some((owner) => owner.key === entry.key)
98
+ )
99
+ coOwners.get(key).push(entry);
100
+ };
82
101
  for (const entry of entries) {
83
102
  const boundaries = entry.boundaries ?? {};
84
103
  if (boundaries.rootElement && boundaries.descendantRootAttribute)
@@ -88,17 +107,9 @@ export function componentOwnershipFacts(entries) {
88
107
  Boolean,
89
108
  ),
90
109
  )) {
91
- const previous = classOwners.get(className);
92
- if (!classOwners.has(className)) classOwners.set(className, entry);
93
- else if (previous && sharesStylesheet(previous, entry))
94
- // The class is co-owned. A selector cannot attribute it to one
95
- // component without depending on catalog order.
96
- classOwners.set(className, null);
110
+ recordOwner(classOwners, classCoOwners, className, entry);
97
111
  const block = bemBlock(className);
98
- const previousBlock = blockOwners.get(block);
99
- if (!blockOwners.has(block)) blockOwners.set(block, entry);
100
- else if (previousBlock && sharesStylesheet(previousBlock, entry))
101
- blockOwners.set(block, null);
112
+ recordOwner(blockOwners, blockCoOwners, block, entry);
102
113
  if (
103
114
  className.startsWith('kui-') &&
104
115
  !className.includes('__') &&
@@ -133,6 +144,8 @@ export function componentOwnershipFacts(entries) {
133
144
  return {
134
145
  classOwners,
135
146
  blockOwners,
147
+ classCoOwners,
148
+ blockCoOwners,
136
149
  dataComponentOwners,
137
150
  tagOwners,
138
151
  descendantRootAttributeOwners,
@@ -147,8 +160,8 @@ export function componentOwnershipFacts(entries) {
147
160
  * cataloged anatomy class. `:has()` / `:not()` arguments are removed because
148
161
  * keying on or excluding a component does not style it. A subject that is a
149
162
  * `::part()` is left to the shadow-part rule (`KUI-L011`).
150
- * Returns `{ entry, via, name }` records; `isForeign(entry)` decides which
151
- * entries the stylesheet does not own.
163
+ * Returns `{ entry, via, name }` records, or `{ coOwners, via, name }` for a
164
+ * shared class. `isForeign(entry)` decides which owners the stylesheet lacks.
152
165
  */
153
166
  export function restyledComponents(
154
167
  selectorList,
@@ -171,17 +184,32 @@ export function restyledComponents(
171
184
  seen.add(key);
172
185
  found.push({ entry, via, name });
173
186
  };
187
+ const addCoOwners = (className) => {
188
+ if (classOwnerFor(facts, className, componentMode) !== null) return;
189
+ const owners =
190
+ facts.classCoOwners.get(className) ??
191
+ (componentMode
192
+ ? facts.blockCoOwners.get(bemBlock(className))
193
+ : undefined);
194
+ if (!owners || !owners.every((entry) => isForeign(entry))) return;
195
+ const name = `.${className}`;
196
+ if (seen.has(`co-owned|class|${name}`)) return;
197
+ seen.add(`co-owned|class|${name}`);
198
+ found.push({ coOwners: owners, via: 'class', name });
199
+ };
174
200
  for (const parts of complexSelectorParts(selectorList)) {
175
201
  const subject = parts.at(-1)?.compound;
176
202
  if (!subject || /::part\(/i.test(subject)) continue;
177
203
  const bare = withoutRelationalArguments(subject);
178
204
  const subjectClasses = classNames(bare);
179
- for (const className of classNames(bare))
205
+ for (const className of subjectClasses) {
180
206
  add(
181
207
  classOwnerFor(facts, className, componentMode),
182
208
  'class',
183
209
  `.${className}`,
184
210
  );
211
+ addCoOwners(className);
212
+ }
185
213
  for (const value of dataComponents(bare))
186
214
  add(
187
215
  facts.dataComponentOwners.get(value),
@@ -715,17 +715,21 @@ function inspectComponentOwnership(
715
715
  isForeign,
716
716
  ownershipEvidence,
717
717
  );
718
- for (const { entry, via, name } of restyled)
718
+ for (const { entry, coOwners, via, name } of restyled)
719
719
  diagnostics.push(
720
720
  diagnostic(
721
721
  'KUI-L019',
722
722
  location(file, rule),
723
- via === 'descendant'
724
- ? `\`${name}\` reaches an unclassed descendant inside ${componentLabel(entry)} (${properties.join(', ')}); components own their styles. Use a child combinator or an own class to target your element, or configure the child through ${configurationFor(entry)}. ${reportGap(entry)}`
725
- : `\`${name}\` makes ${componentLabel(entry)} the subject of an application rule (${properties.join(', ')}); components own their styles. Configure it through ${configurationFor(entry)}. To place your own content in its context, style your own element (\`${name} > .your-element\`). ${reportGap(entry)}`,
723
+ coOwners
724
+ ? `\`${name}\` makes a class co-owned by ${coOwners.map((owner) => componentLabel(owner)).join(', ')} the subject of an application rule (${properties.join(', ')}); those components own their styles. Configure an owning component or style your own element instead.`
725
+ : via === 'descendant'
726
+ ? `\`${name}\` reaches an unclassed descendant inside ${componentLabel(entry)} (${properties.join(', ')}); components own their styles. Use a child combinator or an own class to target your element, or configure the child through ${configurationFor(entry)}. ${reportGap(entry)}`
727
+ : `\`${name}\` makes ${componentLabel(entry)} the subject of an application rule (${properties.join(', ')}); components own their styles. Configure it through ${configurationFor(entry)}. To place your own content in its context, style your own element (\`${name} > .your-element\`). ${reportGap(entry)}`,
726
728
  {
727
729
  selector: rule.selector,
728
- component: entry.key,
730
+ ...(coOwners
731
+ ? { components: coOwners.map((owner) => owner.key) }
732
+ : { component: entry.key }),
729
733
  via,
730
734
  target: name,
731
735
  properties,
@@ -1856,6 +1860,11 @@ export async function analyzeUiProject({
1856
1860
  ownership === 'component'
1857
1861
  ? await ownershipGroupEntries(ownershipGroups, root, contents)
1858
1862
  : [];
1863
+ const groupStyles = new Set(
1864
+ groupEntries.flatMap((entry) =>
1865
+ entry.styleSources.map((style) => resolve(entry.catalogDirectory, style)),
1866
+ ),
1867
+ );
1859
1868
  const profileContexts = new Map();
1860
1869
  const loadContext = async (file) => {
1861
1870
  const startDirectory = file === root ? root : dirname(file);
@@ -1880,12 +1889,32 @@ export async function analyzeUiProject({
1880
1889
  const ownershipEntries = [
1881
1890
  ...entries,
1882
1891
  ...groupEntries,
1883
- ...implicitEntries.filter(
1884
- (entry) =>
1885
- !catalogedSources.has(
1886
- resolve(entry.catalogDirectory, entry.componentSource),
1887
- ),
1888
- ),
1892
+ ...implicitEntries.flatMap((entry) => {
1893
+ const source = resolve(
1894
+ entry.catalogDirectory,
1895
+ entry.componentSource,
1896
+ );
1897
+ if (catalogedSources.has(source)) return [];
1898
+ if (
1899
+ groupEntries.some((group) => {
1900
+ const relativeSource = relative(
1901
+ group.catalogDirectory,
1902
+ source,
1903
+ ).replaceAll('\\', '/');
1904
+ return group.ownershipGroupSources.some((path) =>
1905
+ path.endsWith('/')
1906
+ ? relativeSource.startsWith(path)
1907
+ : relativeSource === path,
1908
+ );
1909
+ })
1910
+ )
1911
+ return [];
1912
+ const styleSources = entry.styleSources.filter(
1913
+ (style) =>
1914
+ !groupStyles.has(resolve(entry.catalogDirectory, style)),
1915
+ );
1916
+ return styleSources.length ? [{ ...entry, styleSources }] : [];
1917
+ }),
1889
1918
  ];
1890
1919
  return {
1891
1920
  profileResult,
package/dist/app-tab.js CHANGED
@@ -1,4 +1,4 @@
1
- export { AppTab } from './chunk-Q6YU3BT7.js';
1
+ export { AppTab } from './chunk-7KHKLG5C.js';
2
2
 
3
3
 
4
4
 
package/dist/badge.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Badge } from './chunk-MP2GDBQZ.js';
1
+ export { Badge } from './chunk-ALGYHYXP.js';
2
2
 
3
3
  //# sourceMappingURL=badge.js.map
package/dist/catalog.js CHANGED
@@ -1,14 +1,14 @@
1
- import { Workbench } from './chunk-3UE24JSC.js';
1
+ import { Workbench } from './chunk-33EOFAUN.js';
2
2
 
3
3
 
4
- import { PopupMenu } from './chunk-J3KGJ6P6.js';
4
+ import { PopupMenu } from './chunk-4NG4Y2Q6.js';
5
5
 
6
6
  import { Row } from './chunk-VZOFZPSO.js';
7
- import { ListHeader } from './chunk-M6DEEBEF.js';
7
+ import { ListHeader } from './chunk-RU2EMR56.js';
8
8
  import { Text } from './chunk-YDGNU2OD.js';
9
9
  import { ListInsetText } from './chunk-DG6G5EPF.js';
10
- import { ListItem } from './chunk-NC4CFOID.js';
11
- import { ToolbarText } from './chunk-R5F5XUJ7.js';
10
+ import { ListItem } from './chunk-6LAQWWEX.js';
11
+ import { ToolbarText } from './chunk-IJNYCPAU.js';
12
12
 
13
13
 
14
14
 
@@ -18,7 +18,7 @@ import { ToolbarText } from './chunk-R5F5XUJ7.js';
18
18
 
19
19
  import { List } from './chunk-W2AC6Q2A.js';
20
20
 
21
- import { ToolbarControlGroup, ToolbarActionLink } from './chunk-QJ5VFCW3.js';
21
+ import { ToolbarControlGroup, ToolbarActionLink } from './chunk-BGD7EHUE.js';
22
22
  import { Toolbar } from './chunk-RGS4WX2M.js';
23
23
  import { LucideIcon } from './chunk-WTSNV5RG.js';
24
24
 
@@ -136,7 +136,8 @@ function CatalogSectionEntries({
136
136
  status: entry.tags?.join(" \xB7 "),
137
137
  selected: active === entry.id,
138
138
  title: entry.description,
139
- multiline: true
139
+ multiline: true,
140
+ rootAttributes: { "data-catalog-entry-name": entry.name }
140
141
  }
141
142
  )) })
142
143
  ] });
@@ -148,25 +149,43 @@ function CatalogSecondarySections({
148
149
  toggleAction
149
150
  }) {
150
151
  const open = !group.collapsible || Boolean(group.expanded);
151
- return /* @__PURE__ */ jsxs(List, { dividerSides: "t", controlInsets: "t", children: [
152
- group.collapsible ? /* @__PURE__ */ jsx(
153
- ListHeader,
154
- {
155
- label: group.label,
156
- toggle: true,
157
- expanded: open,
158
- action: toggleAction
159
- }
160
- ) : /* @__PURE__ */ jsx(ListHeader, { label: group.label }),
161
- open ? /* @__PURE__ */ jsx(List, { gap: "m", rootAttributes: { "data-catalog-secondary": "" }, children: group.sections.map((section) => /* @__PURE__ */ jsx(
162
- CatalogSectionEntries,
163
- {
164
- section,
165
- active,
166
- selectAction
167
- }
168
- )) }) : null
169
- ] });
152
+ return /* @__PURE__ */ jsxs(
153
+ List,
154
+ {
155
+ dividerSides: "t",
156
+ controlInsets: "t",
157
+ rootAttributes: { "data-catalog-secondary-group": group.label },
158
+ children: [
159
+ group.collapsible ? /* @__PURE__ */ jsx(
160
+ ListHeader,
161
+ {
162
+ label: group.label,
163
+ toggle: true,
164
+ expanded: open,
165
+ action: toggleAction
166
+ }
167
+ ) : /* @__PURE__ */ jsx(ListHeader, { label: group.label }),
168
+ /* @__PURE__ */ jsx(
169
+ List,
170
+ {
171
+ gap: "m",
172
+ rootAttributes: {
173
+ "data-catalog-secondary": "",
174
+ "data-catalog-secondary-collapsed": String(!open)
175
+ },
176
+ children: group.sections.map((section) => /* @__PURE__ */ jsx(
177
+ CatalogSectionEntries,
178
+ {
179
+ section,
180
+ active,
181
+ selectAction
182
+ }
183
+ ))
184
+ }
185
+ )
186
+ ]
187
+ }
188
+ );
170
189
  }
171
190
  function CatalogSectionList({
172
191
  sections,
@@ -193,6 +212,17 @@ function CatalogSidebar({
193
212
  }) {
194
213
  return /* @__PURE__ */ jsx("nav", { "aria-label": `${brand.title} components`, children: /* @__PURE__ */ jsxs(List, { gap: "m", controlInsets: "b", children: [
195
214
  brand.subtitle ? /* @__PURE__ */ jsx(ListInsetText, { sides: "rl", children: /* @__PURE__ */ jsx(Text, { variant: "span", size: "compact", tone: "quiet", children: brand.subtitle }) }) : null,
215
+ /* @__PURE__ */ jsx(ListInsetText, { sides: "rl", children: /* @__PURE__ */ jsx(
216
+ "input",
217
+ {
218
+ class: "kui-catalog__filter",
219
+ type: "search",
220
+ "aria-label": "Filter catalog",
221
+ placeholder: "Filter items or headings",
222
+ autocomplete: "off",
223
+ "data-catalog-filter": true
224
+ }
225
+ ) }),
196
226
  /* @__PURE__ */ jsx(
197
227
  CatalogSectionList,
198
228
  {
@@ -210,6 +240,14 @@ function CatalogSidebar({
210
240
  toggleAction: toggleSecondaryAction
211
241
  }
212
242
  ) : null,
243
+ /* @__PURE__ */ jsx(
244
+ ListInsetText,
245
+ {
246
+ sides: "rl",
247
+ rootAttributes: { "data-catalog-filter-empty": "" },
248
+ children: /* @__PURE__ */ jsx(Text, { variant: "span", tone: "quiet", children: "No matching items" })
249
+ }
250
+ ),
213
251
  footer
214
252
  ] }) });
215
253
  }