@kerfjs/ui 5.0.0-beta.107 → 5.0.0-beta.108

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 (118) hide show
  1. package/ai/component-catalog.json +24 -12
  2. package/ai/component-composition.json +8 -2
  3. package/ai/components/button.md +5 -5
  4. package/ai/components/chip.md +1 -0
  5. package/ai/components/foundation.md +1 -1
  6. package/ai/components/list.md +1 -1
  7. package/ai/components/segmented-control.md +1 -0
  8. package/ai/components/surface-scaffold.md +1 -0
  9. package/ai/components/text-field.md +3 -1
  10. package/ai/components/toolbar-control-group.md +1 -0
  11. package/ai/components/toolbar.md +1 -1
  12. package/ai/public-api-signatures-v1.md +64 -13
  13. package/ai/skill.md +12 -6
  14. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  15. package/dist/button.d.ts +3 -3
  16. package/dist/button.js +1 -1
  17. package/dist/catalog.js +4 -4
  18. package/dist/chip.d.ts +20 -2
  19. package/dist/chip.js +2 -1
  20. package/dist/{chunk-23PPPNH6.js → chunk-4OV272LF.js} +2 -2
  21. package/dist/{chunk-23PPPNH6.js.map → chunk-4OV272LF.js.map} +1 -1
  22. package/dist/{chunk-2PXZBFEC.js → chunk-5J6Z4BWY.js} +1 -1
  23. package/dist/{chunk-2PXZBFEC.js.map → chunk-5J6Z4BWY.js.map} +1 -1
  24. package/dist/{chunk-G4DRH4A5.js → chunk-6CDILXF4.js} +3 -1
  25. package/dist/chunk-6CDILXF4.js.map +1 -0
  26. package/dist/{chunk-NLMEDZRM.js → chunk-CPSGUGAY.js} +5 -5
  27. package/dist/{chunk-NLMEDZRM.js.map → chunk-CPSGUGAY.js.map} +1 -1
  28. package/dist/{chunk-TXRKCGT3.js → chunk-KPINJHIC.js} +1 -1
  29. package/dist/chunk-KPINJHIC.js.map +1 -0
  30. package/dist/{chunk-N7MBGONH.js → chunk-LMHN2TJL.js} +22 -2
  31. package/dist/chunk-LMHN2TJL.js.map +1 -0
  32. package/dist/{chunk-NS2TBYM4.js → chunk-LZUUUHNH.js} +7 -3
  33. package/dist/chunk-LZUUUHNH.js.map +1 -0
  34. package/dist/{chunk-OY2MXIBB.js → chunk-NB6POCFH.js} +3 -1
  35. package/dist/chunk-NB6POCFH.js.map +1 -0
  36. package/dist/{chunk-AZ3WT77F.js → chunk-P7EJ4CAD.js} +2 -2
  37. package/dist/{chunk-AZ3WT77F.js.map → chunk-P7EJ4CAD.js.map} +1 -1
  38. package/dist/{chunk-Z4DS77U2.js → chunk-SOFPUW3O.js} +4 -4
  39. package/dist/{chunk-Z4DS77U2.js.map → chunk-SOFPUW3O.js.map} +1 -1
  40. package/dist/{chunk-QLA25V2K.js → chunk-WPNIKZMJ.js} +15 -1
  41. package/dist/chunk-WPNIKZMJ.js.map +1 -0
  42. package/dist/{chunk-DNWWF6UN.js → chunk-WSEJ2FDE.js} +4 -4
  43. package/dist/{chunk-DNWWF6UN.js.map → chunk-WSEJ2FDE.js.map} +1 -1
  44. package/dist/{chunk-WU57BYYM.js → chunk-YVZCKXR3.js} +8 -2
  45. package/dist/chunk-YVZCKXR3.js.map +1 -0
  46. package/dist/{chunk-CMLPFIC5.js → chunk-ZN7KUMPY.js} +3 -1
  47. package/dist/chunk-ZN7KUMPY.js.map +1 -0
  48. package/dist/collapsible-panel.js +2 -2
  49. package/dist/index.js +9 -9
  50. package/dist/list-detail-dialog.js +4 -4
  51. package/dist/list.d.ts +7 -1
  52. package/dist/list.js +1 -1
  53. package/dist/message-composer.js +1 -1
  54. package/dist/nav-stack.js +1 -1
  55. package/dist/segmented-control.d.ts +3 -1
  56. package/dist/segmented-control.js +1 -1
  57. package/dist/split-view.js +1 -1
  58. package/dist/styles/button.css +28 -2
  59. package/dist/styles/components/actions/button/button.css +28 -2
  60. package/dist/styles/components/actions/toolbar/toolbar.css +9 -0
  61. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +29 -1
  62. package/dist/styles/components/collections/framed-list/framed-list.css +2 -0
  63. package/dist/styles/components/layout/pane/pane.css +15 -0
  64. package/dist/styles/components/navigation/nav-stack/nav-stack.css +13 -0
  65. package/dist/styles/components/navigation/tab-bar/tab-bar.css +6 -0
  66. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +16 -0
  67. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +2 -0
  68. package/dist/styles/foundation.css +63 -19
  69. package/dist/styles/framed-list.css +2 -0
  70. package/dist/styles/nav-stack.css +13 -0
  71. package/dist/styles/pane.css +15 -0
  72. package/dist/styles/surface-scaffold.css +2 -0
  73. package/dist/styles/tab-bar.css +6 -0
  74. package/dist/styles/tab-navigator.css +16 -0
  75. package/dist/styles/toolbar-control-group.css +29 -1
  76. package/dist/styles/toolbar.css +9 -0
  77. package/dist/styles/webawesome.css +11 -3
  78. package/dist/surface-scaffold.d.ts +9 -1
  79. package/dist/surface-scaffold.js +1 -1
  80. package/dist/text-field.d.ts +7 -1
  81. package/dist/text-field.js +1 -1
  82. package/dist/toolbar-control-group.d.ts +8 -2
  83. package/dist/toolbar-control-group.js +1 -1
  84. package/dist/toolbar.d.ts +6 -1
  85. package/dist/toolbar.js +1 -1
  86. package/dist/wire-catalog.js +1 -1
  87. package/dist/wire-workbench.js +1 -1
  88. package/dist/workbench-dialog.js +3 -3
  89. package/dist/workbench.js +1 -1
  90. package/docs/accessibility.md +25 -9
  91. package/docs/component-contract.md +19 -6
  92. package/docs/component-selection.md +38 -12
  93. package/docs/design/templates/button/tones-dark.svg +1 -1
  94. package/docs/design/templates/button/tones.svg +1 -1
  95. package/docs/design/templates/button-dark.svg +1 -1
  96. package/docs/design/templates/button.svg +1 -1
  97. package/docs/design/templates/split-button/states-dark.svg +1 -1
  98. package/docs/design/templates/split-button/states.svg +1 -1
  99. package/docs/design/templates/split-button-dark.svg +1 -1
  100. package/docs/design/templates/split-button.svg +1 -1
  101. package/docs/design/templates/text-field/number-dark.svg +1 -0
  102. package/docs/design/templates/text-field/number.svg +1 -0
  103. package/docs/design/templates/text-field-dark.svg +1 -1
  104. package/docs/design/templates/text-field.svg +1 -1
  105. package/docs/layout.md +16 -5
  106. package/docs/webawesome-migration.md +2 -0
  107. package/docs/webawesome-theme.md +8 -2
  108. package/docs/workbench.md +7 -1
  109. package/llms.txt +17 -5
  110. package/package.json +1 -1
  111. package/dist/chunk-CMLPFIC5.js.map +0 -1
  112. package/dist/chunk-G4DRH4A5.js.map +0 -1
  113. package/dist/chunk-N7MBGONH.js.map +0 -1
  114. package/dist/chunk-NS2TBYM4.js.map +0 -1
  115. package/dist/chunk-OY2MXIBB.js.map +0 -1
  116. package/dist/chunk-QLA25V2K.js.map +0 -1
  117. package/dist/chunk-TXRKCGT3.js.map +0 -1
  118. package/dist/chunk-WU57BYYM.js.map +0 -1
