@kerfjs/ui 5.0.0-beta.67 → 5.0.0-beta.69

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 (103) hide show
  1. package/README.md +3 -0
  2. package/ai/application-ui-profile-sync.cjs +20 -1
  3. package/ai/application-ui-profile.d.ts +4 -0
  4. package/ai/application-ui-profile.mjs +27 -0
  5. package/ai/application-ui-profile.schema.json +10 -0
  6. package/ai/component-catalog-extension.schema.json +12 -0
  7. package/ai/component-catalog.json +14 -4
  8. package/ai/component-catalog.schema.json +14 -0
  9. package/ai/component-composition-rules.json +2 -2
  10. package/ai/component-composition.d.ts +2 -0
  11. package/ai/component-composition.json +2 -2
  12. package/ai/component-composition.schema.json +24 -0
  13. package/ai/components/loading-spinner.md +1 -0
  14. package/ai/components/tab-bar.md +5 -4
  15. package/ai/components/toolbar-control-group.md +2 -2
  16. package/ai/public-api-signatures-v1.md +34 -14
  17. package/ai/skill.md +18 -3
  18. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  19. package/analyzer/index.d.mts +1 -0
  20. package/analyzer/index.mjs +108 -12
  21. package/dist/catalog.js +4 -4
  22. package/dist/catalog.js.map +1 -1
  23. package/dist/{chunk-P4UXSYIJ.js → chunk-2ZOLX3IE.js} +2 -2
  24. package/dist/{chunk-P4UXSYIJ.js.map → chunk-2ZOLX3IE.js.map} +1 -1
  25. package/dist/{chunk-E36EVCKG.js → chunk-47CKKLBE.js} +2 -2
  26. package/dist/{chunk-E36EVCKG.js.map → chunk-47CKKLBE.js.map} +1 -1
  27. package/dist/{chunk-JCV4GXWB.js → chunk-6DFDTWGG.js} +100 -61
  28. package/dist/chunk-6DFDTWGG.js.map +1 -0
  29. package/dist/{chunk-IAOAZUOO.js → chunk-7OKX3EF6.js} +2 -2
  30. package/dist/{chunk-IAOAZUOO.js.map → chunk-7OKX3EF6.js.map} +1 -1
  31. package/dist/{chunk-C74LITXE.js → chunk-B3XWUX3A.js} +2 -2
  32. package/dist/{chunk-C74LITXE.js.map → chunk-B3XWUX3A.js.map} +1 -1
  33. package/dist/{chunk-F7OCOBTG.js → chunk-CRVDF4A6.js} +4 -1
  34. package/dist/chunk-CRVDF4A6.js.map +1 -0
  35. package/dist/{chunk-TGM424PI.js → chunk-H7IODXGY.js} +3 -3
  36. package/dist/{chunk-TGM424PI.js.map → chunk-H7IODXGY.js.map} +1 -1
  37. package/dist/chunk-KVBRMEPM.js +44 -0
  38. package/dist/chunk-KVBRMEPM.js.map +1 -0
  39. package/dist/{chunk-EN4LDR5Y.js → chunk-NC4CFOID.js} +2 -2
  40. package/dist/{chunk-EN4LDR5Y.js.map → chunk-NC4CFOID.js.map} +1 -1
  41. package/dist/{chunk-5SYE4CCT.js → chunk-PQZPVBP4.js} +25 -18
  42. package/dist/chunk-PQZPVBP4.js.map +1 -0
  43. package/dist/{chunk-NXWE5S57.js → chunk-QJ5VFCW3.js} +12 -3
  44. package/dist/chunk-QJ5VFCW3.js.map +1 -0
  45. package/dist/{chunk-RKTTF3L4.js → chunk-X3TPECWG.js} +1 -1
  46. package/dist/chunk-X3TPECWG.js.map +1 -0
  47. package/dist/{chunk-VY6YRCTV.js → chunk-X3X4KRDK.js} +2 -2
  48. package/dist/{chunk-VY6YRCTV.js.map → chunk-X3X4KRDK.js.map} +1 -1
  49. package/dist/collapsible-panel.js +2 -2
  50. package/dist/empty-state.js +2 -1
  51. package/dist/index.js +10 -10
  52. package/dist/list-action-row.js +1 -1
  53. package/dist/list-item.js +1 -1
  54. package/dist/loading-spinner.d.ts +7 -2
  55. package/dist/loading-spinner.js +2 -1
  56. package/dist/nav-stack.js +1 -1
  57. package/dist/select.d.ts +4 -2
  58. package/dist/select.js +1 -1
  59. package/dist/split-view.js +1 -1
  60. package/dist/styles/app-tab.css +29 -0
  61. package/dist/styles/loading-spinner.css +4 -0
  62. package/dist/styles/select.css +79 -0
  63. package/dist/styles/tab-bar.css +12 -8
  64. package/dist/styles/token-search-field.css +28 -10
  65. package/dist/styles/toolbar-control-group.css +96 -3
  66. package/dist/styles/webawesome.css +17 -0
  67. package/dist/tab-bar.d.ts +2 -0
  68. package/dist/tab-bar.js +1 -1
  69. package/dist/token-search-field.js +1 -1
  70. package/dist/toolbar-control-group.d.ts +13 -5
  71. package/dist/toolbar-control-group.js +2 -1
  72. package/dist/wire-catalog.js +1 -1
  73. package/dist/wire-scroll-dividers.d.ts +7 -4
  74. package/dist/wire-scroll-dividers.js +1 -1
  75. package/dist/wire-token-search-fields.js +1 -1
  76. package/dist/workbench.js +1 -1
  77. package/docs/accessibility.md +14 -0
  78. package/docs/apple-hig.md +1 -0
  79. package/docs/catalog.md +3 -0
  80. package/docs/component-contract.md +65 -10
  81. package/docs/component-selection.md +24 -6
  82. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  83. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  84. package/docs/design/templates/toolbar-dark.svg +1 -1
  85. package/docs/design/templates/toolbar.svg +1 -1
  86. package/docs/examples/component-catalog-extension.json +1 -0
  87. package/docs/layout.md +6 -0
  88. package/docs/ui-analyzer.md +12 -0
  89. package/docs/ui-doctor.md +15 -3
  90. package/docs/webawesome-theme.md +7 -0
  91. package/doctor/cli.mjs +5 -0
  92. package/doctor/config.schema.json +2 -0
  93. package/doctor/index.d.mts +3 -0
  94. package/doctor/index.mjs +27 -1
  95. package/llms.txt +15 -5
  96. package/package.json +1 -1
  97. package/dist/chunk-5SYE4CCT.js.map +0 -1
  98. package/dist/chunk-F7OCOBTG.js.map +0 -1
  99. package/dist/chunk-JCV4GXWB.js.map +0 -1
  100. package/dist/chunk-NXWE5S57.js.map +0 -1
  101. package/dist/chunk-RKTTF3L4.js.map +0 -1
  102. package/dist/chunk-ZLWQZUGF.js +0 -27
  103. package/dist/chunk-ZLWQZUGF.js.map +0 -1
