@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
package/dist/index.js CHANGED
@@ -17,20 +17,20 @@ export { wireScrollDividers } from './chunk-OZ6X76G5.js';
17
17
  export { wireCheckboxes } from './chunk-CNM6ZWP2.js';
18
18
  export { TabBarItem } from './chunk-ZCDRLN3B.js';
19
19
  export { TabBar } from './chunk-RFLW4FPL.js';
20
- export { TextField } from './chunk-WU57BYYM.js';
20
+ export { TextField } from './chunk-YVZCKXR3.js';
21
21
  export { Spacer } from './chunk-YMUIQRWK.js';
22
22
  export { SplitButton } from './chunk-TI2GV36S.js';
23
23
  export { StateBanner } from './chunk-2FW7D4IG.js';
24
24
  export { SummaryRow } from './chunk-4UB5Z3ZZ.js';
25
25
  export { SunkenPanel } from './chunk-D5RGM3XA.js';
26
26
  export { createSearchFieldModel } from './chunk-CIAUILRR.js';
27
- export { SegmentedControl } from './chunk-NS2TBYM4.js';
27
+ export { SegmentedControl } from './chunk-LZUUUHNH.js';
28
28
  export { Select } from './chunk-ZX2QQJXD.js';
29
29
 
30
30
  export { applySelection, isApplePlatform, selectionGesture } from './chunk-GXSX6Q6O.js';
31
- export { MessageComposer } from './chunk-23PPPNH6.js';
31
+ export { MessageComposer } from './chunk-4OV272LF.js';
32
32
  export { RadioGroup } from './chunk-OHPYT2BH.js';
33
- export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-G4DRH4A5.js';
33
+ export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-6CDILXF4.js';
34
34
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
35
35
 
36
36
  export { EmptyState } from './chunk-B53PS2LE.js';
@@ -43,13 +43,13 @@ export { ContentItem } from './chunk-5USKCWGL.js';
43
43
  export { ContentSwap } from './chunk-EIET4QLK.js';
44
44
  export { CornerStatus } from './chunk-G3J7JDP6.js';
45
45
  export { EdgeActivityIndicator } from './chunk-QH5KOFNS.js';
46
- export { Chip } from './chunk-N7MBGONH.js';
46
+ export { Chip } from './chunk-LMHN2TJL.js';
47
47
  export { ChoiceChips } from './chunk-ITUQC4FW.js';
48
48
  export { ColumnTrack } from './chunk-QYMJHS4K.js';
49
49
  export { Combobox } from './chunk-BKUMG2KB.js';
50
50
  export { openAnchoredSurface, openAnchoredSurfaceAt } from './chunk-3WYREXW4.js';
51
51
  export { BackdropImage } from './chunk-SCDWURBO.js';
52
- export { Button } from './chunk-TXRKCGT3.js';
52
+ export { Button } from './chunk-KPINJHIC.js';
53
53
  export { Switch } from './chunk-LTBVP6VN.js';
54
54
  export { Row } from './chunk-AMIHGFLP.js';
55
55
  export { SearchField, placeSearchFieldCaret, readSearchField } from './chunk-VEME3D7K.js';
@@ -67,12 +67,12 @@ export { PopupMenu, closePopupMenu, openPopupMenu, openPopupMenuAt } from './chu
67
67
 
68
68
  export { Skeleton } from './chunk-CVD5IZGB.js';
69
69
  export { DisclosureArrow } from './chunk-TKNVL53O.js';