@@ -76,11 +76,16 @@
76
76
  surface instead. */
77
77
  .kui-pane[data-background] {
78
78
  background: var(--_kui-pane-background);
79
+ --kui-color-surface-current-region: var(--_kui-pane-background);
79
80
  }
80
81
 
82
+ /* The transparent slots keep the root's surface context. */
81
83
  .kui-pane[data-background][data-appearance="sunken"] > .kui-pane__content,
82
84
  .kui-pane[data-background][data-appearance="sunken"][data-chrome-placement="auto"]
83
85
  > :is(.kui-pane__header, .kui-pane__footer) {
86
+ --kui-color-surface-current: inherit;
87
+ --kui-color-surface-current-region: inherit;
88
+ --kui-color-surface-current-tint: inherit;
84
89
  background: transparent;
85
90
  }
86
91
 
@@ -266,6 +271,16 @@
266
271
  background: var(--kui-chrome-material);
267
272
  }
268
273
 
274
+ /* The material has no single color over scrolling content; the surface
275
+ context takes it at rest, as a tint over the region it covers. A clear
276
+ header paints nothing and keeps the inherited context. */
277
+ .kui-pane[data-header-presentation="overlay"]:not(
278
+ [data-header-material="clear"]
279
+ )
280
+ > .kui-pane__header {
281
+ --kui-color-surface-current-tint: var(--kui-chrome-material);
282
+ }
283
+
269
284
  .kui-pane[data-header-presentation="overlay"][data-header-overlay="measured"]:not(
270
285
  [data-chrome-placement="auto"]
271
286
  )
@@ -337,6 +337,8 @@ h2.kui-dialog-surface__title {
337
337
  -webkit-backdrop-filter: var(--kui-chrome-material-filter);
338
338
  backdrop-filter: var(--kui-chrome-material-filter);
339
339
  background: var(--kui-chrome-material-raised);
340
+ /* The material's surface context at rest, over the raised panel. */
341
+ --kui-color-surface-current-tint: var(--kui-chrome-material-raised);
340
342
  /* The divider is scroll state: the register publishes it only while
341
343
  content is scrolled beneath the header. An inset shadow, so showing it
342
344
  never moves the header or the content. */
@@ -38,6 +38,12 @@
38
38
  --kui-tab-bar-strip-background,
39
39
  var(--kui-tab-bar-background)
40
40
  );