@@ -38,6 +38,35 @@
38
38
  var(--_kui-app-tab-pinned-default-background, var(--kui-color-surface))
39
39
  );
40
40
  }
41
+ .kui-app-tab[data-pinned="true"]::before {
42
+ content: "";
43
+ position: absolute;
44
+ z-index: -1;
45
+ inset-block: 0;
46
+ inset-inline-start: calc(
47
+ -1 *
48
+ (
49
+ var(--kui-tab-bar-strip-padding, 1px) +
50
+ var(--kui-tab-bar-strip-border-width, 1px)
51
+ )
52
+ );
53
+ inset-inline-end: 0;
54
+ background: var(
55
+ --kui-app-tab-pinned-background,
56
+ var(--_kui-app-tab-pinned-default-background, var(--kui-color-surface))
57
+ );
58
+ }
59
+ /* The leading overflow line belongs after the pinned tab, where its scrolling
60
+ peers disappear. Reuse the opaque backing so drag indicators can still use
61
+ ::after. */
62
+ .kui-tab-bar__tabs:dir(ltr)[data-scroll-overflow*="l"]
63
+ > .kui-app-tab[data-pinned="true"]::before,
64
+ .kui-tab-bar__tabs:dir(rtl)[data-scroll-overflow*="r"]
65
+ > .kui-app-tab[data-pinned="true"]::before {
66
+ inset-inline-end: -1px;
67
+ border-inline-end: 1px solid
68
+ var(--kui-tab-bar-divider-color, var(--kui-color-neutral-border-normal));
69
+ }
41
70
  .kui-tab-bar[data-presentation="segmented"] .kui-app-tab[data-pinned="true"],
