@kerfjs/ui 5.0.0-beta.61 → 5.0.0-beta.62

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 (80) hide show
  1. package/ai/component-catalog.json +12 -5
  2. package/ai/component-composition.json +8 -1
  3. package/ai/components/pane.md +3 -2
  4. package/ai/components/select.md +1 -0
  5. package/ai/components/toolbar-text.md +2 -1
  6. package/ai/public-api-signatures-v1.md +27 -11
  7. package/ai/skill.md +3 -1
  8. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  9. package/dist/catalog.js +3 -3
  10. package/dist/{chunk-TEYARRAK.js → chunk-4MG3KXJM.js} +4 -1
  11. package/dist/chunk-4MG3KXJM.js.map +1 -0
  12. package/dist/{chunk-NYL63PSD.js → chunk-ENGVYQPG.js} +4 -3
  13. package/dist/chunk-ENGVYQPG.js.map +1 -0
  14. package/dist/{chunk-5PESG2VP.js → chunk-KYDDCJXU.js} +4 -3
  15. package/dist/chunk-KYDDCJXU.js.map +1 -0
  16. package/dist/{chunk-MERWRTBG.js → chunk-N2DRU35S.js} +1 -1
  17. package/dist/chunk-N2DRU35S.js.map +1 -0
  18. package/dist/{chunk-EZVNC4J6.js → chunk-OD2VXAPL.js} +4 -1
  19. package/dist/chunk-OD2VXAPL.js.map +1 -0
  20. package/dist/{chunk-N3GKMPDW.js → chunk-RKTTF3L4.js} +3 -1
  21. package/dist/chunk-RKTTF3L4.js.map +1 -0
  22. package/dist/{chunk-C4OYOKWX.js → chunk-UA6JW52D.js} +36 -2
  23. package/dist/chunk-UA6JW52D.js.map +1 -0
  24. package/dist/{chunk-3SXMEK3B.js → chunk-V3DX3K65.js} +1 -1
  25. package/dist/chunk-V3DX3K65.js.map +1 -0
  26. package/dist/{chunk-Y74NWGQW.js → chunk-X6ADHJNI.js} +45 -20
  27. package/dist/chunk-X6ADHJNI.js.map +1 -0
  28. package/dist/collapsible-panel.js +2 -2
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +6 -6
  31. package/dist/nav-stack.d.ts +3 -1
  32. package/dist/nav-stack.js +1 -1
  33. package/dist/pane.d.ts +8 -3
  34. package/dist/pane.js +1 -1
  35. package/dist/select.d.ts +2 -0
  36. package/dist/select.js +1 -1
  37. package/dist/split-view.d.ts +1 -1
  38. package/dist/split-view.js +1 -1
  39. package/dist/styles/app-tab.css +84 -0
  40. package/dist/styles/nav-stack.css +13 -0
  41. package/dist/styles/pane.css +33 -0
  42. package/dist/styles/resizable-region.css +9 -0
  43. package/dist/styles/select.css +15 -0
  44. package/dist/styles/split-view.css +6 -0
  45. package/dist/styles/tab-bar.css +3 -0
  46. package/dist/styles/tab-scaffold.css +13 -0
  47. package/dist/styles/toolbar-text.css +15 -6
  48. package/dist/styles/toolbar.css +7 -0
  49. package/dist/styles/workbench.css +117 -93
  50. package/dist/tab-bar.d.ts +6 -2
  51. package/dist/tab-bar.js +1 -1
  52. package/dist/tab-scaffold.d.ts +3 -0
  53. package/dist/tab-scaffold.js +1 -0
  54. package/dist/tab-scaffold.js.map +1 -1
  55. package/dist/toolbar-text.d.ts +1 -1
  56. package/dist/toolbar-text.js +1 -1
  57. package/dist/toolbar.d.ts +2 -2
  58. package/dist/toolbar.js +1 -1
  59. package/dist/wire-tab-bars.js +1 -1
  60. package/dist/workbench.js +1 -1
  61. package/docs/collapsible-panel.md +4 -3
  62. package/docs/component-selection.md +27 -8
  63. package/docs/layout.md +31 -0
  64. package/docs/nav-stack.md +5 -0
  65. package/docs/split-view.md +5 -0
  66. package/docs/sunken-panel.md +8 -1
  67. package/docs/tab-scaffold.md +5 -0
  68. package/docs/webawesome-theme.md +36 -6
  69. package/docs/workbench.md +22 -10
  70. package/llms.txt +4 -0
  71. package/package.json +1 -1
  72. package/dist/chunk-3SXMEK3B.js.map +0 -1
  73. package/dist/chunk-5PESG2VP.js.map +0 -1
  74. package/dist/chunk-C4OYOKWX.js.map +0 -1
  75. package/dist/chunk-EZVNC4J6.js.map +0 -1
  76. package/dist/chunk-MERWRTBG.js.map +0 -1
  77. package/dist/chunk-N3GKMPDW.js.map +0 -1
  78. package/dist/chunk-NYL63PSD.js.map +0 -1
  79. package/dist/chunk-TEYARRAK.js.map +0 -1
  80. package/dist/chunk-Y74NWGQW.js.map +0 -1
@@ -91,21 +91,11 @@
91
91
  flex-direction: column;
92
92
  }
93
93
 