41
+ /* The surface context (foundation.css): the strip's fill is a tint over
42
+ whatever the bar sits on. */
43
+ --kui-color-surface-current-tint: var(
44
+ --kui-tab-bar-strip-background,
45
+ var(--kui-tab-bar-background)
46
+ );
41
47
  scrollbar-width: none;
42
48
  align-items: center;
43
49
  flex: var(--kui-tab-bar-strip-flex, 1 1 auto);
@@ -70,6 +70,13 @@
70
70
 
71
71
  .kui-tab-scaffold__scene[data-background] {
72
72
  background: var(--_kui-tab-scaffold-scene-background);
73
+ --kui-color-surface-current-region: var(--_kui-tab-scaffold-scene-background);
74
+ }
75
+
76
+ /* The navigator's color paints its root and a docked bar over whatever holds
77
+ the navigator: a region in the surface context. */
78
+ .kui-tab-scaffold[data-background] {
79
+ --kui-color-surface-current-region: var(--_kui-tab-scaffold-background);
73
80
  }
74
81
 
75
82
  @media (forced-colors: active) {
@@ -213,6 +220,15 @@
213
220
  background: var(--kui-chrome-material);
214
221
  }
215
222
 
223
+ /* The material's surface context at rest (see pane.css); a clear bar keeps
224
+ the inherited context. */
225
+ .kui-tab-scaffold[data-bar-presentation="overlay"]:not(
226
+ [data-bar-material="clear"]
227
+ )
228
+ > .kui-tab-scaffold__bar {
229
+ --kui-color-surface-current-tint: var(--kui-chrome-material);
230
+ }
231
+
216
232
  .kui-tab-scaffold[data-bar-presentation="overlay"][data-bar-overlay="measured"] {
217
233
  position: relative;
218
234
  }
@@ -132,6 +132,31 @@
132
132
  }
133
133
  }
134
134
 
135
+ /* A sibling of an expanded trailing control yields to the trailing-priority
136
+ row the Toolbar moves that control onto: the same container queries and
137
+ responsiveAt keys as the Toolbar's own row (toolbar.css), so the group
138
+ hides exactly while the row is active and never wraps onto a third row at
139
+ a threshold restated with hideBelow. */
140
+ @container kui-toolbar (max-width: 52rem) {
141
+ .kui-toolbar[data-responsive="trailing-priority"][data-responsive-at="narrow"]
142
+ > .kui-toolbar__trailing:has(> [data-expanded="true"])
143
+ > .kui-toolbar-control-group[data-visibility="yield-to-priority-row"]:not(
144
+ [data-expanded="true"]
145
+ ) {
146
+ display: none;
147
+ }
148
+ }
149
+
150
+ @container kui-toolbar (max-width: 44rem) {
151
+ .kui-toolbar[data-responsive="trailing-priority"][data-responsive-at="compact"]
152
+ > .kui-toolbar__trailing:has(> [data-expanded="true"])
153
+ > .kui-toolbar-control-group[data-visibility="yield-to-priority-row"]:not(
154
+ [data-expanded="true"]
155
+ ) {
156
+ display: none;
157
+ }
158
+ }
159
+
135
160
  /* `shape="rounded"` swaps the default pill radius for the design system's softer
136
161
  rounded rectangle. The shared highlight contract derives every inner hover or
137
162
  selected surface from this outer radius and the group's full inset. */
@@ -206,7 +231,10 @@
206
231
  border-color 0.2s ease;
207
232
  }
208
233
 