42
71
  .kui-tab-bar[data-presentation="inspector"] .kui-app-tab[data-pinned="true"] {
43
72
  --_kui-app-tab-pinned-default-background: var(
@@ -5,6 +5,10 @@
5
5
  overflow: visible;
6
6
  fill: currentColor;
7
7
  }
8
+ .kui-loading-spinner[data-size] {
9
+ width: var(--_kui-loading-spinner-size);
10
+ height: var(--_kui-loading-spinner-size);
11
+ }
8
12
  .kui-loading-spinner path {
9
13
  transform-origin: 12px 12px;
10
14
  animation: kui-loading-spinner-rotate 0.85s linear infinite;
@@ -40,6 +40,55 @@
40
40
  width: max-content;
41
41
  max-width: 100%;
42
42
  }
43
+ .kui-select[data-presentation="title"] {
44
+ width: max-content;
45
+ max-width: 100%;
46
+ }
47
+ .kui-select[data-presentation="title"]::part(combobox) {
48
+ width: max-content;
49
+ min-height: 2.25rem;
50
+ padding-inline: 0;
51
+ border-color: transparent;
52
+ background: transparent;
53
+ box-shadow: none;
54
+ font-size: var(--kui-font-l, 1.25rem);
55
+ font-weight: 700;
56
+ }
57
+ .kui-select[data-presentation="title"]::part(display-input) {
58
+ field-sizing: content;
59
+ flex: 0 0 auto;
60
+ }
61
+ .kui-select[data-presentation="title"]::part(expand-icon) {
62
+ margin-inline-start: 0;
63
+ }
64
+ /* A Toolbar has an 8px inset; ListItem text starts at 17px after its own
65
+ margin, border, and padding. The group contributes 3px, so the title
66
+ Select advances 6px to share the list's text edge. */
67
+ .kui-toolbar-control-group > .kui-select[data-presentation="title"] {
68
+ margin-inline-start: 0.375rem;
69
+ }
70
+ .kui-select[data-presentation="inline"] {
71
+ width: max-content;
72
+ max-width: 100%;
73
+ }
74
+ .kui-select[data-presentation="inline"]::part(combobox) {
75
+ width: max-content;
76
+ min-height: 0;
77
+ padding: 0;
78
+ border-width: 0;
79
+ background: transparent;
80
+ }
81
+ .kui-select--custom-selected[data-presentation="inline"]::part(start) {
82
+ flex: 0 0 auto;
83
+ }
84
+ .kui-select--custom-selected[data-presentation="inline"]
85
+ .kui-select__custom-selected {
86
+ margin-inline-end: 0 !important;
87
+ }
88
+ .kui-select--custom-selected[data-presentation="inline"]
89
+ .kui-select__custom-selected-content {
90
+ display: flex;
91
+ }
43
92
  .kui-select[data-focus-ring-owner="group"]:focus-within::part(combobox) {
44
93
  outline: none;
45
94
  box-shadow: none;
@@ -101,6 +150,12 @@
101
150
  outline: var(--kui-focus-ring);
102
151
  outline-offset: 1px;
103
152
  }
153
+ .kui-select[data-focus-ring-inset="true"][data-focus-ring-owner="select"]:focus-within:not(
154
+ [open]
155
+ )::part(combobox) {
156
+ outline: var(--kui-focus-ring);
157
+ outline-offset: -3px;
158
+ }
104
159
  /* The popup-menu wa-button draws the same chevron glyph at 0.875em inside a
105
160
  20px box; match its box, spacing, and glyph size rather than the smaller
106
161
  form-field disclosure scale. */
@@ -332,6 +387,30 @@
332
387
  width: max-content;
333
388
  max-width: 100%;
334
389
  }
390
+ .kui-select--placeholder[data-presentation="title"] {
391
+ width: max-content;
392
+ max-width: 100%;
393
+ }
394
+ .kui-select--placeholder[data-presentation="title"]
395
+ .kui-select__placeholder-box {
396
+ min-height: 2.25rem;
397
+ padding-inline: 0;
398
+ border-color: transparent;
399
+ background: transparent;
400
+ font-size: var(--kui-font-l, 1.25rem);
401
+ font-weight: 700;
402
+ }
403
+ .kui-select--placeholder[data-presentation="inline"] {
404
+ width: max-content;
405
+ max-width: 100%;
406
+ }
407
+ .kui-select--placeholder[data-presentation="inline"]
408
+ .kui-select__placeholder-box {
409
+ min-height: 0;
410
+ padding: 0;
411
+ border-width: 0;
412
+ background: transparent;
413
+ }
335
414
  .kui-select__placeholder-hint {
336
415
  /* The live wa-select hint part: small body text at Web Awesome's form-control
337
416
  hint metrics (1.6 line height, 7px below the box), inset like the value. */
@@ -136,12 +136,12 @@
136
136
  margin-inline-start: calc(-1px - var(--kui-toolbar-gap, 0.5rem));
137
137
  }
138
138
 
139
- .kui-tab-bar[data-presentation="rail"]:dir(ltr):has(
140
- > .kui-tab-bar__tabs[data-scroll-overflow*="l"]
141
- )::before,
142
- .kui-tab-bar[data-presentation="rail"]:dir(rtl):has(
143
- > .kui-tab-bar__tabs[data-scroll-overflow*="r"]
144
- )::before,
139
+ .kui-tab-bar[data-presentation="rail"]:dir(ltr):not(
140
+ :has(> .kui-tab-bar__tabs > .kui-app-tab[data-pinned="true"])
141
+ ):has(> .kui-tab-bar__tabs[data-scroll-overflow*="l"])::before,
142
+ .kui-tab-bar[data-presentation="rail"]:dir(rtl):not(
143
+ :has(> .kui-tab-bar__tabs > .kui-app-tab[data-pinned="true"])
144
+ ):has(> .kui-tab-bar__tabs[data-scroll-overflow*="r"])::before,
145
145
  .kui-tab-bar[data-presentation="rail"]:dir(ltr):has(
146
146
  > .kui-tab-bar__tabs[data-scroll-overflow*="r"]
147
147
  )::after,
@@ -155,7 +155,9 @@
155
155
  }
156
156
 
157
157
  .kui-tab-bar:not([data-presentation="rail"])