70
- export { ToolbarActionLink, ToolbarControlGroup } from './chunk-2PXZBFEC.js';
71
- export { Toolbar } from './chunk-CMLPFIC5.js';
70
+ export { ToolbarActionLink, ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
71
+ export { Toolbar } from './chunk-ZN7KUMPY.js';
72
72
  export { Pane } from './chunk-HQZG3OFL.js';
73
73
 
74
74
  export { ensureSunkenTexture } from './chunk-IKS72C6P.js';
75
- export { List } from './chunk-OY2MXIBB.js';
75
+ export { List } from './chunk-NB6POCFH.js';
76
76
 
77
77
 
78
78
  export { LoadingSpinner } from './chunk-WVS6CUCT.js';
@@ -1,5 +1,5 @@
1
- import { SplitView } from './chunk-AZ3WT77F.js';
2
- import { DialogSurface } from './chunk-G4DRH4A5.js';
1
+ import { SplitView } from './chunk-P7EJ4CAD.js';
2
+ import { DialogSurface } from './chunk-6CDILXF4.js';
3
3
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
4
4
 
5
5
 
@@ -7,8 +7,8 @@ export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
7
7
  import { ToolbarText } from './chunk-EKHDLBRP.js';
8
8
 
9
9
 
10
- import { ToolbarControlGroup } from './chunk-2PXZBFEC.js';
11
- import { Toolbar } from './chunk-CMLPFIC5.js';
10
+ import { ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
11
+ import { Toolbar } from './chunk-ZN7KUMPY.js';
12
12
  import { Pane } from './chunk-HQZG3OFL.js';
13
13
 
14
14
 
package/dist/list.d.ts CHANGED
@@ -121,6 +121,12 @@ interface ListProps {
121
121
  /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
122
122
  controlInsets?: Sides;
123
123
  className?: string;
124
+ /**
125
+ * The list's id, so app code can address it: name it as a
126
+ * `wireScrollDividers` scroller (with a `Toolbar` above it as its chrome),
127
+ * or find it in tests.
128
+ */
129
+ id?: string;
124
130
  /**
125
131
  * Safe `data-*`, `aria-*`, and structural `role` metadata (a named drop
126
132
  * `region`, `aria-busy`); List-owned structural attributes remain
@@ -138,6 +144,6 @@ interface ListProps {
138
144
  */
139
145
  type ListConfig = Pick<ListProps, 'gap' | 'hAlign' | 'vAlign' | 'dividerSides' | 'textInsets' | 'controlInsets'>;
140
146
  /** A stretch-aligned vertical stack with optional gap, flex, fill, scroll, and dividers. */
141
- declare function List({ children, selectionMode, ariaLabel, gap, flex, fill, hAlign, vAlign, scrollable, scrollClearance, frame, maxHeight, minHeight, surface, inset, contentWidth, contentAlign, dividerSides, textInsets, controlInsets, className, rootAttributes, slot, }: ListProps): kerfjs.SafeHtml;
147
+ declare function List({ children, selectionMode, ariaLabel, gap, flex, fill, hAlign, vAlign, scrollable, scrollClearance, frame, maxHeight, minHeight, surface, inset, contentWidth, contentAlign, dividerSides, textInsets, controlInsets, className, id, rootAttributes, slot, }: ListProps): kerfjs.SafeHtml;
142
148
 
143
149
  export { CssFlex, CssFlexKeyword, CssLength, HorizontalAlignment, List, type ListConfig, type ListContentWidth, type ListInset, type ListProps, type ListSurface, ListVerticalAlignment, ScrollClearance, Sides, UiGap };
package/dist/list.js CHANGED
@@ -1,4 +1,4 @@
1
- export { List } from './chunk-OY2MXIBB.js';
1
+ export { List } from './chunk-NB6POCFH.js';
2
2
 
3
3
 
4
4
 
@@ -1,4 +1,4 @@
1
- export { MessageComposer } from './chunk-23PPPNH6.js';
1
+ export { MessageComposer } from './chunk-4OV272LF.js';
2
2
 
3
3
 
4
4
 
package/dist/nav-stack.js CHANGED
@@ -1,4 +1,4 @@
1
- export { NavStack } from './chunk-Z4DS77U2.js';
1
+ export { NavStack } from './chunk-SOFPUW3O.js';
2
2
 
3
3
 
4
4
 
@@ -25,9 +25,11 @@ interface SegmentedControlProps<Value extends string = string> {
25
25
  className?: string;
26
26
  /** Render as a loading placeholder: every choice keeps its live label or icon, none is selected, and every segment is disabled. */
27
27
  placeholder?: boolean;
28
+ /** Disable the whole control: every segment is disabled and dimmed, the selection stays visible, and the group reports `aria-disabled`. A choice's own `disabled` still disables just that segment. */
29
+ disabled?: boolean;
28
30
  /** Native named-slot assignment when composed inside a web component. */
29
31
  slot?: string;
30
32
  }
31
- declare function SegmentedControl<Value extends string>({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, slot, }: SegmentedControlProps<Value>): kerfjs.SafeHtml;
33
+ declare function SegmentedControl<Value extends string>({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, disabled, slot, }: SegmentedControlProps<Value>): kerfjs.SafeHtml;
32
34
 
33
35
  export { SegmentedControl, type SegmentedControlAppearance, type SegmentedControlChoice, type SegmentedControlLayout, type SegmentedControlProps, type SegmentedControlShape, type SegmentedControlSize };
@@ -1,3 +1,3 @@
1
- export { SegmentedControl } from './chunk-NS2TBYM4.js';
1
+ export { SegmentedControl } from './chunk-LZUUUHNH.js';
2
2
 
3
3
  //# sourceMappingURL=segmented-control.js.map
@@ -1,4 +1,4 @@
1
- export { SplitView } from './chunk-AZ3WT77F.js';
1
+ export { SplitView } from './chunk-P7EJ4CAD.js';
2
2
 
3
3
 
4
4
 
@@ -47,6 +47,13 @@
47
47
  --_kui-button-on-quiet: var(--kui-color-brand-on-quiet);
48
48
  --_kui-button-outline: var(--kui-color-brand-border-loud);
49
49
  }
50
+ .kui-button[data-tone="success"] {
51
+ --_kui-button-fill: var(--kui-color-success-fill-loud);
52
+ --_kui-button-text: var(--kui-color-success-on-loud);
53
+ --_kui-button-quiet: var(--kui-color-success-fill-quiet);
54
+ --_kui-button-on-quiet: var(--kui-color-success-on-fill);
55
+ --_kui-button-outline: var(--kui-color-success-border-loud);
56
+ }
50
57
  .kui-button[data-tone="danger"] {
51
58
  --_kui-button-fill: var(--kui-color-danger-fill-loud);
52
59
  --_kui-button-text: var(--kui-color-danger-on-loud);
@@ -92,9 +99,10 @@
92
99
  border-radius: var(--kui-layout-pill-radius, calc(1px + 1.3125rem));
93
100
  }
94
101
  /* A pressed neutral toggle takes the brand accent, as a pressed toolbar group
95
- control and a selected chip do; brand and danger keep their own tone. */
102
+ control and a selected chip do; semantic tones keep their own color. */
96
103
  .kui-button[aria-pressed="true"]:not(
97
104
  [data-tone="brand"],
105
+ [data-tone="success"],
98
106
  [data-tone="danger"]
99
107
  ) {
100
108
  --_kui-button-fill: var(--kui-color-brand-fill-loud);
@@ -105,7 +113,7 @@
105
113
  }
106
114
  /* A pressed toggle draws on top of its tone and emphasis. Secondary and plain
107
115
  take the tone's quiet fill with a doubled tone outline; primary keeps its
108
- loud fill (darkened for brand and danger) with an inset ring. The second pixel of outline marks
116
+ loud fill (darkened for semantic tones) with an inset ring. The second pixel of outline marks
109
117
  the state without relying on hue. */
110
118
  .kui-button[aria-pressed="true"]:is(
111
119
  [data-emphasis="secondary"],
@@ -133,6 +141,16 @@
133
141
  light-dark(#000, #fff) 18%
134
142
  );
135
143
  }
144
+ .kui-button[data-tone="success"][aria-pressed="true"]:not(
145
+ [data-emphasis="secondary"],
146
+ [data-emphasis="plain"]
147
+ ) {
148
+ --_kui-button-fill: color-mix(
149
+ in oklab,
150
+ var(--kui-color-success-fill-loud),
151
+ light-dark(#000, #fff) 18%
152
+ );
153
+ }
136
154
  .kui-button[data-tone="danger"][aria-pressed="true"]:not(
137
155
  [data-emphasis="secondary"],
138
156
  [data-emphasis="plain"]
@@ -230,6 +248,14 @@
230
248
  cursor: not-allowed;
231
249
  opacity: 0.5;
232
250
  }
251
+ /* Keep a disabled positive action quiet without fading its label below text
252
+ contrast. The subdued fill and border mark it unavailable. */
253
+ .kui-button[data-tone="success"]:is(:disabled, [aria-disabled="true"]) {
254
+ --_kui-button-fill: var(--kui-color-success-fill-quiet);
255
+ --_kui-button-text: var(--kui-color-success-on-fill);
256
+ --_kui-button-border: var(--kui-color-success-border-quiet);
257
+ opacity: 1;
258
+ }
233
259
  .kui-button__icon {
234
260
  display: inline-flex;
235
261
  flex: none;
@@ -47,6 +47,13 @@
47
47
  --_kui-button-on-quiet: var(--kui-color-brand-on-quiet);
48
48
  --_kui-button-outline: var(--kui-color-brand-border-loud);
49
49
  }
50
+ .kui-button[data-tone="success"] {
51
+ --_kui-button-fill: var(--kui-color-success-fill-loud);
52
+ --_kui-button-text: var(--kui-color-success-on-loud);
53
+ --_kui-button-quiet: var(--kui-color-success-fill-quiet);
54
+ --_kui-button-on-quiet: var(--kui-color-success-on-fill);
55
+ --_kui-button-outline: var(--kui-color-success-border-loud);
56
+ }
50
57
  .kui-button[data-tone="danger"] {
51
58
  --_kui-button-fill: var(--kui-color-danger-fill-loud);
52
59
  --_kui-button-text: var(--kui-color-danger-on-loud);
@@ -92,9 +99,10 @@
92
99
  border-radius: var(--kui-layout-pill-radius, calc(1px + 1.3125rem));
93
100
  }
94
101
  /* A pressed neutral toggle takes the brand accent, as a pressed toolbar group
95
- control and a selected chip do; brand and danger keep their own tone. */
102
+ control and a selected chip do; semantic tones keep their own color. */
96
103
  .kui-button[aria-pressed="true"]:not(
97
104
  [data-tone="brand"],
105
+ [data-tone="success"],
98
106
  [data-tone="danger"]
99
107
  ) {
100
108
  --_kui-button-fill: var(--kui-color-brand-fill-loud);
@@ -105,7 +113,7 @@
105
113
  }
106
114
  /* A pressed toggle draws on top of its tone and emphasis. Secondary and plain
107
115
  take the tone's quiet fill with a doubled tone outline; primary keeps its
108
- loud fill (darkened for brand and danger) with an inset ring. The second pixel of outline marks
116
+ loud fill (darkened for semantic tones) with an inset ring. The second pixel of outline marks
109
117
  the state without relying on hue. */
110
118
  .kui-button[aria-pressed="true"]:is(
111
119
  [data-emphasis="secondary"],
@@ -133,6 +141,16 @@
133
141
  light-dark(#000, #fff) 18%
134
142
  );
135
143
  }
144
+ .kui-button[data-tone="success"][aria-pressed="true"]:not(
145
+ [data-emphasis="secondary"],
146
+ [data-emphasis="plain"]
147
+ ) {
148
+ --_kui-button-fill: color-mix(
149
+ in oklab,
150
+ var(--kui-color-success-fill-loud),
151
+ light-dark(#000, #fff) 18%
152
+ );
153
+ }
136
154
  .kui-button[data-tone="danger"][aria-pressed="true"]:not(
137
155
  [data-emphasis="secondary"],
138
156
  [data-emphasis="plain"]
@@ -230,6 +248,14 @@
230
248
  cursor: not-allowed;
231
249
  opacity: 0.5;
232
250
  }
251
+ /* Keep a disabled positive action quiet without fading its label below text
252
+ contrast. The subdued fill and border mark it unavailable. */
253
+ .kui-button[data-tone="success"]:is(:disabled, [aria-disabled="true"]) {
254
+ --_kui-button-fill: var(--kui-color-success-fill-quiet);
255
+ --_kui-button-text: var(--kui-color-success-on-fill);
256
+ --_kui-button-border: var(--kui-color-success-border-quiet);
257
+ opacity: 1;
258
+ }
233
259
  .kui-button__icon {
234
260
  display: inline-flex;
235
261
  flex: none;
@@ -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
@@ -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%;
@@ -48,4 +48,6 @@
48
48
 
49
49
  .kui-framed-list__row[data-highlighted="true"] {
50
50
  background: var(--kui-color-brand-fill-quiet);
51
+ /* The row's controls sit on the tint, not the list's lowered fill. */
52
+ --kui-color-surface-current-tint: var(--kui-color-brand-fill-quiet);
51
53
  }
@@ -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
  )
@@ -224,6 +224,19 @@
224
224
  background: var(--kui-chrome-material);
225
225
  }
226
226
 
227
+ /* The material's surface context at rest (see pane.css); clear chrome keeps
228
+ the inherited context. */
229
+ .kui-nav-stack[data-chrome-presentation="overlay"]:not(
230
+ [data-chrome-material="clear"]
231
+ )
232
+ > .kui-nav-stack__chrome,
233
+ .kui-nav-stack[data-bottom-toolbar-presentation="overlay"]:not(
234
+ [data-bottom-toolbar-material="clear"]
235
+ )
236
+ > .kui-nav-stack__bottom {
237
+ --kui-color-surface-current-tint: var(--kui-chrome-material);
238
+ }
239
+
227
240
  .kui-nav-stack[data-chrome-presentation="overlay"][data-chrome-overlay="measured"]
228
241
  > .kui-nav-stack__chrome:not(.kui-nav-stack__chrome-copy) {
229
242
  position: absolute;
@@ -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
  }
@@ -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. */
@@ -184,12 +184,15 @@
184
184
  --kui-color-surface-current: the opaque surface beneath (default
185
185
  --kui-color-surface); an opaque surface (a raised dialog or popup, a
186
186
  base or raised List) sets it and clears the tint.
187
- --kui-color-surface-current-region: a layout region's translucent
188
- fill over it (a sunken Pane, Workbench area, or TabNavigator scene;
189
- default transparent).
190
- --kui-color-surface-current-tint: a content surface's translucent
191
- fill over that (a SunkenPanel, a sunken Disclosure, a lowered
192
- FramedList, a quiet ContentItem; default transparent).
187
+ --kui-color-surface-current-region: a layout region's fill over it
188
+ (a sunken Pane, Workbench area, or TabNavigator scene; a Pane's or
189
+ TabNavigator's app `background`; default transparent).
190
+ --kui-color-surface-current-tint: a content surface's fill over that
191
+ (a SunkenPanel, a sunken Disclosure, a lowered FramedList or its
192
+ highlighted row, a quiet ContentItem, a TabBar strip, and glass
193
+ chrome at rest: a Pane overlay header, NavStack overlay chrome, a
194
+ TabNavigator overlay bar, a dialog's floating header; default
195
+ transparent).
193
196
  A reader layers the tint over the region over the color, so a content
194
197
  surface inside a sunken region (the common nesting) composes exactly;
195
198
  custom properties cannot fold an inherited value into a new one, so a
@@ -264,9 +267,11 @@
264
267
  var(--kui-color-surface)
265
268
  );
266
269
 
270
+ /* Each brand token below falls back to its tuned literal unless the app
271
+ sets the opt-in --kui-color-brand (see the brand ramp after this block). */
267
272
  --kui-color-brand-fill-loud: var(
268
273
  --wa-color-brand-fill-loud,
269
- light-dark(#0075db, #64d2ff)
274
+ var(--_kui-brand-fill-loud, light-dark(#0075db, #64d2ff))
270
275
  );
271
276
  --kui-color-brand-fill-quiet: var(
272
277
  --wa-color-brand-fill-quiet,
@@ -312,7 +317,7 @@
312
317
  4.65:1 on the page (5.2:1 on white). */
313
318
  --kui-color-brand-on-quiet: var(
314
319
  --wa-color-brand-on-quiet,
315
- light-dark(#0c62f3, #8acbff)
320
+ var(--_kui-brand-on-quiet, light-dark(#0c62f3, #8acbff))
316
321
  );
317
322
  --kui-color-brand-on-normal: var(
318
323
  --wa-color-brand-on-normal,
@@ -328,7 +333,10 @@
328
333
  4.26:1 over brand-fill-normal (#d6ecff), while #1a5dcf (same hue) clears
329
334
  ~4.9:1 there and ~5.5:1 over brand-fill-quiet (#ebf6ff). The dark value
330
335
  matches brand-on-quiet, which already passes over the dark fills. */
331
- --kui-color-brand-on-fill: light-dark(#1a5dcf, #8acbff);
336
+ --kui-color-brand-on-fill: var(
337
+ --_kui-brand-on-fill,
338
+ light-dark(#1a5dcf, #8acbff)
339
+ );
332
340
 
333
341
  /* Pop is attractive emphasis without status meaning: use it for featured,
334
342
  novel, or celebratory content, never as a success/danger substitute. */
@@ -538,7 +546,11 @@
538
546
 
539
547
  --kui-focus-ring: var(
540
548
  --wa-focus-ring,
541
- 3px solid var(--wa-color-focus, light-dark(#007aff, #64d2ff))
549
+ 3px solid
550
+ var(
551
+ --wa-color-focus,
552
+ var(--_kui-brand-focus, light-dark(#007aff, #64d2ff))
553
+ )
542
554
  );
543
555
  /* Elevation mirrors webawesome.css's scheme-aware shadows: dark raises the
544
556
  alpha so a raised surface still separates from a near-black page. */
@@ -596,6 +608,37 @@
596
608
  --kui-color-surface-current-tint: initial;
597
609
  }
598
610
 
611
+ /* The opt-in brand, left undeclared so the tuned literals apply:
612
+ --kui-color-brand: any color an app sets on :root;
613
+ the whole brand ramp follows it, the way a scoped tint's accent re-derives
614
+ its ramp: the loud fill (and the quiet/normal fills and borders mixed from
615
+ it), brand text and links, brand text on brand fills, and the focus ring.
616
+ Lightness is clamped per scheme so every pair keeps WCAG AA for any hue
617
+ and chroma (the brand-ramp browser test sweeps them): a light fill stays
618
+ dark enough for white text and a dark one light enough for near-black
619
+ text; brand text stays dark enough for the page, the surface, and the
620
+ brand fills in light, and light enough in dark. Unset, each private
621
+ derivation references an undefined variable, is invalid, and the tokens
622
+ above keep their tuned literals exactly. Web Awesome's theme reads the
623
+ same derivations (webawesome.css). */
624
+ @supports (color: oklch(from red l c h)) {
625
+ :root {
626
+ --_kui-brand-fill-loud: light-dark(
627
+ oklch(from var(--kui-color-brand) min(l, 0.46) c h),
628
+ oklch(from var(--kui-color-brand) clamp(0.74, l, 0.86) c h)
629
+ );
630
+ --_kui-brand-on-quiet: light-dark(
631
+ oklch(from var(--kui-color-brand) min(l, 0.43) c h),
632
+ oklch(from var(--kui-color-brand) max(l, 0.86) c h)
633
+ );
634
+ --_kui-brand-on-fill: light-dark(
635
+ oklch(from var(--kui-color-brand) min(l, 0.38) c h),
636
+ oklch(from var(--kui-color-brand) max(l, 0.9) c h)
637
+ );
638
+ --_kui-brand-focus: var(--_kui-brand-fill-loud);
639
+ }
640
+ }
641
+
599
642
  @supports (color: oklch(from red l c h)) {
600
643
  @media not (prefers-contrast: more) {
601
644
  [data-kui-tint="light"] {
@@ -646,21 +689,22 @@
646
689
  scope: the loud fill (buttons, the current row, checked controls), its
647
690
  on-loud text, brand text and links, text on brand fills, and the focus
648
691
  ring. Lightness is clamped so each pair keeps WCAG AA for any accent:
649
- a light scope's fill stays at or below 50% under white text, a dark
692
+ a light scope's fill stays at or below 46% under white text, a dark
650
693
  scope's between 72% and 85% under near-black text, and brand text at
651
- most 45% (light) or at least 80% (dark). */
694
+ most 36% (light) or at least 94% (dark). Dark on-fill text also caps
695
+ chroma so a vivid red stays light after gamut clipping. */
652
696
  [data-kui-tint="light"][data-kui-tint-accent] {
653
697
  --wa-color-brand-fill-loud: oklch(
654
- from var(--_kui-tint-accent) min(l, 0.5) c h
698
+ from var(--_kui-tint-accent) min(l, 0.46) c h
655
699
  );
656
700
  --wa-color-brand-on-loud: #fff;
657
701
  --wa-color-brand-on-quiet: oklch(
658
- from var(--_kui-tint-accent) min(l, 0.45) c h
702
+ from var(--_kui-tint-accent) min(l, 0.36) c h
659
703
  );
660
704
  --kui-color-brand-on-fill: oklch(
661
- from var(--_kui-tint-accent) min(l, 0.38) c h
705
+ from var(--_kui-tint-accent) min(l, 0.3) c h
662
706
  );
663
- --wa-color-focus: oklch(from var(--_kui-tint-accent) min(l, 0.5) c h);
707
+ --wa-color-focus: oklch(from var(--_kui-tint-accent) min(l, 0.46) c h);
664
708
  }
665
709
 
666
710
  [data-kui-tint="dark"][data-kui-tint-accent] {
@@ -669,13 +713,13 @@
669
713
  );
670
714
  --wa-color-brand-on-loud: #111113;
671
715
  --wa-color-brand-on-quiet: oklch(
672
- from var(--_kui-tint-accent) max(l, 0.8) c h
716
+ from var(--_kui-tint-accent) max(l, 0.94) c h
673
717
  );
674
718
  --kui-color-brand-on-fill: oklch(
675
- from var(--_kui-tint-accent) max(l, 0.82) c h
719
+ from var(--_kui-tint-accent) max(l, 0.95) min(c, 0.12) h
676
720
  );
677
721
  --wa-color-focus: oklch(
678
- from var(--_kui-tint-accent) clamp(0.72, l, 0.85) c h
722
+ from var(--_kui-tint-accent) clamp(0.78, l, 0.85) c h
679
723
  );
680
724
  }
681
725
  }
@@ -48,4 +48,6 @@
48
48
 
49
49
  .kui-framed-list__row[data-highlighted="true"] {
50
50
  background: var(--kui-color-brand-fill-quiet);
51
+ /* The row's controls sit on the tint, not the list's lowered fill. */
52
+ --kui-color-surface-current-tint: var(--kui-color-brand-fill-quiet);
51
53
  }
@@ -224,6 +224,19 @@
224
224
  background: var(--kui-chrome-material);
225
225
  }
226
226
 
227
+ /* The material's surface context at rest (see pane.css); clear chrome keeps
228
+ the inherited context. */
229
+ .kui-nav-stack[data-chrome-presentation="overlay"]:not(
230
+ [data-chrome-material="clear"]
231
+ )
232
+ > .kui-nav-stack__chrome,
233
+ .kui-nav-stack[data-bottom-toolbar-presentation="overlay"]:not(
234
+ [data-bottom-toolbar-material="clear"]
235
+ )
236
+ > .kui-nav-stack__bottom {
237
+ --kui-color-surface-current-tint: var(--kui-chrome-material);
238
+ }
239
+
227
240
  .kui-nav-stack[data-chrome-presentation="overlay"][data-chrome-overlay="measured"]
228
241
  > .kui-nav-stack__chrome:not(.kui-nav-stack__chrome-copy) {
229
242
  position: absolute;