209
- .kui-toolbar-control-group[data-content="search"]:has(
234
+ /* A fill search spans a content-sized group's line. A `sizing="grow"` group
235
+ keeps its own grow basis instead (below): taking the whole line would make
236
+ it the line's hypothetical size and wrap every sibling group. */
237
+ .kui-toolbar-control-group[data-content="search"]:not([data-sizing="grow"]):has(
210
238
  > .kui-search-field[data-expanded="true"][data-fill="true"]
211
239
  ) {
212
240
  width: 100%;
@@ -16,6 +16,15 @@
16
16
  font-family: var(--kui-font-sans);
17
17
  }
18
18
 
19
+ /* In a List, a vertical stack, the toolbar is chrome and keeps its whole
20
+ natural height. Its explicit one-band min-height (below) replaces the flex
21
+ item's automatic content minimum, so a column shrinking toward a long
22
+ flex scroller beside it would otherwise squeeze a wrapped or stacked
23
+ toolbar onto its first row and clip the rest. */
24
+ .kui-list > .kui-toolbar {
25
+ flex-shrink: 0;
26
+ }
27
+
19
28
  /* Safe areas: a toolbar compensates only for the inline edges its owner hands
20
29
  it through `--kui-edge-inset-inline-*` (a Pane header or footer does), so its
21
30
  box and dividers keep reaching the edge while its zones clear the unsafe
@@ -54,14 +54,19 @@
54
54
  theme stylesheets build their shadows from this token, so an app that
55
55
  layers one of them keeps kerf's scheme-aware shadow color. */
56
56
  --wa-color-shadow: light-dark(rgb(0 0 0 / 0.14), rgb(0 0 0 / 0.5));
57
- --wa-color-focus: light-dark(#007aff, #64d2ff);
57
+ /* The brand tokens follow kerf's opt-in --kui-color-brand when the app
58
+ sets it (foundation.css derives the ramp); unset, the literals stand. */
59
+ --wa-color-focus: var(--_kui-brand-focus, light-dark(#007aff, #64d2ff));
58
60
  /* Kerf-owned loud fills and on-loud text: every on-loud/fill-loud pair
59
61
  clears WCAG AA (4.5:1) in both schemes. Light brand and danger keep
60
62
  white text on a slightly deeper fill of the same hue (#0088ff ->
61
63
  #0075db, #ff383c -> #eb0005); success keeps its bright green with dark
62
64
  text, as warning already does; dark brand and danger keep their fills
63
65
  with dark text. src/foundation.css mirrors these as fallbacks. */
64
- --wa-color-brand-fill-loud: light-dark(#0075db, #64d2ff);
66
+ --wa-color-brand-fill-loud: var(
67
+ --_kui-brand-fill-loud,
68
+ light-dark(#0075db, #64d2ff)
69
+ );
65
70
  --wa-color-brand-fill-quiet: color-mix(
66
71
  in srgb,
67
72
  var(--wa-color-brand-fill-loud) 8%,
@@ -86,7 +91,10 @@
86
91
  /* Kerf-owned light on-quiet values: darkened from Hot Sheet 2's hues so
87
92
  they clear AA on the lowered page background (#f2f2f7) as well as on
88
93
  the surface; src/foundation.css mirrors them as fallbacks. */
89
- --wa-color-brand-on-quiet: light-dark(#0c62f3, #8acbff);
94
+ --wa-color-brand-on-quiet: var(
95
+ --_kui-brand-on-quiet,
96
+ light-dark(#0c62f3, #8acbff)
97
+ );
90
98
  --wa-color-brand-on-normal: var(--wa-color-brand-on-quiet);
91
99
  --wa-color-brand-on-loud: light-dark(#fff, #111113);
92
100
 
@@ -88,6 +88,14 @@ interface DialogSurfaceOptions {
88
88
  * copy in the body; rendered as `aria-describedby` on the `<dialog>`.
89
89
  */
90
90
  describedBy?: string;
91
+ /**
92
+ * An alert dialog: a confirmation that interrupts the person to report
93
+ * something urgent or ask before a destructive action ("Delete this
94
+ * topic?"). Renders `role="alertdialog"` on the `<dialog>`; set
95
+ * `describedBy` to the element holding the message, and leave
96
+ * `lightDismiss` off so the person answers it.
97
+ */
98
+ alert?: boolean;
91
99
  /** The body content. */
92
100
  children: KerfUiContent;
93
101
  /**
@@ -202,7 +210,7 @@ interface DialogSurfaceOptions {
202
210
  * floating header and footer measurement, the bottom sheet's keyboard
203
211
  * clearance and grabber, and re-render resilience while open.
204
212
  */
205
- declare function DialogSurface({ id, label, labelledBy, describedBy, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
213
+ declare function DialogSurface({ id, label, labelledBy, describedBy, alert, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
206
214
  /** Where a PopoverDialogSurface appears: the top layer, or in flow. */
207
215
  type PopoverDialogSurfacePresentation = 'overlay' | 'embedded';
208
216
  /** Application `data-*` metadata on the surface root. */
@@ -1,4 +1,4 @@
1
- export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-G4DRH4A5.js';
1
+ export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-6CDILXF4.js';
2
2
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
3
3
 
4
4
 
@@ -50,6 +50,12 @@ interface TextFieldOptions {
50
50
  showCount?: boolean;
51
51
  /** Single-line value kind (default `text`). */
52
52
  type?: TextFieldType;
53
+ /** Native lower bound for `type="number"` (number or numeric string). */
54
+ min?: number | string;
55
+ /** Native upper bound for `type="number"` (number or numeric string). */
56
+ max?: number | string;
57
+ /** Native number increment (number, numeric string, or `"any"`). */
58
+ step?: number | string;
53
59
  /** A multi-line field: a `<textarea>`. */
54
60
  multiline?: boolean;
55
61
  /** A multi-line field's height in lines (default 3). */
@@ -78,6 +84,6 @@ interface TextFieldOptions {
78
84
  * The whole field is one `<label>`, so clicking anywhere on it focuses the
79
85
  * input; the input is named by `label` (or `ariaLabel`) directly.
80
86
  */
81
- declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
87
+ declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
82
88
 
83
89
  export { TextField, type TextFieldName, type TextFieldOptions, type TextFieldProps, type TextFieldRootAttributes, type TextFieldSize, type TextFieldType };
@@ -1,4 +1,4 @@
1
- export { TextField } from './chunk-WU57BYYM.js';
1
+ export { TextField } from './chunk-YVZCKXR3.js';
2
2
 
3
3
 
4
4
 
@@ -19,7 +19,7 @@ type ToolbarControlGroupSelectedTone = 'brand' | 'neutral' | 'pop';
19
19
  type ToolbarControlGroupOverflow = 'visible' | 'scroll' | 'wrap';
20
20
  type ToolbarControlGroupExpandedOverflow = 'clip' | 'visible';
21
21
  type ToolbarControlGroupMenuInset = 'standard' | 'compact' | 'list-zero';
22
- type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling';
22
+ type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling' | 'yield-to-priority-row';
23
23
  type ToolbarControlGroupSizing = 'content' | 'grow' | 'fill';
24
24
  type ToolbarControlGroupPlacement = 'start' | 'end';
25
25
  interface ToolbarActionLinkProps {
@@ -82,7 +82,13 @@ interface ToolbarControlGroupProps {
82
82
  overflow?: ToolbarControlGroupOverflow;
83
83
  /** Let app-owned anchored surfaces extend outside an expanded search group. Collapsed searches retain their clipping. */
84
84
  expandedOverflow?: ToolbarControlGroupExpandedOverflow;
85
- /** Responsive visibility owned by the enclosing Toolbar container. */
85
+ /**
86
+ * Responsive visibility owned by the enclosing Toolbar container.
87
+ * `yield-to-priority-row` hides this trailing group exactly while its
88
+ * Toolbar's `responsive="trailing-priority"` row is active (an expanded
89
+ * trailing sibling at the Toolbar's `responsiveAt` width), so the app never
90
+ * restates that breakpoint with `hideBelow`.
91
+ */
86
92
  visibility?: ToolbarControlGroupVisibility;
87
93
  /** Hide below this Toolbar content width; requires wireToolbarVisibility. */
88
94
  hideBelow?: CssLength;
@@ -1,4 +1,4 @@
1
- export { ToolbarActionLink, ToolbarControlGroup } from './chunk-2PXZBFEC.js';
1
+ export { ToolbarActionLink, ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
2
2
 
3
3
 
4
4
 
package/dist/toolbar.d.ts CHANGED
@@ -9,6 +9,11 @@ type ToolbarPosition = 'header' | 'footer';
9
9
  /** Control-band density: `default` (60px with 44px groups) or `compact`. */
10
10
  type ToolbarDensity = 'default' | 'compact';
11
11
  interface ToolbarProps {
12
+ /**
13
+ * The toolbar's id, so app code can address it: name it as a scrolling
14
+ * List's chrome in `wireScrollDividers({ targets })`, or find it in tests.
15
+ */
16
+ id?: string;
12
17
  leading?: KerfUiContent;
13
18
  center?: KerfUiContent;
14
19
  trailing?: KerfUiContent;
@@ -73,6 +78,6 @@ interface ToolbarProps {
73
78
  * toolbar) forwards, so the app configures that toolbar instead of styling it.
74
79
  */
75
80
  type ToolbarConfig = Pick<ToolbarProps, 'density' | 'dividerSides' | 'centerAlign' | 'responsive' | 'responsiveAt' | 'safeAreaEdges'>;
76
- declare function Toolbar({ leading, center, trailing, label, position, density, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
81
+ declare function Toolbar({ id, leading, center, trailing, label, position, density, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
77
82
 
78
83
  export { Sides, Toolbar, type ToolbarConfig, type ToolbarDensity, type ToolbarPosition, type ToolbarProps };
package/dist/toolbar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Toolbar } from './chunk-CMLPFIC5.js';
1
+ export { Toolbar } from './chunk-ZN7KUMPY.js';
2
2
 
3
3
  //# sourceMappingURL=toolbar.js.map
@@ -1,4 +1,4 @@
1
- import { wireWorkbench } from './chunk-QLA25V2K.js';
1
+ import { wireWorkbench } from './chunk-WPNIKZMJ.js';
2
2
 
3
3
  import { wireScrollDividers } from './chunk-OZ6X76G5.js';
4
4
  import { loadUxReviewProject } from './chunk-6C6DLP3W.js';
@@ -1,4 +1,4 @@
1
- export { wireWorkbench } from './chunk-QLA25V2K.js';
1
+ export { wireWorkbench } from './chunk-WPNIKZMJ.js';
2
2
 
3
3
 
4
4
 
@@ -1,7 +1,7 @@
1
- import { DialogSurface } from './chunk-G4DRH4A5.js';
1
+ import { DialogSurface } from './chunk-6CDILXF4.js';
2
2
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
3
3
 
4
- import { Workbench } from './chunk-NLMEDZRM.js';
4
+ import { Workbench } from './chunk-CPSGUGAY.js';
5
5
 
6
6
 
7
7
 
@@ -10,7 +10,7 @@ import { ToolbarText } from './chunk-EKHDLBRP.js';
10
10
 
11
11
 
12
12
 
13
- import { ToolbarControlGroup } from './chunk-2PXZBFEC.js';
13
+ import { ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
14
14
 
15
15
 
16
16
 
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-NLMEDZRM.js';
1
+ export { Workbench } from './chunk-CPSGUGAY.js';
2
2
 
3
3
 
4
4
 
@@ -370,7 +370,10 @@ supporting line (or app-owned summary copy in the body) is the dialog's
370
370
  accessible description; `WorkbenchDialog` wires its title `description`
371
371
  automatically (`<id>-description`), and `ListDetailDialog` takes an explicit
372
372
  `describedBy`, since its detail description describes the selection, not the
373
- dialog. For a **page or view**
373
+ dialog. A `DialogSurface` that asks before a destructive action ("Delete this
374
+ topic?") sets `alert`, which renders `role="alertdialog"` on the native
375
+ `<dialog>`; pair it with `describedBy` on the message so it is announced, and
376
+ leave `lightDismiss` off so the person answers it. For a **page or view**
374
377
  title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
375
378
  `role="heading"` with a matching `aria-level`, giving the view a heading landmark
376
379
  so screen-reader heading navigation and "main heading" semantics work — the same
@@ -479,7 +482,9 @@ controls; Enter and Space use native button activation. The application handles
479
482
  the supplied `data-action`, reads `data-segment-value`, updates `value`, and
480
483
  re-renders. Keep labels unique and meaningful even when `content` shows only an
481
484
  icon. The filled track uses the normal neutral text role for readable unselected
482
- labels in light and dark themes. Use tabs—not a segmented control—when choices switch page regions that
485
+ labels in light and dark themes. A whole-control `disabled` disables every
486
+ native button and marks the group `aria-disabled` while `aria-pressed` still
487
+ reports the current choice. Use tabs—not a segmented control—when choices switch page regions that
483
488
  need `tab`/`tabpanel` semantics.
484
489
 
485
490
  ## ChoiceChips
@@ -539,6 +544,12 @@ its label; the input itself reports `required`. The focus ring is drawn on the
539
544
  box around the input (`:focus-within`), 3px with a 1px offset like every Kerf
540
545
  control.
541
546
 
547
+ For `type="number"`, `min`, `max`, and `step` pass through to the native input.
548
+ Each accepts a number or string; `step="any"` allows arbitrary decimals.
549
+ They are omitted for other input types and `multiline` textareas. Native
550
+ constraint validation (`rangeUnderflow`, `rangeOverflow`, and `stepMismatch`)
551
+ then applies without custom field wiring.
552
+
542
553
  `clearable` adds a native `<button>` named by `clearLabel` (default "Clear")
543
554
  after the input. It is hidden while the field is empty, is a normal Tab stop,
544
555
  and with `wireTextFields(root)` empties the field, dispatches bubbling `input`
@@ -567,12 +578,16 @@ a surface that changes the background behind its children publishes the
567
578
  surface context (`--kui-color-surface-current`, `-region`, and `-tint`; see
568
579
  `docs/component-contract.md`), and the line layers it the same way the page
569
580
  does. On a sunken panel, a quiet `ContentItem`, a lowered `FramedList`, a
570
- raised `List`, popup, or dialog, or a sunken Pane, Workbench area, or
571
- TabNavigator scene, the line matches the pixels outside the box, so it reads
572
- as a cut through the fill instead of a light halo. On a doubly sunken surface
573
- the brand ring still clears 3:1 against the line (about 3.2:1 in light, 6.9:1
574
- in dark). A sunken surface's grain is not part of the line, and a translucent
575
- glass material, which has no single color, falls back to the base surface.
581
+ raised `List`, popup, or dialog, a highlighted `FramedList` row, a sunken
582
+ Pane, Workbench area, or TabNavigator scene, or a Pane or TabNavigator app
583
+ `background`, the line matches the pixels outside the box, so it reads as a
584
+ cut through the fill instead of a light halo. On a doubly sunken surface the
585
+ brand ring still clears 3:1 against the line (about 3.2:1 in light, 6.9:1 in
586
+ dark). A sunken surface's grain is not part of the line. Translucent glass
587
+ chrome (an overlay header, bar, or bottom toolbar, a dialog's floating
588
+ header) has no single color over scrolling content, so the line paints the
589
+ material over the surface beneath it: exact at rest, an approximation while
590
+ content scrolls under the bar.
576
591
 
577
592
  ## Checkbox, RadioGroup, and Switch
578
593
 
@@ -786,7 +801,8 @@ the Tab order while collapsed or not inline. `wireWorkbench`, given a panel's
786
801
  `collapsed` signal, returns focus stranded in a closing panel to the control that
787
802
  opened it, else the restore control, else a control whose `aria-controls` names the
788
803
  panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
789
- pattern). See [`workbench.md`](workbench.md).
804
+ pattern), whether its own toggle or a persistent toggle outside the Workbench
805
+ opened it, as soon as the panel can take focus. See [`workbench.md`](workbench.md).
790
806
 
791
807
  ## Chrome material
792
808
 
@@ -424,11 +424,17 @@ background behind its children publishes, on its own painted element, the
424
424
  foundation tokens `--kui-color-surface-current` (the opaque surface beneath:
425
425
  a raised `DialogSurface`, popover dialog, `AnchoredSurface`, or `List`
426
426
  `surface`, and a sunken Pane's opaque auto chrome, set it and clear both
427
- layers), `--kui-color-surface-current-region` (a layout region's translucent
428
- fill: a sunken `Pane`, `Workbench` area, or `TabNavigator` scene), and
429
- `--kui-color-surface-current-tint` (a content surface's translucent fill: a
430
- `SunkenPanel`, a sunken `Disclosure`, a lowered `FramedList`, a quiet,
431
- hovered, pressed, or selected `ContentItem`). They are undeclared at `:root`
427
+ layers), `--kui-color-surface-current-region` (a layout region's fill: a sunken
428
+ `Pane`, `Workbench` area, or `TabNavigator` scene, and a `Pane`'s or
429
+ `TabNavigator`'s app `background`), and `--kui-color-surface-current-tint` (a
430
+ content surface's fill: a `SunkenPanel`, a sunken `Disclosure`, a lowered
431
+ `FramedList` and its highlighted row, a quiet, hovered, pressed, or selected
432
+ `ContentItem`, a `TabBar` strip, and translucent glass chrome at rest — a
433
+ `Pane` overlay header, `NavStack` overlay chrome or bottom toolbar, a
434
+ `TabNavigator` overlay bar, and a dialog's floating header; clear chrome
435
+ paints nothing and keeps the inherited context). Interactive rows (`ListItem`,
436
+ `ListActionRow`, `SummaryRow`) are buttons or links themselves, so no control
437
+ nests on their hover or selection fill and they publish nothing. They are undeclared at `:root`
432
438
  and a scoped `[data-kui-tint]` resets them. A control that must match what
433
439
  lies behind it (the inset focus ring's inner line on `Button` and
434
440
  `SplitButton`) layers tint over region over the opaque color, falling back to
@@ -635,7 +641,14 @@ preferences remove that entrance animation.
635
641
  Use `visibility="yield-to-expanded-sibling"` on trailing groups that should
636
642
  leave a 480px-or-narrower toolbar while a sibling trailing group is expanded.
637
643
  They return when it collapses or the toolbar widens; leading and center zones
638
- remain visible.
644
+ remain visible. In a `responsive="trailing-priority"` Toolbar, use
645
+ `visibility="yield-to-priority-row"` instead: the group leaves exactly while
646
+ the expanded trailing control has its own row, from the same container query
647
+ as the row at the Toolbar's `responsiveAt` (832px for `narrow`, 704px for
648
+ `compact`, both inclusive). Do not restate that width with `hideBelow`, which
649
+ hides strictly below its threshold: at exactly 704px the row is active while
650
+ `hideBelow={rem(44)}` still shows the group, which then wraps onto a third
651
+ row.
639
652
  A group's controls are plain `<button>`s (a standalone `Button` belongs outside a group); a popup menu is a `PopupMenu`, which renders its own
640
653
  trigger. By default a group stays on one row: each icon sits beside its label,
641
654
  and the enclosing `Toolbar`'s responsive policy relocates whole groups. Set
@@ -108,6 +108,30 @@ useful subtree. Prefer a component prop or component-specific token when the
108
108
  change belongs to one component or instance, and preserve the documented
109
109
  foreground/background contrast relationships and visible focus ring.
110
110
 
111
+ ### Brand color
112
+
113
+ Set the opt-in `--kui-color-brand` once on `:root` to give the application its
114
+ own brand. Any color works: Kerf derives the whole brand ramp from it — the
115
+ loud fill (and the quiet and normal fills and borders mixed from it), its
116
+ on-loud text, brand text and links, brand text on brand fills, and the focus
117
+ ring — clamping lightness in each color scheme so every pair keeps WCAG AA for
118
+ any hue. Unset, every brand token keeps its tuned default exactly. With the
119
+ Web Awesome integration the same derivation drives `--wa-color-brand-*` and
120
+ `--wa-color-focus`.
121
+
122
+ ```css
123
+ :root {
124
+ --kui-color-brand: #1a7f37;
125
+ }
126
+ ```
127
+
128
+ Do not change the brand by overriding `--kui-color-brand-fill-loud` alone:
129
+ brand text (`--kui-color-brand-on-quiet`, `--kui-color-brand-on-fill`) and the
130
+ focus ring are separate tuned values and would stay the default blue. Choose a
131
+ brand hue apart from the status tones it shares surfaces with: `info` follows
132
+ the brand, so a green brand makes an info banner read like `success`. For one
133
+ subtree with a different identity, use a scoped `tint` accent instead.
134
+
111
135
  ### Typed runtime dimensions
112
136
 
113
137
  Use `Row`, `Grid`, or `List` spacing shorthands for repeated sibling spacing: `gap="xs"`,
@@ -643,15 +667,15 @@ the control's dimensions, shows `LoadingSpinner` in its place, sets
643
667
  group hides only the glyph; a `content="text"` or `"mixed"` group hides the
644
668
  whole control, label included, so the spinner never draws over text.
645
669
 
646
- | Want | Put in the zone | Notes |
647
- | ------------------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
648
- | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
649
- | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
650
- | One primary application action | `trailing={<Button tone="brand">New Ticket…</Button>}` | The standalone button keeps its own chrome; use `tone="danger"` when the action is destructive |
651
- | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
652
- | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
653
- | A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
654
- | A collapsible search box | `<ToolbarControlGroup single><SearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireSearchFields` manages expand/collapse/focus by default |
670
+ | Want | Put in the zone | Notes |
671
+ | ------------------------------ | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
672
+ | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
673
+ | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
674
+ | One primary application action | `trailing={<Button tone="brand">New Ticket…</Button>}` | The standalone button keeps its own chrome; use `tone="success"` for a positive pricing check or `tone="danger"` when the action is destructive |
675
+ | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
676
+ | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
677
+ | A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
678
+ | A collapsible search box | `<ToolbarControlGroup single><SearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireSearchFields` manages expand/collapse/focus by default |
655
679
 
656
680
  ### Room for an expanded search
657
681
 
@@ -665,9 +689,11 @@ other controls; plan for it instead:
665
689
  beside the siblings.
666
690
  - If a useful search cannot fit beside the other controls, set those controls
667
691
  aside while it is open. Mark them `visibility="yield-to-expanded-sibling"`
668
- (they leave a 480px-or-narrower toolbar while a sibling is expanded), or
669
- gather them behind a "…" `PopupMenu` overflow group. The application decides
670
- which controls yield.
692
+ (they leave a 480px-or-narrower toolbar while a sibling is expanded), or, in
693
+ a `responsive="trailing-priority"` Toolbar,
694
+ `visibility="yield-to-priority-row"` (they leave exactly while the expanded
695
+ control has its own row), or gather them behind a "…" `PopupMenu` overflow
696
+ group. The application decides which controls yield.
671
697
 
672
698
  ### Application surfaces below search
673
699
 
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="ct0"><rect x="29" y="33" width="105.3" height="18"/></clipPath><clipPath id="ct1"><rect x="160.3" y="33" width="51" height="18"/></clipPath><clipPath id="ct2"><rect x="237.3" y="33" width="104.8" height="18"/></clipPath></defs><rect x="20" y="20" width="123.3" height="44" rx="12" fill="rgb(100,210,255)"/><rect x="20.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="29" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(17,17,19)">Save Changes</text><rect x="151.3" y="20" width="69" height="44" rx="12" fill="rgb(229,229,234)"/><rect x="151.5" y="20.5" width="68" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="160.28" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(28,28,30)">Cancel</text><rect x="228.3" y="20" width="122.8" height="44" rx="12" fill="rgb(255,56,60)"/><rect x="228.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="237.27" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(17,17,19)">Delete Project</text></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="ct0"><rect x="29" y="33" width="105.3" height="18"/></clipPath><clipPath id="ct1"><rect x="160.3" y="33" width="51" height="18"/></clipPath><clipPath id="ct2"><rect x="237.3" y="33" width="88.9" height="18"/></clipPath><clipPath id="ct3"><rect x="352.2" y="33" width="104.8" height="18"/></clipPath></defs><rect x="20" y="20" width="123.3" height="44" rx="12" fill="rgb(100,210,255)"/><rect x="20.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="29" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(17,17,19)">Save Changes</text><rect x="151.3" y="20" width="69" height="44" rx="12" fill="rgb(229,229,234)"/><rect x="151.5" y="20.5" width="68" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="160.28" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(28,28,30)">Cancel</text><rect x="228.3" y="20" width="106.9" height="44" rx="12" fill="rgb(52,199,89)"/><rect x="228.5" y="20.5" width="106" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="237.27" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(29,29,31)">Price Check</text><rect x="343.2" y="20" width="122.8" height="44" rx="12" fill="rgb(255,56,60)"/><rect x="343.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="352.16" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(17,17,19)">Delete Project</text></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="ct0"><rect x="29" y="33" width="105.3" height="18"/></clipPath><clipPath id="ct1"><rect x="160.3" y="33" width="51" height="18"/></clipPath><clipPath id="ct2"><rect x="237.3" y="33" width="104.8" height="18"/></clipPath></defs><rect x="20" y="20" width="123.3" height="44" rx="12" fill="rgb(0,117,219)"/><rect x="20.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="29" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Save Changes</text><rect x="151.3" y="20" width="69" height="44" rx="12" fill="rgb(72,72,74)"/><rect x="151.5" y="20.5" width="68" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="160.28" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Cancel</text><rect x="228.3" y="20" width="122.8" height="44" rx="12" fill="rgb(235,0,5)"/><rect x="228.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="237.27" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Delete Project</text></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="ct0"><rect x="29" y="33" width="105.3" height="18"/></clipPath><clipPath id="ct1"><rect x="160.3" y="33" width="51" height="18"/></clipPath><clipPath id="ct2"><rect x="237.3" y="33" width="88.9" height="18"/></clipPath><clipPath id="ct3"><rect x="352.2" y="33" width="104.8" height="18"/></clipPath></defs><rect x="20" y="20" width="123.3" height="44" rx="12" fill="rgb(0,117,219)"/><rect x="20.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="29" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Save Changes</text><rect x="151.3" y="20" width="69" height="44" rx="12" fill="rgb(72,72,74)"/><rect x="151.5" y="20.5" width="68" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="160.28" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Cancel</text><rect x="228.3" y="20" width="106.9" height="44" rx="12" fill="rgb(52,199,89)"/><rect x="228.5" y="20.5" width="106" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="237.27" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(29,29,31)">Price Check</text><rect x="343.2" y="20" width="122.8" height="44" rx="12" fill="rgb(235,0,5)"/><rect x="343.5" y="20.5" width="122" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="352.16" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Delete Project</text></svg>