158
- > .kui-tab-bar__tabs[data-scroll-overflow*="l"] {
158
+ > .kui-tab-bar__tabs[data-scroll-overflow*="l"]:not(
159
+ :dir(ltr):has(> .kui-app-tab[data-pinned="true"])
160
+ ) {
159
161
  border-left-color: var(
160
162
  --kui-tab-bar-divider-color,
161
163
  var(--kui-color-neutral-border-normal)
@@ -163,7 +165,9 @@
163
165
  }
164
166
 
165
167
  .kui-tab-bar:not([data-presentation="rail"])
166
- > .kui-tab-bar__tabs[data-scroll-overflow*="r"] {
168
+ > .kui-tab-bar__tabs[data-scroll-overflow*="r"]:not(
169
+ :dir(rtl):has(> .kui-app-tab[data-pinned="true"])
170
+ ) {
167
171
  border-right-color: var(
168
172
  --kui-tab-bar-divider-color,
169
173
  var(--kui-color-neutral-border-normal)
@@ -28,10 +28,6 @@
28
28
  }
29
29
 
30
30
  .kui-token-search {
31
- --kui-token-search-background: var(--kui-color-surface);
32
- --kui-token-search-border: var(--kui-color-neutral-border-normal);
33
- --kui-token-search-token-background: var(--kui-color-brand-fill-normal);
34
- --kui-token-search-token-foreground: var(--kui-color-brand-on-quiet);
35
31
  --kui-token-search-line-size: 2.125rem;
36
32
 
37
33
  display: grid;
@@ -41,10 +37,11 @@
41
37
  min-height: 2.75rem;
42
38
  padding: 0.25rem 0.5rem;
43
39
  box-sizing: border-box;
44
- border: 1px solid var(--kui-token-search-border);
40
+ border: 1px solid
41
+ var(--kui-token-search-border, var(--kui-color-neutral-border-normal));
45
42
  border-radius: var(--kui-layout-pill-radius, calc(1px + 1.3125rem));
46
43
  color: var(--kui-color-text);
47
- background: var(--kui-token-search-background);
44
+ background: var(--kui-token-search-background, var(--kui-color-surface));
48
45
  grid-template-columns: auto minmax(0, 1fr) auto auto;
49
46
  align-items: start;
50
47
  gap: var(--kui-layout-item-gap, 0.5rem);
@@ -98,6 +95,15 @@
98
95
  cursor: text;
99
96
  }
100
97
 
98
+ /* iOS can magnify a small editor on focus and leave the page magnified after
99
+ blur. Keep the desktop small-text rhythm, but give touch editors legible text
100
+ without restricting the user's own page zoom. */
101
+ @media (hover: none) and (pointer: coarse) {
102
+ .kui-token-search__editor {
103
+ font-size: max(16px, var(--kui-font-s));
104
+ }
105
+ }
106
+
101
107
  .kui-token-search__editor:empty::before {
102
108
  color: var(--kui-color-text-quiet);
103
109
  content: attr(data-placeholder);
@@ -110,8 +116,14 @@
110
116
  margin-inline: 0.125rem;
111
117
  padding-inline-start: 0.45rem;
112
118
  border-radius: var(--kui-radius-pill);
113
- color: var(--kui-token-search-token-foreground);
114
- background: var(--kui-token-search-token-background);
119
+ color: var(
120
+ --kui-token-search-token-foreground,
121
+ var(--kui-color-brand-on-quiet)
122
+ );
123
+ background: var(
124
+ --kui-token-search-token-background,
125
+ var(--kui-color-brand-fill-normal)
126
+ );
115
127
  font-size: var(--kui-font-xs);
116
128
  line-height: 1;
117
129
  white-space: nowrap;
@@ -246,7 +258,13 @@
246
258
  }
247
259
 
248
260
  .kui-token-search[data-collapsible="true"] {
249
- width: min(var(--kui-token-search-expanded-width, 30rem), 100%);
261
+ width: min(
262
+ var(
263
+ --_kui-toolbar-group-grow-search-width,
264
+ var(--kui-token-search-expanded-width, 30rem)
265
+ ),
266
+ 100%
267
+ );
250
268
  max-width: 100%;
251
269
  overflow: hidden;
252
270
  /* Size the inline axis through `width` only. A `flex-basis` of the expanded
@@ -334,7 +352,7 @@
334
352
 
335
353
  .kui-token-search[data-presentation="toolbar-group"][data-collapsible="true"]:focus-within {
336
354
  border-color: var(--kui-color-focus);
337
- background: var(--kui-token-search-background);
355
+ background: var(--kui-token-search-background, var(--kui-color-surface));
338
356
  }
339
357
 
340
358
  .kui-token-search[data-presentation="toolbar-group"][data-collapsible="true"][data-expanded="false"] {
@@ -102,6 +102,24 @@
102
102
  }
103
103
  }
104
104
 
105
+ @container kui-toolbar (max-width: 14rem) {
106
+ .kui-toolbar-control-group[data-visibility="hide-collapsed-tiny"][data-expanded="false"] {
107
+ display: none;
108
+ }
109
+ }
110
+
111
+ /* An expanded trailing control owns the compact row when sibling groups opt
112
+ into yielding. Scoping to the trailing zone keeps independent center and
113
+ leading controls visible, and the siblings return when it closes. */
114
+ @container kui-toolbar (max-width: 30rem) {
115
+ .kui-toolbar__trailing:has(> .kui-toolbar-control-group[data-expanded="true"])
116
+ > .kui-toolbar-control-group[data-visibility="yield-to-expanded-sibling"]:not(
117
+ [data-expanded="true"]
118
+ ) {
119
+ display: none;
120
+ }
121
+ }
122
+
105
123
  /* `shape="rounded"` swaps the default pill radius for the design system's softer
106
124
  rounded rectangle. The shared highlight contract derives every inner hover or
107
125
  selected surface from this outer radius and the group's full inset. */
@@ -143,6 +161,16 @@
143
161
  max-width: 100%;
144
162
  overflow-x: auto;
145
163
  }
164
+ .kui-toolbar-control-group[data-overflow="wrap"] {
165
+ max-width: 100%;
166
+ height: auto;
167
+ flex-wrap: wrap;
168
+ }
169
+ :is(.kui-toolbar__leading, .kui-toolbar__center, .kui-toolbar__trailing)
170
+ > .kui-toolbar-control-group[data-overflow="wrap"] {
171
+ margin-block-start: 0;
172
+ align-self: center;
173
+ }
146
174
  .kui-toolbar-control-group[data-content="search"] {
147
175
  width: min(var(--kui-token-search-expanded-width, 30rem), 100%);
148
176
  max-width: 100%;
@@ -179,6 +207,55 @@
179
207
  width: var(--kui-toolbar-group-size, calc(2px + 2.625rem));
180
208
  flex: 0 0 auto;
181
209
  }
210
+
211
+ .kui-toolbar-control-group[data-sizing="grow"][data-expanded="true"] {
212
+ --_kui-toolbar-group-grow-search-width: 100%;
213
+ width: auto;
214
+ min-width: min(100%, var(--kui-toolbar-group-grow-basis, 19rem));
215
+ flex: 1 1 var(--kui-toolbar-group-grow-basis, 19rem);
216
+ }
217
+ .kui-toolbar-control-group[data-sizing="fill"] {
218
+ width: 100%;
219
+ min-width: 0;
220
+ flex: 1 1 100%;
221
+ }
222
+
223
+ /* Auto margin consumes the free space before this group on its flex line;
224
+ when the expanded fill search takes a whole line, the margin resolves to 0. */
225
+ .kui-toolbar[data-responsive="stack"]
226
+ > :is(.kui-toolbar__center, .kui-toolbar__trailing)
227
+ > .kui-toolbar-control-group[data-placement="end"] {
228
+ margin-inline-start: auto;
229
+ }
230
+
231
+ /* A fill search crosses from the preceding control line to its own line.
232
+ Move its visual down one control band as it opens without changing layout. */
233
+ .kui-toolbar[data-responsive="stack"]
234
+ > :is(.kui-toolbar__center, .kui-toolbar__trailing)
235
+ > .kui-toolbar-control-group[data-content="search"][data-sizing="fill"][data-expanded="true"] {
236
+ animation: kui-toolbar-fill-search-enter 0.25s ease-out both;
237
+ }
238
+
239
+ @keyframes kui-toolbar-fill-search-enter {
240
+ from {
241
+ transform: translateY(
242
+ calc(
243
+ -1 * (var(--_kui-toolbar-band) + var(--kui-toolbar-gap, 0.5rem))
244
+ )
245
+ );
246
+ }
247
+ to {
248
+ transform: translateY(0);
249
+ }
250
+ }
251
+
252
+ @container kui-toolbar (max-width: 30rem) {
253
+ .kui-toolbar-control-group[data-sizing="grow"][data-expanded="true"] {
254
+ width: 100%;
255
+ min-width: 0;
256
+ flex: 1 1 100%;
257
+ }
258
+ }
182
259
  .kui-toolbar-control-group[data-content="text"]
183
260
  > :is(button, wa-button, .kui-toolbar-action-link),
184
261
  .kui-toolbar-control-group[data-content="mixed"]
@@ -284,7 +361,12 @@
284
361
  place-items: center;
285
362
  }
286
363
 
287
- .kui-toolbar-control-group > button:hover {
364
+ .kui-toolbar-control-group > button:disabled {
365
+ opacity: 0.5;
366
+ cursor: not-allowed;
367
+ }
368
+
369
+ .kui-toolbar-control-group > button:not(:disabled):hover {
288
370
  border-color: var(--kui-toolbar-control-border);
289
371
  background: var(--kui-toolbar-control-hover-background);
290
372
  }
@@ -495,7 +577,7 @@
495
577
 
496
578
  .kui-toolbar-control-group[data-single="true"]:has(
497
579
  > :is(
498
- button,
580
+ button:not(:disabled),
499
581
  wa-button,
500
582
  .kui-popup-menu,
501
583
  wa-dropdown:not(.kui-popup-menu),
@@ -519,7 +601,12 @@
519
601
  inner layer inset from the border (KF-J9P1ZD — a heading toolbar's lone Close/
520
602
  Done action). */
521
603
  .kui-toolbar-control-group:not([data-single="true"]):has(
522
- > :is(button, wa-button, .kui-select, .kui-toolbar-action-link):only-child
604
+ > :is(
605
+ button:not(:disabled),
606
+ wa-button,
607
+ .kui-select,
608
+ .kui-toolbar-action-link
609
+ ):only-child
523
610
  ):hover {
524
611
  background-color: var(--kui-toolbar-control-hover-background);
525
612
  }
@@ -653,6 +740,12 @@
653
740
  .kui-toolbar-control-group[data-content="search"] {
654
741
  transition-duration: 0s;
655
742
  }
743
+
744
+ .kui-toolbar[data-responsive="stack"]
745
+ > :is(.kui-toolbar__center, .kui-toolbar__trailing)
746
+ > .kui-toolbar-control-group[data-content="search"][data-sizing="fill"][data-expanded="true"] {
747
+ animation: none;
748
+ }
656
749
  }
657
750
 
658
751
  /* Avatar controls paint the image as chrome rather than inserting a replaced
@@ -591,6 +591,23 @@
591
591
  }
592
592
  }
593
593
 
594
+ /* Small Web Awesome fields have 14px hosts by default. Raise only the editable
595
+ shadow part on touch devices so focusing them need not magnify the page;
596
+ their compact chrome and desktop typography keep their normal sizes. */
597
+ @media (hover: none) and (pointer: coarse) {
598
+ @layer wa-theme-overrides {
599
+ :is(wa-input, wa-number-input, wa-time-input):is(
600
+ [size="xs"],
601
+ [size="s"],
602
+ [size="small"]
603
+ )::part(input),
604
+ wa-textarea:is([size="xs"], [size="s"], [size="small"])::part(textarea),
605
+ wa-select:is([size="xs"], [size="s"], [size="small"])::part(display-input) {
606
+ font-size: max(16px, var(--wa-font-size-m, 16px));
607
+ }
608
+ }
609
+ }
610
+
594
611
  @media (prefers-contrast: more) {
595
612
  @layer wa-theme-overrides {
596
613
  :where(:root),
package/dist/tab-bar.d.ts CHANGED
@@ -12,10 +12,12 @@ interface TabBarProps {
12
12
  label: string;
13
13
  children: KerfUiContent;
14
14
  leading?: KerfUiContent;
15
+ /** Tab-local action: a ToolbarControlGroup or a standalone Web Awesome button. */
15
16
  trailing?: KerfUiContent;
16
17
  /**
17
18
  * Action pinned to the far edge of the bar. Combine with an adjacent `trailing`
18
19
  * action when the tab-local and workspace-level actions must remain distinct.
20
+ * Accepts a ToolbarControlGroup or a standalone Web Awesome button.
19
21
  */
20
22
  end?: KerfUiContent;
21
23
  className?: string;
package/dist/tab-bar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { TabBar } from './chunk-RKTTF3L4.js';
1
+ export { TabBar } from './chunk-X3TPECWG.js';
2
2
 
3
3
  //# sourceMappingURL=tab-bar.js.map
@@ -1,4 +1,4 @@
1
- export { TokenSearchField, placeTokenSearchCaret, readTokenSearchField } from './chunk-5SYE4CCT.js';
1
+ export { TokenSearchField, placeTokenSearchCaret, readTokenSearchField } from './chunk-PQZPVBP4.js';
2
2
 
3
3
 
4
4
 
@@ -11,9 +11,11 @@ type ToolbarControlGroupContent = 'icon' | 'text' | 'mixed' | 'avatar' | 'search
11
11
  type ToolbarControlGroupFocusRing = 'control' | 'outline' | 'halo';
12
12
  type ToolbarControlGroupSelectedChrome = 'raised' | 'filled' | 'outline';
13
13
  type ToolbarControlGroupSelectedTone = 'brand' | 'neutral' | 'pop';
14
- type ToolbarControlGroupOverflow = 'visible' | 'scroll';
14
+ type ToolbarControlGroupOverflow = 'visible' | 'scroll' | 'wrap';
15
15
  type ToolbarControlGroupMenuInset = 'standard' | 'compact' | 'list-zero';
16
- type ToolbarControlGroupVisibility = 'always' | 'compact-only';
16
+ type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling';
17
+ type ToolbarControlGroupSizing = 'content' | 'grow' | 'fill';
18
+ type ToolbarControlGroupPlacement = 'start' | 'end';
17
19
  interface ToolbarActionLinkProps {
18
20
  href: string;
19
21
  label: string;
@@ -52,10 +54,16 @@ interface ToolbarControlGroupProps {
52
54
  nestedDropdown?: boolean;
53
55
  /** Configure the nested dropdown menu inset without consumer ::part() CSS. */
54
56
  menuInset?: ToolbarControlGroupMenuInset;
55
- /** Keep an overlong row of actions inside the available width with horizontal scrolling. */
57
+ /** Keep an overlong row inside the available width by scrolling or wrapping. */
56
58
  overflow?: ToolbarControlGroupOverflow;
57
59
  /** Responsive visibility owned by the enclosing Toolbar container. */
58
60
  visibility?: ToolbarControlGroupVisibility;
61
+ /** Intrinsic (default), grow from a basis, or occupy a full wrapping row. */
62
+ sizing?: ToolbarControlGroupSizing;
63
+ /** Place this group at the end of its flex row in a wrapping Toolbar zone. */
64
+ placement?: ToolbarControlGroupPlacement;
65
+ /** CSS length used as the minimum width and flex basis for `sizing="grow"`; defaults to 19rem. */
66
+ growBasis?: string;
59
67
  /** Move this group into the work-area toolbar when its panel collapses. */
60
68
  relocateOnCollapse?: boolean;
61
69
  /** Add contrast behind photo-backed avatar content. */
@@ -72,6 +80,6 @@ interface ToolbarControlGroupProps {
72
80
  /** Native named-slot assignment when composed inside a web component. */
73
81
  slot?: string;
74
82
  }
75
- declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance, shape, size, density, content, focusRing, selectedChrome, selectedTone, nestedDropdown, menuInset, overflow, visibility, relocateOnCollapse, scrim, busy, busyLabel, avatarImage, slot, }: ToolbarControlGroupProps): kerfjs.SafeHtml;
83
+ declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance, shape, size, density, content, focusRing, selectedChrome, selectedTone, nestedDropdown, menuInset, overflow, visibility, sizing, placement, growBasis, relocateOnCollapse, scrim, busy, busyLabel, avatarImage, slot, }: ToolbarControlGroupProps): kerfjs.SafeHtml;
76
84
 
77
- export { ToolbarActionLink, type ToolbarActionLinkProps, ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupContent, type ToolbarControlGroupDensity, type ToolbarControlGroupFocusRing, type ToolbarControlGroupMenuInset, type ToolbarControlGroupOverflow, type ToolbarControlGroupProps, type ToolbarControlGroupSelectedChrome, type ToolbarControlGroupSelectedTone, type ToolbarControlGroupShape, type ToolbarControlGroupSize, type ToolbarControlGroupTone, type ToolbarControlGroupVisibility };
85
+ export { ToolbarActionLink, type ToolbarActionLinkProps, ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupContent, type ToolbarControlGroupDensity, type ToolbarControlGroupFocusRing, type ToolbarControlGroupMenuInset, type ToolbarControlGroupOverflow, type ToolbarControlGroupPlacement, type ToolbarControlGroupProps, type ToolbarControlGroupSelectedChrome, type ToolbarControlGroupSelectedTone, type ToolbarControlGroupShape, type ToolbarControlGroupSize, type ToolbarControlGroupSizing, type ToolbarControlGroupTone, type ToolbarControlGroupVisibility };
@@ -1,4 +1,5 @@
1
- export { ToolbarActionLink, ToolbarControlGroup } from './chunk-NXWE5S57.js';
1
+ export { ToolbarActionLink, ToolbarControlGroup } from './chunk-QJ5VFCW3.js';
2
+
2
3
 
3
4
 
4
5
  //# sourceMappingURL=toolbar-control-group.js.map
@@ -1,6 +1,6 @@
1
1
  import { wireWorkbench } from './chunk-TJK44RQC.js';
2
2
 
3
- import { wireScrollDividers } from './chunk-JCV4GXWB.js';
3
+ import { wireScrollDividers } from './chunk-6DFDTWGG.js';
4
4
 
5
5
 
6
6
  import { delegate } from 'kerfjs';
@@ -33,6 +33,7 @@ interface WireScrollDividersOptions {
33
33
  */
34
34
  targets?: readonly ScrollDividerTarget[];
35
35
  }
36
+ type Root = HTMLElement | Document;
36
37
  /**
37
38
  * Wire scroll dividers below `root`: a divider between pinned chrome and the
38
39
  * content that scrolls beside it shows only while content is scrolled away
@@ -60,10 +61,12 @@ interface WireScrollDividersOptions {
60
61
  *
61
62
  * Structure is re-read after every DOM change below root (a re-render that
62
63
  * drops the attributes gets them back before paint), scroll is tracked with
63
- * one capturing listener, and size changes of each scroller and its children
64
- * with a `ResizeObserver`. Returns a disposer that removes every attribute it
65
- * wrote. See `docs/23-app-layouts.md` §3.7.
64
+ * one capturing listener per registered root, and size changes of each
65
+ * scroller and its children with a `ResizeObserver`. Overlapping registrations
66
+ * share one attribute writer; each disposer removes its registration, and the
67
+ * final disposer removes the wiring-owned attributes. See
68
+ * `docs/23-app-layouts.md` §3.7.
66
69
  */
67
- declare function wireScrollDividers(root: HTMLElement | Document, { targets }?: WireScrollDividersOptions): () => void;
70
+ declare function wireScrollDividers(root: Root, { targets }?: WireScrollDividersOptions): () => void;
68
71
 
69
72
  export { type ScrollDividerTarget, type WireScrollDividersOptions, wireScrollDividers };
@@ -1,3 +1,3 @@
1
- export { wireScrollDividers } from './chunk-JCV4GXWB.js';
1
+ export { wireScrollDividers } from './chunk-6DFDTWGG.js';
2
2
 
3
3
  //# sourceMappingURL=wire-scroll-dividers.js.map
@@ -1,4 +1,4 @@
1
- export { wireTokenSearchFields } from './chunk-P4UXSYIJ.js';
1
+ export { wireTokenSearchFields } from './chunk-2ZOLX3IE.js';
2
2
 
3
3
 
4
4
 
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-TGM424PI.js';
1
+ export { Workbench } from './chunk-H7IODXGY.js';
2
2
 
3
3
 
4
4
 
@@ -101,6 +101,13 @@ Navigation selects can use intrinsic `presentation="navigation"` plus
101
101
  `labelMaxWidth` for component-owned ellipsis. These props own the control's
102
102
  appearance; its parent continues to own outer placement.
103
103
 
104
+ Use `presentation="inline"` with a single-value `renderSelected` when the
105
+ selected content, such as a `Badge`, already provides the trigger chrome. The
106
+ closed combobox has no minimum height, inline padding, or border width, so the
107
+ rendered content defines its box. The Select still owns the focus ring,
108
+ accessible combobox name, and listbox interaction; keep `label` or `ariaLabel`
109
+ even when the selected content shows its own short text.
110
+
104
111
  Use `hint` for persistent supporting text below the control; use
105
112
  `placeholderText` only for the empty value shown inside the closed control.
106
113
  Kerf passes hint text through Web Awesome's form-control contract, which renders
@@ -346,6 +353,9 @@ workspace action in `end`; TabBar keeps the adjacent action immediately after
346
353
  the shrinkable/scrolling tabs and pins `end` at the far edge. Leading,
347
354
  trailing, and end actions remain visible and do not shrink. The component owns
348
355
  that geometry; applications must not override its anatomy to create the split.
356
+ `trailing` and `end` each accept one `ToolbarControlGroup` for grouped controls
357
+ or one standalone `wa-button` when a primary action needs its own Web Awesome
358
+ chrome. Do not add a wrapper or flex margins to position the action.
349
359
  `AppTab` provides compact 32px, segmented, truncating-label, and icon-only
350
360
  presentations. Icon-only tabs keep the required `name` as the tab button's
351
361
  accessible name while visually hiding the duplicate label. The default name
@@ -444,6 +454,10 @@ there as the editor wraps. In `collapsible` mode, the closed state is one named
444
454
  iconic search button and the open state is the same named searchbox, whether
445
455
  the field stands alone or is composed inside `ToolbarControlGroup`.
446
456
 
457
+ On coarse touch devices, the editor's computed text size has a 16px floor. This
458
+ avoids iOS focus magnification of the small-text editor while preserving the
459
+ user's ability to pinch zoom; other field chrome and desktop density are unchanged.
460
+
447
461
  For an app-driven query replacement with an unmanaged field, change its
448
462
  `revision` along with `query` so the editor text rebuilds. For a model-managed
449
463
  field, call `model.replace({ query, tokens })`: it parses the supplied state and
package/docs/apple-hig.md CHANGED
@@ -7,6 +7,7 @@ Native Apple applications should use native platform controls and follow the Hum
7
7
  - Accessibility is behavior: every workflow is keyboard-operable; focus is visible; DOM and reading order agree; controls have accessible names; decorative icons are hidden.
8
8
  - Use adaptive semantic colors. Every custom role needs usable light, dark, and increased-contrast behavior. Never rely on color alone.
9
9
  - Use the system UI font stack and allow browser zoom through 200%. Avoid thin weights and hard minimums that clip enlarged text.
10
+ - On iOS, prevent unwanted focus magnification by keeping editable text legible at touch widths. Do not force the page scale back on blur: that would also undo a person's deliberate pinch zoom.
10
11
  - Group with space and alignment before adding borders. Keep essential content in the leading reading path and secondary detail in progressive disclosure.
11
12
  - Use one icon family and one metaphor per action. Directional icons may mirror in RTL; checkmarks, logos, and real-world objects do not.
12
13
  - Motion is brief, causal, and optional. Honor reduced-motion and reduced-transparency preferences; repeating indicators stop moving under reduced motion.
package/docs/catalog.md CHANGED
@@ -441,6 +441,9 @@ revealCatalogEntry(app, initial, { block: "center" });
441
441
  the preview (`scroll`), or stay pinned while the entry pane is tall enough and
442
442
  scroll with the preview when it is short (`auto`, the default) — so a short
443
443
  window or large text never squeezes the preview out.
444
+ The resource footer uses `ToolbarControlGroup overflow="wrap"`: whole links
445
+ move to another row when they do not fit, keeping Guidance and source labels
446
+ readable without horizontal scrolling.
444
447
  - **`sidebar`** configures the sidebar rail — it forwards to the Workbench's left
445
448
  rail: `size` (default 288px), `resizable` (`true` or `{ min, max }`),
446
449
  `separator`, `collapseMotion`, `presentation`, `responsiveOverlayAt` (default