94
- .kui-workbench:has(
95
- > .kui-workbench__rail--left[data-presentation="inline"]:not(
96
- [data-collapsed="true"]
97
- )
98
- )
99
- > .kui-workbench__center {
94
+ .kui-workbench[data-left-inline-expanded="true"] > .kui-workbench__center {
100
95
  --kui-edge-inset-inline-start: 0px;
101
96
  }
102
97
 
103
- .kui-workbench:has(
104
- > .kui-workbench__rail--right[data-presentation="inline"]:not(
105
- [data-collapsed="true"]
106
- )
107
- )
108
- > .kui-workbench__center {
98
+ .kui-workbench[data-right-inline-expanded="true"] > .kui-workbench__center {
109
99
  --kui-edge-inset-inline-end: 0px;
110
100
  }
111
101
 
@@ -115,18 +105,64 @@
115
105
  overflow: auto;
116
106
  }
117
107
 
118
- .kui-workbench__main:has(
119
- ~ .kui-workbench__drawer[data-presentation="inline"]:not(
120
- [data-collapsed="true"]
121
- )
122
- ) {
123
- --kui-edge-inset-block-end: 0px;
108
+ /* When no Pane chrome is composed, the Workbench region owns its scroll.
109
+ A resizable auto-overflow panel instead scrolls its content box so its
110
+ separator handle remains fixed. Paint only that owner. */
111
+ .kui-workbench__main[data-appearance="sunken"],
112
+ :is(.kui-workbench__rail, .kui-workbench__drawer)[data-appearance="sunken"] {
113
+ background: var(
114
+ --kui-sunken-panel-background,
115
+ var(--kui-color-surface-lowered)
116
+ );
117
+ }
118
+
119
+ :is(
120
+ .kui-workbench__rail,
121
+ .kui-workbench__drawer
122
+ )[data-appearance="sunken"][data-resizable="true"][data-content-overflow="auto"] {
123
+ background: transparent;
124
+ }
125
+
126
+ :is(
127
+ .kui-workbench__rail,
128
+ .kui-workbench__drawer
129
+ )[data-appearance="sunken"][data-resizable="true"][data-content-overflow="auto"]
130
+ > .kui-workbench__panel-content {
131
+ background: var(
132
+ --kui-sunken-panel-background,
133
+ var(--kui-color-surface-lowered)
134
+ );
135
+ }
136
+
137
+ .kui-workbench__main[data-drawer-inline-expanded="true"] {
138
+ --_kui-workbench-content-edge-block-end: 0px;
124
139
  }
125
140
 
126
141
  /* The main area and each panel's content are the consumers: they pad for the
127
142
  edges their region still touches, the main area inside its scroller so
128
143
  content scrolls under an unsafe edge but can always be scrolled clear. A
129
144
  region whose only child is a Pane or layout lets that child own the insets. */
145
+ .kui-workbench__center,
146
+ .kui-workbench__rail,
147
+ .kui-workbench__drawer {
148
+ --_kui-workbench-content-edge-block-start: var(--kui-edge-inset-block-start);
149
+ --_kui-workbench-content-edge-block-end: var(--kui-edge-inset-block-end);
150
+ --_kui-workbench-content-edge-inline-start: var(
151
+ --kui-edge-inset-inline-start
152
+ );
153
+ --_kui-workbench-content-edge-inline-end: var(--kui-edge-inset-inline-end);
154
+ }
155
+
156
+ .kui-workbench__main,
157
+ .kui-workbench__panel-content {
158
+ /* Clear the public context at the region, so arbitrary app children inherit
159
+ zero without a selector whose subject is every child in the document. */
160
+ --kui-edge-inset-block-start: 0px;
161
+ --kui-edge-inset-block-end: 0px;
162
+ --kui-edge-inset-inline-start: 0px;
163
+ --kui-edge-inset-inline-end: 0px;
164
+ }
165
+
130
166
  .kui-workbench__main:not(
131
167
  :has(
132
168
  > :is(
@@ -149,44 +185,24 @@
149
185
  ):only-child
150
186
  )
151
187
  ) {
152
- padding-block: var(--kui-edge-inset-block-start)
153
- var(--kui-edge-inset-block-end);
154
- padding-inline: var(--kui-edge-inset-inline-start)
155
- var(--kui-edge-inset-inline-end);
156
- scroll-padding-block: var(--kui-edge-inset-block-start)
157
- var(--kui-edge-inset-block-end);
158
- }
159
-
160
- /* Every other child sees a cleared context. The exemption is tested on the
161
- child itself, never as `region:not(:has(…)) > *`: a `:has()` before a
162
- universal child makes each child of the region re-check the region's
163
- `:has()` on every DOM change (~46ms per restyle for 1000 rows in Chromium).
164
- SplitView, NavStack, TabScaffold, and CollapsiblePanel content use the same
165
- shape. */
166
- .kui-workbench__main
167
- > :not(
168
- :is(
169
- [data-component="pane"],
170
- [data-component="nav-stack"],
171
- [data-component="split-view"],
172
- [data-component="workbench"],
173
- [data-component="tab-scaffold"]
174
- ):only-child
175
- ),
176
- .kui-workbench__panel-content
177
- > :not(
178
- :is(
179
- [data-component="pane"],
180
- [data-component="nav-stack"],
181
- [data-component="split-view"],
182
- [data-component="workbench"],
183
- [data-component="tab-scaffold"]
184
- ):only-child
185
- ) {
186
- --kui-edge-inset-block-start: 0px;
187
- --kui-edge-inset-block-end: 0px;
188
- --kui-edge-inset-inline-start: 0px;
189
- --kui-edge-inset-inline-end: 0px;
188
+ padding-block: var(--_kui-workbench-content-edge-block-start)
189
+ var(--_kui-workbench-content-edge-block-end);
190
+ padding-inline: var(--_kui-workbench-content-edge-inline-start)
191
+ var(--_kui-workbench-content-edge-inline-end);
192
+ scroll-padding-block: var(--_kui-workbench-content-edge-block-start)
193
+ var(--_kui-workbench-content-edge-block-end);
194
+ }
195
+
196
+ /* A nested Workbench owns its safe-area insets. Other sole layout children
197
+ restore this context from their own stylesheets. */
198
+ :is(.kui-workbench__main, .kui-workbench__panel-content)
199
+ > [data-component="workbench"]:only-child {
200
+ --kui-edge-inset-block-start: var(--_kui-workbench-content-edge-block-start);
201
+ --kui-edge-inset-block-end: var(--_kui-workbench-content-edge-block-end);
202
+ --kui-edge-inset-inline-start: var(
203
+ --_kui-workbench-content-edge-inline-start
204
+ );
205
+ --kui-edge-inset-inline-end: var(--_kui-workbench-content-edge-inline-end);
190
206
  }
191
207
 
192
208
  /* A region whose only child is a Pane or layout lets it fill the region, so a
@@ -231,6 +247,11 @@
231
247
  overflow: visible;
232
248
  }
233
249
 
250
+ .kui-workbench__rail:has(wa-dropdown[open]),
251
+ .kui-workbench__drawer:has(wa-dropdown[open]) {
252
+ z-index: var(--kui-workbench-popup-z, 5);
253
+ }
254
+
234
255
  /* The fixed-size sliding content. The rail/drawer keeps a definite size on its
235
256
  content so the transform reads as a slide, not a squeeze. */
236
257
  .kui-workbench__rail .kui-workbench__panel-content {
@@ -254,6 +275,11 @@
254
275
  transition: none;
255
276
  }
256
277
 
278
+ :is(.kui-workbench__rail, .kui-workbench__drawer)[data-resizing="true"]
279
+ > .kui-workbench__panel-content {
280
+ transition: none;
281
+ }
282
+
257
283
  .kui-workbench__rail[data-collapse-motion="fade-slide"][data-collapsed="true"]
258
284
  .kui-workbench__panel-content,
259
285
  .kui-workbench__drawer[data-collapse-motion="fade-slide"][data-collapsed="true"]
@@ -579,10 +605,7 @@
579
605
  `responsiveFillAt`), and inline above it. The Workbench becomes a named
580
606
  inline-size container only when a panel opts in. The work area keeps its
581
607
  safe-area inset under an overlay panel. */
582
- .kui-workbench:has(
583
- > .kui-workbench__rail[data-responsive-overlay-at],
584
- > .kui-workbench__center > .kui-workbench__drawer[data-responsive-overlay-at]
585
- ) {
608
+ .kui-workbench[data-responsive-overlay="true"] {
586
609
  container: kui-workbench / inline-size;
587
610
  }
588
611
 
@@ -622,16 +645,12 @@
622
645
  display: none;
623
646
  }
624
647
 
625
- .kui-workbench:has(
626
- > .kui-workbench__rail--left[data-responsive-overlay-at="narrow"][data-presentation="inline"]
627
- )
648
+ .kui-workbench[data-left-responsive-inline="narrow"]
628
649
  > .kui-workbench__center {
629
650
  --kui-edge-inset-inline-start: var(--_kui-workbench-safe-inline-start);
630
651
  }
631
652
 
632
- .kui-workbench:has(
633
- > .kui-workbench__rail--right[data-responsive-overlay-at="narrow"][data-presentation="inline"]
634
- )
653
+ .kui-workbench[data-right-responsive-inline="narrow"]
635
654
  > .kui-workbench__center {
636
655
  --kui-edge-inset-inline-end: var(--_kui-workbench-safe-inline-end);
637
656
  }
@@ -665,10 +684,10 @@
665
684
  display: none;
666
685
  }
667
686
 
668
- .kui-workbench__main:has(
669
- ~ .kui-workbench__drawer[data-responsive-overlay-at="narrow"][data-presentation="inline"]
670
- ) {
671
- --kui-edge-inset-block-end: var(--_kui-workbench-safe-block-end);
687
+ .kui-workbench__main[data-drawer-responsive-inline="narrow"] {
688
+ --_kui-workbench-content-edge-block-end: var(
689
+ --_kui-workbench-safe-block-end
690
+ );
672
691
  }
673
692
  }
674
693
 
@@ -708,16 +727,12 @@
708
727
  display: none;
709
728
  }
710
729
 
711
- .kui-workbench:has(
712
- > .kui-workbench__rail--left[data-responsive-overlay-at="compact"][data-presentation="inline"]
713
- )
730
+ .kui-workbench[data-left-responsive-inline="compact"]
714
731
  > .kui-workbench__center {
715
732
  --kui-edge-inset-inline-start: var(--_kui-workbench-safe-inline-start);
716
733
  }
717
734
 
718
- .kui-workbench:has(
719
- > .kui-workbench__rail--right[data-responsive-overlay-at="compact"][data-presentation="inline"]
720
- )
735
+ .kui-workbench[data-right-responsive-inline="compact"]
721
736
  > .kui-workbench__center {
722
737
  --kui-edge-inset-inline-end: var(--_kui-workbench-safe-inline-end);
723
738
  }
@@ -751,13 +766,28 @@
751
766
  display: none;
752
767
  }
753
768
 
754
- .kui-workbench__main:has(
755
- ~ .kui-workbench__drawer[data-responsive-overlay-at="compact"][data-presentation="inline"]
756
- ) {
757
- --kui-edge-inset-block-end: var(--_kui-workbench-safe-block-end);
769
+ .kui-workbench__main[data-drawer-responsive-inline="compact"] {
770
+ --_kui-workbench-content-edge-block-end: var(
771
+ --_kui-workbench-safe-block-end
772
+ );
758
773
  }
759
774
  }
760
775
 
776
+ /* Release the panel and its sliding/scrolling content while a PopupMenu or
777
+ Web Awesome dropdown is open. This comes after resize clipping and keeps
778
+ the popup in place through wa-show/wa-after-hide without a prop change. */
779
+ .kui-workbench__rail:not([data-collapsed="true"]):has(wa-dropdown[open]),
780
+ .kui-workbench__drawer:not([data-collapsed="true"]):has(wa-dropdown[open]) {
781
+ overflow: visible;
782
+ clip-path: none;
783
+ }
784
+ .kui-workbench__rail:not([data-collapsed="true"]):has(wa-dropdown[open])
785
+ > .kui-workbench__panel-content,
786
+ .kui-workbench__drawer:not([data-collapsed="true"]):has(wa-dropdown[open])
787
+ > .kui-workbench__panel-content {
788
+ overflow: visible;
789
+ }
790
+
761
791
  /* A collapsed panel's restore control floats in a corner of the Workbench
762
792
  itself, never of the viewport, so an embedded Workbench cannot float it over
763
793
  page chrome outside it. It is inset from the corner plus the unsafe area of
@@ -860,20 +890,15 @@
860
890
  the open rail), so the overlay's own floating controls stay; FloatingToolbar
861
891
  and the layouts' restore corners read it. A direct child
862
892
  resolves the Workbench's own container query, never a nested one's. */
863
- .kui-workbench:has(
864
- > .kui-workbench__rail[data-presentation="overlay"]:not(
865
- [data-collapsed="true"]
866
- )
867
- )
893
+ .kui-workbench[data-rail-overlay-expanded="true"]
868
894
  > :is(.kui-workbench__center, .kui-workbench__restore) {
869
895
  --_kui-workbench-covered: hidden;
870
896
  }
871
897
 
872
898
  @container kui-workbench (max-width: 44rem) {
873
- .kui-workbench:has(
874
- > .kui-workbench__rail[data-responsive-overlay-at="narrow"][data-presentation="inline"]:not(
875
- [data-collapsed="true"]
876
- )
899
+ .kui-workbench:is(
900
+ [data-left-responsive-expanded="narrow"],
901
+ [data-right-responsive-expanded="narrow"]
877
902
  )
878
903
  > :is(.kui-workbench__center, .kui-workbench__restore) {
879
904
  --_kui-workbench-covered: hidden;
@@ -881,10 +906,9 @@
881
906
  }
882
907
 
883
908
  @container kui-workbench (max-width: 28rem) {
884
- .kui-workbench:has(
885
- > .kui-workbench__rail[data-responsive-overlay-at="compact"][data-presentation="inline"]:not(
886
- [data-collapsed="true"]
887
- )
909
+ .kui-workbench:is(
910
+ [data-left-responsive-expanded="compact"],
911
+ [data-right-responsive-expanded="compact"]
888
912
  )
889
913
  > :is(.kui-workbench__center, .kui-workbench__restore) {
890
914
  --_kui-workbench-covered: hidden;
package/dist/tab-bar.d.ts CHANGED
@@ -5,6 +5,8 @@ type TabActivation = 'automatic' | 'manual';
5
5
  type TabBarAllocation = 'intrinsic' | 'fill';
6
6
  type TabBarPresentation = 'rail' | 'segmented' | 'inspector';
7
7
  type TabBarTrailingPlacement = 'separate' | 'adjacent';
8
+ /** Tablist width where segmented AppTabs switch to icon-only. */
9
+ type TabBarIconOnlyAt = 'wide' | 'narrow' | 'compact';
8
10
  interface TabBarProps {
9
11
  id: string;
10
12
  label: string;
@@ -28,12 +30,14 @@ interface TabBarProps {
28
30
  allocation?: TabBarAllocation;
29
31
  /** Named strip chrome for application rails, segmented tabs, or inspectors. */
30
32
  presentation?: TabBarPresentation;
33
+ /** Switch segmented AppTabs to icon-only at tablist widths of 832, 704, or 448px. Each tab needs a leading icon. */
34
+ iconOnlyAt?: TabBarIconOnlyAt;
31
35
  /** Keep a trailing action beside the final tab or at the far edge of the bar. */
32
36
  trailingPlacement?: TabBarTrailingPlacement;
33
37
  /** Native named-slot assignment when composed inside a web component. */
34
38
  slot?: string;
35
39
  }
36
40
  /** Render a controlled tab strip. The application owns selection, order, and persistence. */
37
- declare function TabBar({ id, label, children, leading, trailing, end, className, activation, allocation, presentation, trailingPlacement, slot, }: TabBarProps): kerfjs.SafeHtml;
41
+ declare function TabBar({ id, label, children, leading, trailing, end, className, activation, allocation, presentation, iconOnlyAt, trailingPlacement, slot, }: TabBarProps): kerfjs.SafeHtml;
38
42
 
39
- export { type TabActivation, TabBar, type TabBarAllocation, type TabBarPresentation, type TabBarProps, type TabBarTrailingPlacement };
43
+ export { type TabActivation, TabBar, type TabBarAllocation, type TabBarIconOnlyAt, type TabBarPresentation, type TabBarProps, type TabBarTrailingPlacement };
package/dist/tab-bar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { TabBar } from './chunk-N3GKMPDW.js';
1
+ export { TabBar } from './chunk-RKTTF3L4.js';
2
2
 
3
3
  //# sourceMappingURL=tab-bar.js.map
@@ -1,4 +1,5 @@
1
1
  import { SafeHtml } from 'kerfjs';
2
+ import { PaneAppearance } from './pane.js';
2
3
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3
4
 
4
5
  interface TabScaffoldTabBase<Id extends string> {
@@ -8,6 +9,8 @@ interface TabScaffoldTabBase<Id extends string> {
8
9
  icon?: SafeHtml;
9
10
  /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */
10
11
  content: KerfUiContent;
12
+ /** Background of this scene's scrolling work surface. */
13
+ appearance?: PaneAppearance;
11
14
  }
12
15
  /** A tab with an optional count or short-status badge. */
13
16
  interface TabScaffoldTabCountBadge {
@@ -26,6 +26,7 @@ function TabScaffold({
26
26
  class: "kui-tab-scaffold__scene",
27
27
  "data-tab-scaffold-scene": tab.id,
28
28
  "data-active": String(tab.id === active),
29
+ "data-appearance": tab.appearance === "sunken" ? "sunken" : void 0,
29
30
  "aria-hidden": String(tab.id !== active),
30
31
  children: tab.content
31
32
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tab-scaffold.tsx"],"names":[],"mappings":"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","file":"tab-scaffold.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge } from './badge.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\ninterface TabScaffoldTabBase<Id extends string> {\n id: Id;\n label: string;\n /** Decorative icon shown above the label in the bottom bar. */\n icon?: SafeHtml;\n /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */\n content: KerfUiContent;\n}\n\n/** A tab with an optional count or short-status badge. */\ninterface TabScaffoldTabCountBadge {\n /**\n * Optional count or short status shown as a solid danger `Badge` at the\n * top-trailing corner of the tab icon (the iOS tab-bar badge). Omitted, `''`,\n * or non-finite numbers render no badge. The visual badge is `aria-hidden`;\n * its meaning reaches assistive technology through `badgeLabel`.\n *\n * Pass `true` instead for the text-free dot (new content without a count).\n */\n badge?: string | number;\n /**\n * Localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"3 unread\"` → `\"Inbox, 3 unread\"`).\n * Defaults to the badge text itself. Ignored when no badge renders.\n */\n badgeLabel?: string;\n}\n\n/** A tab with the text-free dot badge (new content without a count). */\ninterface TabScaffoldTabDotBadge {\n /**\n * `true` shows a solid danger dot `Badge` at the top-trailing corner of the\n * tab icon — the iOS tab-bar dot for new content without a count.\n */\n badge: true;\n /**\n * Required localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"New activity\"` →\n * `\"Feed, New activity\"`). A dot has no text, so this is its only meaning.\n */\n badgeLabel: string;\n}\n\n/** One bottom-bar destination: its label, optional icon and badge, and content. */\nexport type TabScaffoldTab<Id extends string = string> =\n TabScaffoldTabBase<Id> & (TabScaffoldTabCountBadge | TabScaffoldTabDotBadge);\n\nexport interface TabScaffoldProps<Id extends string = string> {\n id: string;\n /** Accessible name for the tab bar. */\n label: string;\n tabs: readonly TabScaffoldTab<Id>[];\n /** The controlled active tab id (the app owns selection). */\n active: NoInfer<Id>;\n /**\n * The line over the tab bar, where it meets the active scene. `scroll`\n * (default) shows it only while more of the scene's content lies below —\n * never at the scroll end or when the content fits — once\n * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above\n * the scaffold; unwired, it does not show. `always` shows it without the\n * wiring; `none` never, even when wired. The line is the bar's own 1px top\n * border, so no state moves the bar or a tab.\n */\n chromeDividers?: 'scroll' | 'always' | 'none';\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches\n * between major sections, each tab keeping its own content (usually a `NavStack`)\n * mounted so its stack and scroll survive a switch. Controlled — the app owns\n * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.\n * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of\n * a bottom bar. See `docs/23-app-layouts.md` §3.4.\n */\nexport function TabScaffold<Id extends string>({\n id,\n label,\n tabs,\n active,\n chromeDividers = 'scroll',\n className = '',\n slot,\n}: TabScaffoldProps<Id>) {\n return (\n <section\n class={`kui-tab-scaffold ${className}`.trim()}\n id={id}\n data-component=\"tab-scaffold\"\n data-tab-scaffold-id={id}\n data-chrome-dividers={\n chromeDividers === 'always' || chromeDividers === 'none'\n ? chromeDividers\n : undefined\n }\n slot={slot}\n >\n <div class=\"kui-tab-scaffold__scenes\">\n {tabs.map((tab) => (\n <div\n class=\"kui-tab-scaffold__scene\"\n data-tab-scaffold-scene={tab.id}\n data-active={String(tab.id === active)}\n aria-hidden={String(tab.id !== active)}\n >\n {tab.content}\n </div>\n ))}\n </div>\n <nav class=\"kui-tab-scaffold__bar\" role=\"tablist\" aria-label={label}>\n {tabs.map((tab) => {\n const dot = tab.badge === true;\n const badgeText = dot ? undefined : normalizeBadge(tab.badge);\n const hasBadge = dot || badgeText !== undefined;\n const badge = hasBadge && (\n <span\n class=\"kui-tab-scaffold__tab-badge\"\n data-badge-kind={dot ? 'dot' : 'text'}\n >\n {badgeText === undefined ? (\n <Badge tone=\"danger\" size=\"dot\" ariaHidden />\n ) : (\n <Badge\n tone=\"danger\"\n appearance=\"solid\"\n size=\"compact\"\n ariaHidden\n >\n {badgeText}\n </Badge>\n )}\n </span>\n );\n // A dot has no text fallback: without a usable badgeLabel (reachable\n // only from untyped callers) the tab keeps its plain accessible name.\n const badgeLabel = hasBadge\n ? tab.badgeLabel?.trim() || badgeText\n : undefined;\n return (\n <button\n type=\"button\"\n class=\"kui-tab-scaffold__tab\"\n role=\"tab\"\n data-tab-scaffold-tab={tab.id}\n aria-selected={String(tab.id === active)}\n tabindex={tab.id === active ? '0' : '-1'}\n aria-label={\n badgeLabel === undefined\n ? undefined\n : `${tab.label}, ${badgeLabel}`\n }\n data-has-badge={hasBadge ? 'true' : undefined}\n >\n {tab.icon ? (\n <span class=\"kui-tab-scaffold__tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n {badge}\n </span>\n ) : (\n badge\n )}\n <span class=\"kui-tab-scaffold__tab-label\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </section>\n );\n}\n\nfunction normalizeBadge(\n badge: string | number | undefined,\n): string | undefined {\n if (typeof badge === 'number')\n return Number.isFinite(badge) ? String(badge) : undefined;\n if (typeof badge === 'string' && badge.trim()) return badge.trim();\n return undefined;\n}\n"]}
1
+ {"version":3,"sources":["../src/tab-scaffold.tsx"],"names":[],"mappings":"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","file":"tab-scaffold.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge } from './badge.js';\nimport type { PaneAppearance } from './pane.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\ninterface TabScaffoldTabBase<Id extends string> {\n id: Id;\n label: string;\n /** Decorative icon shown above the label in the bottom bar. */\n icon?: SafeHtml;\n /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */\n content: KerfUiContent;\n /** Background of this scene's scrolling work surface. */\n appearance?: PaneAppearance;\n}\n\n/** A tab with an optional count or short-status badge. */\ninterface TabScaffoldTabCountBadge {\n /**\n * Optional count or short status shown as a solid danger `Badge` at the\n * top-trailing corner of the tab icon (the iOS tab-bar badge). Omitted, `''`,\n * or non-finite numbers render no badge. The visual badge is `aria-hidden`;\n * its meaning reaches assistive technology through `badgeLabel`.\n *\n * Pass `true` instead for the text-free dot (new content without a count).\n */\n badge?: string | number;\n /**\n * Localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"3 unread\"` → `\"Inbox, 3 unread\"`).\n * Defaults to the badge text itself. Ignored when no badge renders.\n */\n badgeLabel?: string;\n}\n\n/** A tab with the text-free dot badge (new content without a count). */\ninterface TabScaffoldTabDotBadge {\n /**\n * `true` shows a solid danger dot `Badge` at the top-trailing corner of the\n * tab icon — the iOS tab-bar dot for new content without a count.\n */\n badge: true;\n /**\n * Required localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"New activity\"` →\n * `\"Feed, New activity\"`). A dot has no text, so this is its only meaning.\n */\n badgeLabel: string;\n}\n\n/** One bottom-bar destination: its label, optional icon and badge, and content. */\nexport type TabScaffoldTab<Id extends string = string> =\n TabScaffoldTabBase<Id> & (TabScaffoldTabCountBadge | TabScaffoldTabDotBadge);\n\nexport interface TabScaffoldProps<Id extends string = string> {\n id: string;\n /** Accessible name for the tab bar. */\n label: string;\n tabs: readonly TabScaffoldTab<Id>[];\n /** The controlled active tab id (the app owns selection). */\n active: NoInfer<Id>;\n /**\n * The line over the tab bar, where it meets the active scene. `scroll`\n * (default) shows it only while more of the scene's content lies below —\n * never at the scroll end or when the content fits — once\n * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above\n * the scaffold; unwired, it does not show. `always` shows it without the\n * wiring; `none` never, even when wired. The line is the bar's own 1px top\n * border, so no state moves the bar or a tab.\n */\n chromeDividers?: 'scroll' | 'always' | 'none';\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches\n * between major sections, each tab keeping its own content (usually a `NavStack`)\n * mounted so its stack and scroll survive a switch. Controlled — the app owns\n * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.\n * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of\n * a bottom bar. See `docs/23-app-layouts.md` §3.4.\n */\nexport function TabScaffold<Id extends string>({\n id,\n label,\n tabs,\n active,\n chromeDividers = 'scroll',\n className = '',\n slot,\n}: TabScaffoldProps<Id>) {\n return (\n <section\n class={`kui-tab-scaffold ${className}`.trim()}\n id={id}\n data-component=\"tab-scaffold\"\n data-tab-scaffold-id={id}\n data-chrome-dividers={\n chromeDividers === 'always' || chromeDividers === 'none'\n ? chromeDividers\n : undefined\n }\n slot={slot}\n >\n <div class=\"kui-tab-scaffold__scenes\">\n {tabs.map((tab) => (\n <div\n class=\"kui-tab-scaffold__scene\"\n data-tab-scaffold-scene={tab.id}\n data-active={String(tab.id === active)}\n data-appearance={tab.appearance === 'sunken' ? 'sunken' : undefined}\n aria-hidden={String(tab.id !== active)}\n >\n {tab.content}\n </div>\n ))}\n </div>\n <nav class=\"kui-tab-scaffold__bar\" role=\"tablist\" aria-label={label}>\n {tabs.map((tab) => {\n const dot = tab.badge === true;\n const badgeText = dot ? undefined : normalizeBadge(tab.badge);\n const hasBadge = dot || badgeText !== undefined;\n const badge = hasBadge && (\n <span\n class=\"kui-tab-scaffold__tab-badge\"\n data-badge-kind={dot ? 'dot' : 'text'}\n >\n {badgeText === undefined ? (\n <Badge tone=\"danger\" size=\"dot\" ariaHidden />\n ) : (\n <Badge\n tone=\"danger\"\n appearance=\"solid\"\n size=\"compact\"\n ariaHidden\n >\n {badgeText}\n </Badge>\n )}\n </span>\n );\n // A dot has no text fallback: without a usable badgeLabel (reachable\n // only from untyped callers) the tab keeps its plain accessible name.\n const badgeLabel = hasBadge\n ? tab.badgeLabel?.trim() || badgeText\n : undefined;\n return (\n <button\n type=\"button\"\n class=\"kui-tab-scaffold__tab\"\n role=\"tab\"\n data-tab-scaffold-tab={tab.id}\n aria-selected={String(tab.id === active)}\n tabindex={tab.id === active ? '0' : '-1'}\n aria-label={\n badgeLabel === undefined\n ? undefined\n : `${tab.label}, ${badgeLabel}`\n }\n data-has-badge={hasBadge ? 'true' : undefined}\n >\n {tab.icon ? (\n <span class=\"kui-tab-scaffold__tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n {badge}\n </span>\n ) : (\n badge\n )}\n <span class=\"kui-tab-scaffold__tab-label\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </section>\n );\n}\n\nfunction normalizeBadge(\n badge: string | number | undefined,\n): string | undefined {\n if (typeof badge === 'number')\n return Number.isFinite(badge) ? String(badge) : undefined;\n if (typeof badge === 'string' && badge.trim()) return badge.trim();\n return undefined;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import * as kerfjs from 'kerfjs';
2
2
 
3
- type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small' | 'xsmall';
3
+ type ToolbarTextSize = 'xlarge' | 'xlarge-fixed' | 'large' | 'default' | 'small' | 'xsmall';
4
4
  /** ARIA heading level for a title exposed as a heading landmark. */
5
5
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
6
6
  interface ToolbarTextBaseProps {
@@ -1,4 +1,4 @@
1
- export { ToolbarText } from './chunk-3SXMEK3B.js';
1
+ export { ToolbarText } from './chunk-V3DX3K65.js';
2
2
 
3
3
 
4
4
 
package/dist/toolbar.d.ts CHANGED
@@ -17,8 +17,8 @@ interface ToolbarProps {
17
17
  * then draws its facing side the same way.
18
18
  */
19
19
  dividerSides?: Sides;
20
- /** Horizontal treatment of the center zone. Defaults to centered content. */
21
- centerAlign?: 'center' | 'stretch';
20
+ /** `balanced` gives both side zones equal tracks, centering the middle zone on the toolbar even if only one side has controls. */
21
+ centerAlign?: 'center' | 'stretch' | 'balanced';
22
22
  /**
23
23
  * Component-owned responsive layout; applications choose the policy rather
24
24
  * than restyling toolbar internals. Under every policy the trailing zone
package/dist/toolbar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Toolbar } from './chunk-MERWRTBG.js';
1
+ export { Toolbar } from './chunk-N2DRU35S.js';
2
2
 
3
3
  //# sourceMappingURL=toolbar.js.map
@@ -1,3 +1,3 @@
1
- export { reorderTabs, wireTabBars } from './chunk-C4OYOKWX.js';
1
+ export { reorderTabs, wireTabBars } from './chunk-UA6JW52D.js';
2
2
 
3
3
  //# sourceMappingURL=wire-tab-bars.js.map
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-Y74NWGQW.js';
1
+ export { Workbench } from './chunk-X6ADHJNI.js';
2
2
 
3
3
 
4
4
 
@@ -80,9 +80,10 @@ and that of the components it renders (`Pane`, `Toolbar`, `ToolbarControlGroup`,
80
80
  corners, with typed `restoreInset` for the corner distance. Set
81
81
  `restorePlacement="inline"` when the container should place the control in
82
82
  normal flow. An inline region's surface uses
83
- `--kui-resizable-region-background`; set `contentOverflow="visible"` while
84
- an anchored popup must escape the region's content box, then return to the
85
- normal overflow policy when it closes.
83
+ `--kui-resizable-region-background`; an open descendant `PopupMenu` or Web
84
+ Awesome dropdown releases clipping and raises the popup layer for its
85
+ lifecycle. Set `contentOverflow="visible"` for other anchored content that
86
+ must escape the region's content box.
86
87
  Overlay presentation also clamps fixed-size animated content to the configured
87
88
  responsive overlay maximum, so a remembered desktop size cannot escape a narrow
88
89
  viewport.
@@ -176,10 +176,16 @@ an upstream component or recipe request.
176
176
 
177
177
  ## Ambiguous choices
178
178
 
179
+ For a lowered background behind an entire scrolling work area, choose
180
+ `Pane appearance="sunken"` or the appearance option on the layout that owns
181
+ the scroll viewport. Use `SunkenPanel` for a padded content group that does
182
+ not introduce scrolling. See [Lowered work surfaces](./layout.md#lowered-work-surfaces).
183
+
179
184
  Set `ResizableRegion`'s `--kui-resizable-region-background` for an inline
180
- surface. Use `contentOverflow="visible"` while an anchored popup must escape
181
- the region; this also raises the region on its public popup layer. A collapsed
182
- region can place `restoreControl` in a top or bottom corner with
185
+ surface. An open descendant `PopupMenu` or Web Awesome dropdown automatically
186
+ releases clipping and raises the region on its public popup layer. Set
187
+ `contentOverflow="visible"` for other anchored content that must escape the
188
+ region. A collapsed region can place `restoreControl` in a top or bottom corner with
183
189
  `restorePosition`, set its typed `restoreInset`, or use
184
190
  `restorePlacement="inline"` for a control in normal flow.
185
191
 
@@ -212,7 +218,9 @@ name. Disabled and loading links omit `href` and leave the tab order. Use
212
218
  Use `Toolbar responsive="trailing-priority"` for a collapsible search in the
213
219
  trailing zone that should take a full second row when expanded. `responsiveAt`
214
220
  chooses the compact or narrow container breakpoint. The leading identity stays
215
- in the first row. `--kui-toolbar-leading-min-width`,
221
+ in the first row. Set `centerAlign="balanced"` when the center zone must sit on
222
+ the toolbar's midpoint despite unequal or empty side content; it gives the
223
+ leading and trailing zones equal tracks. `--kui-toolbar-leading-min-width`,
216
224
  `--kui-toolbar-trailing-gap`, `--kui-toolbar-trailing-justify`, and the
217
225
  `--kui-toolbar-*-padding-inline` zone tokens adjust local geometry without
218
226
  selecting toolbar anatomy from application CSS.
@@ -225,8 +233,10 @@ For compact dialog copy, use `Text flush lineHeight="tight"`; block Text still
225
233
  resets native margins. `ValueTable density="compact"` reduces row padding and
226
234
  gap. Its `--kui-value-table-row-columns`, `--kui-value-table-row-padding-block`,
227
235
  and `--kui-value-table-row-gap` tokens support a specific metadata layout.
228
- Use `ToolbarText size="xsmall"` for a dense rail heading. A
229
- `TokenSearchField` editor can shrink to zero minimum width inside a narrow
236
+ Use `ToolbarText size="xsmall"` for a dense rail heading. Use
237
+ `size="xlarge-fixed"` for a toolbar heading at the fixed 20px step; `xlarge`
238
+ retains the responsive page-title clamp. Pass `headingLevel` when the text is
239
+ a section landmark. A `TokenSearchField` editor can shrink to zero minimum width inside a narrow
230
240
  group; set `--kui-token-search-editor-min-width` only when a wider editor is
231
241
  required.
232
242
 
@@ -234,7 +244,11 @@ required.
234
244
  - `TabBar` changes tabpanels and supports overflow/reorder; `SegmentedControl` chooses among a few compact views; `Select` handles a longer value list.
235
245
 
236
246
  Use `AppTab presentation="icon-only"` with its `name` kept as the accessible
237
- tab name, and `labelMaxWidth` for an ellipsized visible name. Set `attention`
247
+ tab name. For segmented tabs that should show names in a wide reader and icons
248
+ in a narrower container, set `TabBar iconOnlyAt="wide"` (832px), `"narrow"`
249
+ (704px), or `"compact"` (448px), and give each segmented `AppTab` a leading
250
+ icon. The tablist's own width controls the switch; the visually hidden name
251
+ remains its accessible name. Use `labelMaxWidth` for an ellipsized visible name. Set `attention`
238
252
  to color a tab's name with `--kui-app-tab-attention-color`. Set `dropTarget`
239
253
  while an app drag is over a tab's content; the tab keeps its selection semantics
240
254
  and uses the `--kui-app-tab-drop-target-*` tokens for its highlight. Set
@@ -246,7 +260,9 @@ its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
246
260
  Set `pinned` on the first `AppTab` when that tab must remain visible as peers
247
261
  scroll. It stays inside the tablist and keyboard order. Set the public
248
262
  `--kui-app-tab-pinned-background` token if the surrounding surface differs from
249
- the default; the pinned tab must cover peers as they scroll beneath it.
263
+ the default; the pinned tab must cover peers as they scroll beneath it. Call
264
+ `wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
265
+ scroll correction.
250
266
 
251
267
  - `StateBanner` persists beside affected work; `EmptyState` replaces absent content; `wa-callout` is contextual ecosystem content; `wa-toast` and `wa-toast-item` are transient and must not carry the only copy of important state.
252
268
 
@@ -258,6 +274,9 @@ stacked copy's spacing at an instance boundary.
258
274
 
259
275
  For a compact `Select`, set `triggerWidth` to `"fit-content"`, `"max-content"`,
260
276
  or `"fill"` and cap it with `--kui-select-trigger-max-width` when needed.
277
+ For a round icon-only toolbar trigger, set `selectedPresentation="icon-only"`
278
+ and `caret={false}`. The control keeps its accessible name, current choice help
279
+ tag, and keyboard-operated listbox; the default keeps the visible caret.
261
280
  Custom `renderSelected` text inherits the control color by default; the
262
281
  `--kui-select-selected-color`, `--kui-select-selected-font-size`, and
263
282
  `--kui-select-selected-font-weight` tokens configure its typography. Plain
package/docs/layout.md CHANGED
@@ -36,6 +36,37 @@ size) tall, and below that lets the whole pane scroll as one column, header,
36
36
  content, and footer together, so the content keeps its natural height. The
37
37
  default `fixed` always pins them.
38
38
 
39
+ ### Lowered work surfaces
40
+
41
+ Set `appearance="sunken"` when the work area itself should use the lowered
42
+ surface color, including blank space below short content and the scroll
43
+ viewport. The default appearance is unchanged. The fixed Pane paints its
44
+ content scroller; with `chromePlacement="auto"`, the Pane root paints the
45
+ surface because it becomes the scroller in a short viewport. Header and footer
46
+ chrome stay on the normal surface. No second scroll container or
47
+ `SunkenPanel` wrapper is needed.
48
+ For custom chrome colors in auto mode, set `--kui-pane-chrome-background`
49
+ on the Pane; it defaults to `--kui-color-surface`.
50
+
51
+ ```tsx
52
+ <Pane appearance="sunken" footer={<Composer />}>
53
+ <Conversation />
54
+ </Pane>
55
+ ```
56
+
57
+ `PaneConfig.appearance` forwards through Workbench `mainPane`, panel `pane`,
58
+ and CollapsiblePanel `pane`. Workbench also applies that choice to its main or
59
+ panel region when it renders no Pane chrome. Set `appearance: "sunken"` on a
60
+ `NavStackView` or `TabScaffoldTab` when its view or scene owns scrolling.
61
+ For a `SplitView`, put a `Pane` in the list or detail region and set its
62
+ appearance; SplitView only arranges the regions and does not own their scroll.
63
+ Choose the innermost owner when composing layouts.
64
+
65
+ The color uses `--kui-sunken-panel-background`, falling back to
66
+ `--kui-color-surface-lowered`, just like `SunkenPanel`. Select one scroll owner
67
+ in a nested layout so a translucent override paints once. The public color
68
+ override may be set at a Pane or layout boundary.
69
+
39
70
  Pass any combination of logical sides to `separators`: `block-start`,
40
71
  `block-end`, `inline-start`, and `inline-end`. Every line is off by default and
41
72
  each enabled side uses `--kui-pane-separator-width` (1px) and
package/docs/nav-stack.md CHANGED
@@ -60,6 +60,11 @@ optional per-view `bottomToolbar`. The component-level
60
60
  The back control appears automatically once the stack has more than one entry;
61
61
  `wireNavStack`'s `onBack` is where the app pops its own signal.
62
62
 
63
+ Set a view's `appearance: "sunken"` to paint its scroll viewport with the
64
+ shared lowered-surface color, including space after short content. When the
65
+ view holds a `Pane` as its only child, set the appearance on the Pane instead
66
+ if its content owns scrolling. See [Lowered work surfaces](layout.md#lowered-work-surfaces).
67
+
63
68
  ## The top toolbar
64
69
 
65
70
  The top chrome is a real `Toolbar`, so it follows the same zone,
@@ -26,6 +26,11 @@ shared interior edge. A pane whose only child is a `Pane` lets that child own
26
26
  the insets, and the compact form hands them to its `NavStack`. See
27
27
  [Choosing an app layout › Safe areas](app-layouts.md#safe-areas).
28
28
 
29
+ For a lowered list or detail background, render a `Pane` in that region with
30
+ `appearance="sunken"`. SplitView arranges regions but does not own their
31
+ vertical scroll; its compact NavStack form can use the view's or nested Pane's
32
+ appearance. See [Lowered work surfaces](layout.md#lowered-work-surfaces).
33
+
29
34
  ## Responsive by device class
30
35
 
31
36
  `SplitView` is declarative; the app derives `compact` from the device class and