@kerfjs/ui 5.0.0-beta.50 → 5.0.0-beta.51

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 (205) hide show
  1. package/README.md +29 -5
  2. package/ai/compile-time-contracts-v1.json +5 -2
  3. package/ai/component-catalog-v2.d.ts +19 -1
  4. package/ai/component-catalog-v2.json +842 -127
  5. package/ai/component-catalog-v2.schema.json +33 -1
  6. package/ai/component-catalog.json +603 -126
  7. package/ai/component-catalog.schema.json +21 -1
  8. package/ai/public-api-signatures-v1.md +201 -29
  9. package/ai/skill.md +12 -9
  10. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  11. package/analyzer/index.mjs +105 -35
  12. package/dist/app-tab.js +1 -1
  13. package/dist/catalog-resources.d.ts +1 -1
  14. package/dist/catalog.d.ts +28 -5
  15. package/dist/catalog.js +66 -25
  16. package/dist/catalog.js.map +1 -1
  17. package/dist/{chunk-COUPLRZG.js → chunk-5FZZMIMD.js} +3 -3
  18. package/dist/{chunk-COUPLRZG.js.map → chunk-5FZZMIMD.js.map} +1 -1
  19. package/dist/{chunk-DARTMI5Z.js → chunk-5PMGQDG7.js} +2 -2
  20. package/dist/{chunk-DARTMI5Z.js.map → chunk-5PMGQDG7.js.map} +1 -1
  21. package/dist/{chunk-V6RZWDII.js → chunk-7GUQYHKA.js} +19 -2
  22. package/dist/chunk-7GUQYHKA.js.map +1 -0
  23. package/dist/{chunk-ZNZ2JDBW.js → chunk-7IUSV4XG.js} +1 -1
  24. package/dist/chunk-7IUSV4XG.js.map +1 -0
  25. package/dist/{chunk-ELRMBQEC.js → chunk-AEPXGCNF.js} +17 -2
  26. package/dist/chunk-AEPXGCNF.js.map +1 -0
  27. package/dist/{chunk-5CE64WGC.js → chunk-D547PH4L.js} +3 -3
  28. package/dist/{chunk-5CE64WGC.js.map → chunk-D547PH4L.js.map} +1 -1
  29. package/dist/{chunk-S6ZTTTV7.js → chunk-DVZCMMB3.js} +3 -3
  30. package/dist/{chunk-S6ZTTTV7.js.map → chunk-DVZCMMB3.js.map} +1 -1
  31. package/dist/chunk-EKYZCRCX.js +256 -0
  32. package/dist/chunk-EKYZCRCX.js.map +1 -0
  33. package/dist/{chunk-3V75EQIP.js → chunk-JM53SMVF.js} +3 -3
  34. package/dist/{chunk-3V75EQIP.js.map → chunk-JM53SMVF.js.map} +1 -1
  35. package/dist/{chunk-2WMSE4MK.js → chunk-JMGKM7RK.js} +2 -2
  36. package/dist/{chunk-2WMSE4MK.js.map → chunk-JMGKM7RK.js.map} +1 -1
  37. package/dist/{chunk-2YOUUEWR.js → chunk-KX7DP2GO.js} +4 -4
  38. package/dist/{chunk-2YOUUEWR.js.map → chunk-KX7DP2GO.js.map} +1 -1
  39. package/dist/{chunk-OL4TC3U2.js → chunk-MO56ZZLG.js} +6 -2
  40. package/dist/chunk-MO56ZZLG.js.map +1 -0
  41. package/dist/{chunk-OQI7RCRP.js → chunk-MTM43SJO.js} +7 -1
  42. package/dist/chunk-MTM43SJO.js.map +1 -0
  43. package/dist/{chunk-ZSYLBVWG.js → chunk-NMGTHYW3.js} +3 -3
  44. package/dist/{chunk-ZSYLBVWG.js.map → chunk-NMGTHYW3.js.map} +1 -1
  45. package/dist/{chunk-AQSKJ6U6.js → chunk-NZXWU4R5.js} +21 -2
  46. package/dist/chunk-NZXWU4R5.js.map +1 -0
  47. package/dist/{chunk-DJKWFYAA.js → chunk-O3RCCSQJ.js} +3 -3
  48. package/dist/{chunk-DJKWFYAA.js.map → chunk-O3RCCSQJ.js.map} +1 -1
  49. package/dist/{chunk-UMGY5JSP.js → chunk-S4QRSBFI.js} +22 -2
  50. package/dist/chunk-S4QRSBFI.js.map +1 -0
  51. package/dist/{chunk-KKNK3JUT.js → chunk-UBH743HV.js} +5 -5
  52. package/dist/chunk-UBH743HV.js.map +1 -0
  53. package/dist/{chunk-54HM36IQ.js → chunk-ULCDZTWL.js} +2 -2
  54. package/dist/{chunk-54HM36IQ.js.map → chunk-ULCDZTWL.js.map} +1 -1
  55. package/dist/{chunk-SIU2AC5W.js → chunk-UPZ6EEJN.js} +3 -3
  56. package/dist/{chunk-SIU2AC5W.js.map → chunk-UPZ6EEJN.js.map} +1 -1
  57. package/dist/collapsible-panel.d.ts +1 -1
  58. package/dist/collapsible-panel.js.map +1 -1
  59. package/dist/css-values.d.ts +39 -3
  60. package/dist/css-values.js +1 -1
  61. package/dist/grid.d.ts +15 -1
  62. package/dist/grid.js +2 -1
  63. package/dist/index.d.ts +1 -1
  64. package/dist/index.js +19 -19
  65. package/dist/list-action-row.js +1 -1
  66. package/dist/list-header.js +1 -1
  67. package/dist/list-item.js +1 -1
  68. package/dist/list.d.ts +21 -2
  69. package/dist/list.js +2 -1
  70. package/dist/nav-stack.js +1 -1
  71. package/dist/pane.d.ts +17 -2
  72. package/dist/pane.js +1 -1
  73. package/dist/row.d.ts +19 -1
  74. package/dist/row.js +2 -1
  75. package/dist/segmented-control.js +1 -1
  76. package/dist/select.d.ts +13 -3
  77. package/dist/select.js +1 -1
  78. package/dist/skeleton.js +1 -1
  79. package/dist/spacer.js +1 -1
  80. package/dist/split-view.js +1 -1
  81. package/dist/state-banner.js +1 -1
  82. package/dist/styles/catalog/components/catalog-example-stack.css +4 -1
  83. package/dist/styles/catalog/components/catalog-example.css +145 -0
  84. package/dist/styles/collapsible-panel.css +132 -4
  85. package/dist/styles/document.css +23 -0
  86. package/dist/styles/foundation.css +21 -7
  87. package/dist/styles/grid.css +10 -0
  88. package/dist/styles/list-header.css +45 -0
  89. package/dist/styles/list-inset-control.css +8 -1
  90. package/dist/styles/list-item.css +6 -0
  91. package/dist/styles/list.css +11 -0
  92. package/dist/styles/nav-stack.css +89 -2
  93. package/dist/styles/pane.css +86 -1
  94. package/dist/styles/resizable-region.css +93 -5
  95. package/dist/styles/row.css +10 -0
  96. package/dist/styles/select.css +39 -4
  97. package/dist/styles/split-view.css +107 -1
  98. package/dist/styles/tab-scaffold.css +64 -1
  99. package/dist/styles/text.css +6 -0
  100. package/dist/styles/toolbar-control-group.css +90 -11
  101. package/dist/styles/toolbar-text.css +4 -1
  102. package/dist/styles/toolbar.css +130 -38
  103. package/dist/styles/webawesome.css +48 -2
  104. package/dist/styles/workbench.css +136 -4
  105. package/dist/text.d.ts +4 -3
  106. package/dist/text.js +1 -1
  107. package/dist/toolbar-text.js +1 -1
  108. package/dist/toolbar.d.ts +24 -3
  109. package/dist/toolbar.js +1 -1
  110. package/dist/{types-CzgSrFXP.d.ts → types-CEsieBZi.d.ts} +6 -1
  111. package/dist/value-table.js +1 -1
  112. package/dist/wire-resizable-regions.js +1 -1
  113. package/dist/wire-sidebar.d.ts +18 -3
  114. package/dist/wire-sidebar.js +95 -14
  115. package/dist/wire-sidebar.js.map +1 -1
  116. package/docs/accessibility.md +45 -8
  117. package/docs/app-layouts.md +73 -0
  118. package/docs/catalog.md +23 -2
  119. package/docs/collapsible-panel.md +81 -8
  120. package/docs/component-contract.md +28 -9
  121. package/docs/component-integration.md +9 -4
  122. package/docs/component-selection.md +25 -6
  123. package/docs/design/templates/list/stack-dark.svg +1 -1
  124. package/docs/design/templates/list/stack.svg +1 -1
  125. package/docs/design/templates/list-dark.svg +1 -1
  126. package/docs/design/templates/list-header/action-dark.svg +1 -1
  127. package/docs/design/templates/list-header/action.svg +1 -1
  128. package/docs/design/templates/list-header/count-dark.svg +1 -1
  129. package/docs/design/templates/list-header/count.svg +1 -1
  130. package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
  131. package/docs/design/templates/list-header/inline-action.svg +1 -1
  132. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  133. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  134. package/docs/design/templates/list-header-dark.svg +1 -1
  135. package/docs/design/templates/list-header.svg +1 -1
  136. package/docs/design/templates/list.svg +1 -1
  137. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  138. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  139. package/docs/design/templates/state-banner-dark.svg +1 -1
  140. package/docs/design/templates/state-banner.svg +1 -1
  141. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  142. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  143. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  144. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  145. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  146. package/docs/design/templates/toolbar-control-group.svg +1 -1
  147. package/docs/design/templates/toolbar-dark.svg +1 -1
  148. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  149. package/docs/design/templates/toolbar-text/default.svg +1 -1
  150. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  151. package/docs/design/templates/toolbar-text/large.svg +1 -1
  152. package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
  153. package/docs/design/templates/toolbar-text/small.svg +1 -1
  154. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  155. package/docs/design/templates/toolbar-text.svg +1 -1
  156. package/docs/design/templates/toolbar.svg +1 -1
  157. package/docs/document-baseline.md +31 -0
  158. package/docs/layout.md +86 -21
  159. package/docs/nav-stack.md +8 -0
  160. package/docs/recipes.md +78 -43
  161. package/docs/split-view.md +8 -0
  162. package/docs/tab-scaffold.md +4 -2
  163. package/docs/type-contracts.md +21 -17
  164. package/docs/ui-analyzer.md +19 -0
  165. package/docs/ui-evaluator.md +17 -0
  166. package/docs/ux-demo.md +4 -4
  167. package/docs/webawesome-theme.md +20 -2
  168. package/docs/workbench.md +20 -1
  169. package/evaluator/index.mjs +73 -8
  170. package/llms.txt +32 -14
  171. package/package.json +4 -2
  172. package/ux-demo/recipes/app-shell.tsx +209 -178
  173. package/ux-demo/recipes/collapsible-sidebar.tsx +188 -159
  174. package/ux-demo/recipes/compact-toolbar.tsx +48 -35
  175. package/ux-demo/recipes/composer-form.tsx +85 -79
  176. package/ux-demo/recipes/list-detail-dialog.tsx +124 -142
  177. package/ux-demo/recipes/list-workspace-states.tsx +83 -83
  178. package/ux-demo/recipes/loaders.ts +5 -2
  179. package/ux-demo/recipes/loading-inspector.tsx +157 -160
  180. package/ux-demo/recipes/navigation-sidebar.tsx +111 -109
  181. package/ux-demo/recipes/navigation-stack.tsx +50 -49
  182. package/ux-demo/recipes/types.ts +14 -0
  183. package/ux-demo/recipes/workspace-header.tsx +73 -56
  184. package/dist/chunk-AQSKJ6U6.js.map +0 -1
  185. package/dist/chunk-ELRMBQEC.js.map +0 -1
  186. package/dist/chunk-J3EE6XWD.js +0 -115
  187. package/dist/chunk-J3EE6XWD.js.map +0 -1
  188. package/dist/chunk-KKNK3JUT.js.map +0 -1
  189. package/dist/chunk-OL4TC3U2.js.map +0 -1
  190. package/dist/chunk-OQI7RCRP.js.map +0 -1
  191. package/dist/chunk-UMGY5JSP.js.map +0 -1
  192. package/dist/chunk-V6RZWDII.js.map +0 -1
  193. package/dist/chunk-ZNZ2JDBW.js.map +0 -1
  194. package/ux-demo/recipes/app-shell.css +0 -87
  195. package/ux-demo/recipes/collapsible-sidebar.css +0 -83
  196. package/ux-demo/recipes/compact-toolbar.css +0 -35
  197. package/ux-demo/recipes/composer-form.css +0 -19
  198. package/ux-demo/recipes/list-detail-dialog.css +0 -42
  199. package/ux-demo/recipes/list-workspace-states.css +0 -30
  200. package/ux-demo/recipes/loading-inspector.css +0 -7
  201. package/ux-demo/recipes/navigation-sidebar.css +0 -12
  202. package/ux-demo/recipes/navigation-stack.css +0 -12
  203. package/ux-demo/recipes/recipe-root.css +0 -67
  204. package/ux-demo/recipes/recipe-root.tsx +0 -87
  205. package/ux-demo/recipes/workspace-header.css +0 -7
@@ -21,6 +21,151 @@
21
21
  font-size: var(--kui-font-xs);
22
22
  }
23
23
 
24
+ .kui-catalog-example__viewport {
25
+ position: relative;
26
+ max-width: 100%;
27
+ box-sizing: border-box;
28
+ }
29
+
30
+ .kui-catalog-example__viewport > * {
31
+ max-width: 100%;
32
+ box-sizing: border-box;
33
+ }
34
+
35
+ .kui-catalog-example__viewport[data-layout="grid"] {
36
+ display: grid;
37
+ }
38
+
39
+ .kui-catalog-example__viewport[data-layout="flex"] {
40
+ display: flex;
41
+ }
42
+
43
+ .kui-catalog-example__viewport[data-layout="flex-column"] {
44
+ display: flex;
45
+ flex-direction: column;
46
+ }
47
+
48
+ .kui-catalog-example__viewport[data-width="full"] {
49
+ width: 100%;
50
+ }
51
+
52
+ .kui-catalog-example__viewport[data-width="compact"] {
53
+ width: min(100%, 27.5rem);
54
+ }
55
+
56
+ .kui-catalog-example__viewport[data-width="medium"] {
57
+ width: min(100%, 40rem);
58
+ }
59
+
60
+ .kui-catalog-example__viewport[data-width="wide"] {
61
+ width: 100%;
62
+ max-width: 54rem;
63
+ }
64
+
65
+ .kui-catalog-example__viewport[data-width="text"] {
66
+ width: 17.5rem;
67
+ }
68
+
69
+ .kui-catalog-example__viewport[data-width="control"] {
70
+ min-width: min(24rem, 100%);
71
+ }
72
+
73
+ .kui-catalog-example__viewport[data-height="short"] {
74
+ height: 13.75rem;
75
+ }
76
+
77
+ .kui-catalog-example__viewport[data-height="reduced"] {
78
+ height: 17.5rem;
79
+ }
80
+
81
+ .kui-catalog-example__viewport[data-height="medium"] {
82
+ height: 20rem;
83
+ }
84
+
85
+ .kui-catalog-example__viewport[data-height="tall"] {
86
+ height: 22.5rem;
87
+ }
88
+
89
+ /* An application-sized frame for app shells and whole-screen layouts, which
90
+ read cramped in the component-sized heights above. The frame stands in for
91
+ the screen, so it is the containing block for screen-fixed chrome: a compact
92
+ CollapsiblePanel overlay, its backdrop, and restore controls dock to the
93
+ frame's edges instead of the page's, where the frame's inset and clip would
94
+ cut them off. An identity transform establishes that containing block.
95
+ `contain: layout` does too, but WebKit then mis-hit-tests any transformed
96
+ descendant of that fixed chrome once the frame also clips (every pressed
97
+ ListItem scales), so taps on an open overlay's items were lost. */
98
+ .kui-catalog-example__viewport[data-height="app"] {
99
+ height: 37rem;
100
+ transform: translate(0);
101
+ }
102
+
103
+ .kui-catalog-example__viewport[data-min-height="short"] {
104
+ min-height: 16rem;
105
+ }
106
+
107
+ .kui-catalog-example__viewport[data-min-height="medium"] {
108
+ min-height: 20rem;
109
+ }
110
+
111
+ .kui-catalog-example__viewport[data-frame] {
112
+ border: 1px solid var(--kui-color-border);
113
+ border-radius: var(--kui-radius-m);
114
+ }
115
+
116
+ .kui-catalog-example__viewport[data-frame="dashed"] {
117
+ border-style: dashed;
118
+ }
119
+
120
+ .kui-catalog-example__viewport[data-surface="default"] {
121
+ background: var(--kui-color-surface);
122
+ }
123
+
124
+ .kui-catalog-example__viewport[data-surface="lowered"] {
125
+ background: var(--kui-color-surface-lowered);
126
+ }
127
+
128
+ .kui-catalog-example__viewport[data-overflow="hidden"] {
129
+ overflow: hidden;
130
+ }
131
+
132
+ .kui-catalog-example__viewport[data-overflow="auto-x"] {
133
+ overflow-x: auto;
134
+ }
135
+
136
+ .kui-catalog-example__viewport[data-shadow="true"] {
137
+ box-shadow: var(--kui-shadow-s);
138
+ }
139
+
140
+ .kui-catalog-example__compact-fallback {
141
+ display: none;
142
+ margin: 0;
143
+ color: var(--kui-color-text-quiet);
144
+ font-size: var(--kui-font-s);
145
+ }
146
+
147
+ @media ((max-width: 52rem)) {
148
+ .kui-catalog-example__viewport[data-responsive="roomy-only"] {
149
+ display: none;
150
+ }
151
+
152
+ .kui-catalog-example__compact-fallback {
153
+ display: block;
154
+ }
155
+ }
156
+
157
+ @media ((max-width: 30rem)) {
158
+ .kui-catalog-example__viewport[data-min-height="medium"] {
159
+ min-height: 16rem;
160
+ }
161
+ }
162
+
163
+ @media ((forced-colors: active)) {
164
+ .kui-catalog-example__viewport[data-frame] {
165
+ forced-color-adjust: auto;
166
+ }
167
+ }
168
+
24
169
  /* The published inset lets the geometry overlay exclude this authoring aid. */
25
170
  .kui-catalog-example[data-align="glyph"]
26
171
  > :not([data-catalog-example-label]):not([data-catalog-example-note]) {
@@ -2,34 +2,158 @@
2
2
  to zero (one reflow, no per-frame width/height animation) while the fixed-size
3
3
  content slides out via transform (composited, clipped by overflow). The same
4
4
  technique as Workbench and the catalog sidebar (docs/18). */
5
+ /* Safe areas: the panel docks to one screen edge, so its surface grows by
6
+ that edge's unsafe inset and its content compensates for every edge the
7
+ panel still touches, never its interior edge. An expanded inline panel also
8
+ takes its edge away from its direct siblings; collapsing it hands the edge
9
+ back. A panel wrapped in an app-owned grid cell cannot see its siblings, so
10
+ that app routes `--kui-edge-inset-*` itself (docs/app-layouts.md). */
5
11
  .kui-collapsible-panel {
12
+ --_kui-collapsible-panel-safe-block-start: var(
13
+ --kui-edge-inset-block-start,
14
+ var(--kui-safe-area-block-start, env(safe-area-inset-top, 0px))
15
+ );
16
+ --_kui-collapsible-panel-safe-block-end: var(
17
+ --kui-edge-inset-block-end,
18
+ var(--kui-safe-area-block-end, env(safe-area-inset-bottom, 0px))
19
+ );
20
+ --_kui-collapsible-panel-safe-inline-start: var(
21
+ --kui-edge-inset-inline-start,
22
+ var(--kui-safe-area-inline-start, env(safe-area-inset-left, 0px))
23
+ );
24
+ --_kui-collapsible-panel-safe-inline-end: var(
25
+ --kui-edge-inset-inline-end,
26
+ var(--kui-safe-area-inline-end, env(safe-area-inset-right, 0px))
27
+ );
6
28
  overflow: hidden;
7
29
  background: var(--kui-color-surface);
8
30
  }
9
31
 
10
32
  .kui-collapsible-panel--left,
11
33
  .kui-collapsible-panel--right {
12
- width: var(--kui-collapsible-panel-width, 17.5rem);
34
+ width: var(--_kui-collapsible-panel-extent);
13
35
  min-width: 0;
14
36
  height: 100%;
15
37
  }
16
38
 
17
39
  .kui-collapsible-panel--left {
40
+ --_kui-collapsible-panel-extent: calc(
41
+ var(--kui-collapsible-panel-width, 17.5rem) +
42
+ var(--_kui-collapsible-panel-safe-inline-start)
43
+ );
18
44
  border-inline-end: 1px solid var(--kui-color-border);
19
45
  }
20
46
 
21
47
  .kui-collapsible-panel--right {
48
+ --_kui-collapsible-panel-extent: calc(
49
+ var(--kui-collapsible-panel-width, 17.5rem) +
50
+ var(--_kui-collapsible-panel-safe-inline-end)
51
+ );
22
52
  border-inline-start: 1px solid var(--kui-color-border);
23
53
  }
24
54
 
25
55
  .kui-collapsible-panel--bottom {
56
+ --_kui-collapsible-panel-extent: calc(
57
+ var(--kui-collapsible-panel-height, 13.75rem) +
58
+ var(--_kui-collapsible-panel-safe-block-end)
59
+ );
26
60
  position: relative;
27
61
  width: 100%;
28
- height: var(--kui-collapsible-panel-height, 13.75rem);
62
+ height: var(--_kui-collapsible-panel-extent);
29
63
  min-height: 0;
30
64
  border-block-start: 1px solid var(--kui-color-border);
31
65
  }
32
66
 
67
+ /* The panel's content sees the edges it touches; its interior edge is clear. */
68
+ .kui-collapsible-panel > .kui-collapsible-panel__content {
69
+ --kui-edge-inset-block-start: var(--_kui-collapsible-panel-safe-block-start);
70
+ --kui-edge-inset-block-end: var(--_kui-collapsible-panel-safe-block-end);
71
+ --kui-edge-inset-inline-start: var(
72
+ --_kui-collapsible-panel-safe-inline-start
73
+ );
74
+ --kui-edge-inset-inline-end: var(--_kui-collapsible-panel-safe-inline-end);
75
+ }
76
+
77
+ .kui-collapsible-panel--left > .kui-collapsible-panel__content {
78
+ --kui-edge-inset-inline-end: 0px;
79
+ }
80
+
81
+ .kui-collapsible-panel--right > .kui-collapsible-panel__content {
82
+ --kui-edge-inset-inline-start: 0px;
83
+ }
84
+
85
+ .kui-collapsible-panel--bottom > .kui-collapsible-panel__content {
86
+ --kui-edge-inset-block-start: 0px;
87
+ }
88
+
89
+ /* Content whose only child is a Pane or layout lets that child own the insets;
90
+ anything else is padded here and sees a cleared context. */
91
+ .kui-collapsible-panel__content:not(
92
+ :has(
93
+ > :is(
94
+ [data-component="pane"],
95
+ [data-component="nav-stack"],
96
+ [data-component="split-view"],
97
+ [data-component="workbench"],
98
+ [data-component="tab-scaffold"]
99
+ ):only-child
100
+ )
101
+ ) {
102
+ padding-block: var(--kui-edge-inset-block-start)
103
+ var(--kui-edge-inset-block-end);
104
+ padding-inline: var(--kui-edge-inset-inline-start)
105
+ var(--kui-edge-inset-inline-end);
106
+ }
107
+
108
+ .kui-collapsible-panel__content:not(
109
+ :has(
110
+ > :is(
111
+ [data-component="pane"],
112
+ [data-component="nav-stack"],
113
+ [data-component="split-view"],
114
+ [data-component="workbench"],
115
+ [data-component="tab-scaffold"]
116
+ ):only-child
117
+ )
118
+ )
119
+ > * {
120
+ --kui-edge-inset-block-start: 0px;
121
+ --kui-edge-inset-block-end: 0px;
122
+ --kui-edge-inset-inline-start: 0px;
123
+ --kui-edge-inset-inline-end: 0px;
124
+ }
125
+
126
+ /* An expanded inline panel is the region at its edge, so its direct siblings
127
+ no longer reach that edge. */
128
+ .kui-collapsible-panel--left[data-presentation="inline"]:not(
129
+ [data-collapsed="true"],
130
+ [data-collapsible-overlay="true"] *,
131
+ [data-collapsible-responsive="hidden"] *
132
+ )
133
+ ~ * {
134
+ --kui-edge-inset-inline-start: 0px;
135
+ }
136
+
137
+ :has(
138
+ ~ .kui-collapsible-panel--right[data-presentation="inline"]:not(
139
+ [data-collapsed="true"],
140
+ [data-collapsible-overlay="true"] *,
141
+ [data-collapsible-responsive="hidden"] *
142
+ )
143
+ ) {
144
+ --kui-edge-inset-inline-end: 0px;
145
+ }
146
+
147
+ :has(
148
+ ~ .kui-collapsible-panel--bottom[data-presentation="inline"]:not(
149
+ [data-collapsed="true"],
150
+ [data-collapsible-overlay="true"] *,
151
+ [data-collapsible-responsive="hidden"] *
152
+ )
153
+ ) {
154
+ --kui-edge-inset-block-end: 0px;
155
+ }
156
+
33
157
  .kui-collapsible-panel[data-collapsed="true"] {
34
158
  border: 0;
35
159
  }
@@ -65,7 +189,7 @@
65
189
 
66
190
  .kui-collapsible-panel--left .kui-collapsible-panel__content,
67
191
  .kui-collapsible-panel--right .kui-collapsible-panel__content {
68
- width: var(--kui-collapsible-panel-width, 17.5rem);
192
+ width: var(--_kui-collapsible-panel-extent);
69
193
  }
70
194
 
71
195
  .kui-collapsible-panel--bottom .kui-collapsible-panel__content {
@@ -73,7 +197,7 @@
73
197
  inset-inline: 0;
74
198
  inset-block-end: 0;
75
199
  width: 100%;
76
- height: var(--kui-collapsible-panel-height, 13.75rem);
200
+ height: var(--_kui-collapsible-panel-extent);
77
201
  }
78
202
 
79
203
  .kui-collapsible-panel[data-collapse-motion="none"]
@@ -164,9 +288,13 @@
164
288
  );
165
289
  }
166
290
 
291
+ /* A button does not stretch between its insets the way a div does, so the
292
+ backdrop sizes itself to its containing block explicitly. */
167
293
  .kui-collapsible-panel__backdrop {
168
294
  position: fixed;
169
295
  inset: 0;
296
+ width: 100%;
297
+ height: 100%;
170
298
  z-index: var(--kui-collapsible-panel-backdrop-z, 39);
171
299
  border: 0;
172
300
  padding: 0;
@@ -0,0 +1,23 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ html,
8
+ body,
9
+ .kui-app-root {
10
+ height: 100%;
11
+ }
12
+
13
+ body {
14
+ margin: 0;
15
+ color: var(--kui-color-text);
16
+ background: var(--kui-color-surface-lowered);
17
+ font-family: var(--kui-font-sans);
18
+ line-height: 1.45;
19
+ }
20
+
21
+ :where(a) {
22
+ color: var(--kui-color-text-link);
23
+ }
@@ -10,13 +10,13 @@
10
10
  "Segoe UI",
11
11
  sans-serif
12
12
  );
13
- --kui-font-mono: var(
14
- --wa-font-family-code,
15
- ui-monospace,
16
- SFMono-Regular,
17
- Menlo,
18
- monospace
19
- );
13
+ /* Kerf owns the code stack rather than deferring to Web Awesome's
14
+ `ui-monospace, monospace`: Chromium does not recognize `ui-monospace`, and
15
+ the generic `monospace` ends any stack, so named fallbacks after it would
16
+ never apply. Kerf's Web Awesome theme points `--wa-font-family-code` here. */
17
+ --kui-font-mono:
18
+ ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
19
+ monospace;
20
20
  --kui-font-2xs: var(--wa-font-size-2xs, 11px);
21
21
  --kui-font-xs: var(--wa-font-size-xs, 12px);
22
22
  --kui-font-s: var(--wa-font-size-s, 14px);
@@ -64,6 +64,15 @@
64
64
  --kui-toolbar-group-inner-size: 2.625rem;
65
65
  --kui-toolbar-group-size: calc(2px + var(--kui-toolbar-group-inner-size));
66
66
  --kui-toolbar-gap: var(--kui-layout-item-gap);
67
+ /* Device safe-area insets, in logical terms. Panes and layouts read these
68
+ through the inherited `--kui-edge-inset-*` edge context: surfaces paint
69
+ through the unsafe area while their content gains compensating padding on
70
+ the edges it still touches (docs/app-layouts.md "Safe areas"). Override
71
+ them to simulate a device or to reserve space for app-owned chrome. */
72
+ --kui-safe-area-block-start: env(safe-area-inset-top, 0px);
73
+ --kui-safe-area-block-end: env(safe-area-inset-bottom, 0px);
74
+ --kui-safe-area-inline-start: env(safe-area-inset-left, 0px);
75
+ --kui-safe-area-inline-end: env(safe-area-inset-right, 0px);
67
76
  --kui-radius-s: var(--wa-border-radius-s, 0.1875rem);
68
77
  --kui-radius-m: var(--wa-border-radius-m, 0.375rem);
69
78
  --kui-radius-l: var(--wa-border-radius-l, 0.75rem);
@@ -386,6 +395,11 @@
386
395
  );
387
396
  }
388
397
 
398
+ :root:dir(rtl) {
399
+ --kui-safe-area-inline-start: env(safe-area-inset-right, 0px);
400
+ --kui-safe-area-inline-end: env(safe-area-inset-left, 0px);
401
+ }
402
+
389
403
  @media (prefers-contrast: more) {
390
404
  :root {
391
405
  --kui-color-pop-fill-quiet: Canvas;
@@ -1,5 +1,9 @@
1
1
  .kui-grid {
2
2
  --kui-grid-gap: var(--kui-layout-item-gap, 0.5rem);
3
+ /* Private per-instance values are reset on every Grid so a nested Grid never
4
+ inherits its ancestor's; the inline style overrides them per prop. */
5
+ --_kui-grid-gap: var(--kui-grid-gap);
6
+ --_kui-grid-flex: 0 1 auto;
3
7
 
4
8
  display: grid;
5
9
  min-width: 0;
@@ -9,3 +13,9 @@
9
13
  grid-template-columns: repeat(var(--_kui-grid-columns), minmax(0, 1fr));
10
14
  gap: var(--_kui-grid-gap, var(--kui-grid-gap));
11
15
  }
16
+
17
+ /* `fill`: the layout root of a definite-height parent (an app root or a
18
+ fixed-height frame) takes its full height. Inside a flex layout use `flex`. */
19
+ .kui-grid[data-fill="true"] {
20
+ height: 100%;
21
+ }
@@ -123,11 +123,56 @@
123
123
  padding: var(--kui-layout-item-padding, 0.5rem);
124
124
  align-items: center;
125
125
  justify-content: center;
126
+ position: relative;
126
127
  align-self: center;
127
128
  cursor: pointer;
128
129
  flex: none;
129
130
  }
130
131
 
132
+ /* The fitted square is the visual and hover surface; the pointer target is at
133
+ least 44px in both dimensions. A transparent, out-of-flow layer extends the
134
+ button's hit area evenly around the square without moving layout or painting
135
+ anything, and hovering it lights the unchanged fitted square. Standard headers
136
+ are 44px tall and reserve the 4px inline slot at the logical end, so the
137
+ extension stays inside the header; the leading 4px falls in the 8px gap before
138
+ the dormant title. Offsets are from the padding box, hence the extra 1px for the
139
+ border. */
140
+ .kui-list-header__action::before {
141
+ --_kui-list-header-action-hit-outset: min(
142
+ 0px,
143
+ calc((var(--_kui-list-header-action-size) - 2.75rem) / 2)
144
+ );
145
+ content: "";
146
+ position: absolute;
147
+ inset: calc(var(--_kui-list-header-action-hit-outset) - 1px);
148
+ }
149
+
150
+ /* Compact headers are 36px tall like the rest of the compact List family, so the
151
+ hit area widens to 44px but never reaches past the header into a neighboring
152
+ row or header. */
153
+ .kui-list-header[data-density="compact"] .kui-list-header__action::before {
154
+ inset-block: -1px;
155
+ }
156
+
157
+ /* Trailing icon actions share one axis: a toolbar control centers its glyph in a
158
+ 44px group slot flush with the 8px inline margin, so the fitted action centers
159
+ itself in a slot of that same size instead of sitting flush at the margin
160
+ (which put its glyph ~4px outside a Pane header's trailing toolbar glyph). The
161
+ slot, not the button, grows by the difference, so the fitted hover square and
162
+ its 18px visual are unchanged. Inline headers carry no outer geometry. */
163
+ .kui-list-header:not([data-inline="true"]) > .kui-list-header__action {
164
+ margin-inline-end: max(
165
+ 0px,
166
+ calc(
167
+ (
168
+ var(--kui-toolbar-group-size, calc(2px + 2.625rem)) -
169
+ var(--_kui-list-header-action-size)
170
+ ) /
171
+ 2
172
+ )
173
+ );
174
+ }
175
+
131
176
  .kui-list-header__toggle {
132
177
  padding: var(--kui-layout-item-padding, 0.5rem);
133
178
  appearance: none;
@@ -1,11 +1,18 @@
1
1
  .kui-list-inset-control {
2
2
  display: flex;
3
3
  margin: 0;
4
- justify-content: stretch;
5
4
  align-items: stretch;
6
5
  gap: 0.5rem;
7
6
  }
8
7
 
8
+ /* `justify-content: stretch` is a no-op on a flex row, so the documented
9
+ stretch comes from the children: each grows from its natural width to share
10
+ the row, and may shrink below its intrinsic minimum instead of overflowing. */
11
+ .kui-list-inset-control > * {
12
+ min-width: 0;
13
+ flex: 1 1 auto;
14
+ }
15
+
9
16
  .kui-list-inset-control[data-sides*="t"] {
10
17
  margin-top: var(--kui-layout-inline-margin, 0.5rem);
11
18
  }
@@ -139,6 +139,12 @@
139
139
  .kui-list-item[data-multiline="true"] {
140
140
  height: fit-content;
141
141
  white-space: normal;
142
+ /* Size the single grid row to its content and center that track in the
143
+ 44px minimum, instead of stretching it. The start-aligned icon then
144
+ measures its first-line offset from the same edge the centered label
145
+ starts at; a stretched track left a short label centered 1.4px below
146
+ the icon's first-line position. */
147
+ align-content: center;
142
148
  }
143
149
 
144
150
  .kui-list-item[data-multiline="true"] .kui-list-item__label {
@@ -1,6 +1,11 @@
1
1
  .kui-list {
2
2
  --kui-list-divider-color: var(--kui-color-border);
3
3
  --kui-list-gap: var(--kui-layout-item-gap, 0.5rem);
4
+ /* Private per-instance values are reset on every List so a nested List that
5
+ omits `gap` or `flex` never inherits its ancestor's; the component's inline
6
+ style overrides these defaults for the props it does set. */
7
+ --_kui-list-gap: 0;
8
+ --_kui-list-flex: 0 1 auto;
4
9
  --_kui-list-inset-top: 0;
5
10
  --_kui-list-inset-right: 0;
6
11
  --_kui-list-inset-bottom: 0;
@@ -120,3 +125,9 @@
120
125
  .kui-list[divider-sides*="l"] {
121
126
  --_kui-list-divider-left: var(--kui-list-divider-color);
122
127
  }
128
+
129
+ /* `fill`: the layout root of a definite-height parent (an app root or a
130
+ fixed-height frame) takes its full height. Inside a flex layout use `flex`. */
131
+ .kui-list[data-fill="true"] {
132
+ height: 100%;
133
+ }
@@ -7,10 +7,33 @@
7
7
  overflow: hidden;
8
8
  flex-direction: column;
9
9
  background: var(--kui-color-surface);
10
+ /* Safe areas: the stack's surfaces paint through unsafe areas; its chrome,
11
+ bottom toolbar, and each view compensate for the edges the stack still
12
+ touches (see pane.css for the `--kui-edge-inset-*` edge context). */
13
+ --_kui-nav-stack-safe-block-start: var(
14
+ --kui-edge-inset-block-start,
15
+ var(--kui-safe-area-block-start, env(safe-area-inset-top, 0px))
16
+ );
17
+ --_kui-nav-stack-safe-block-end: var(
18
+ --kui-edge-inset-block-end,
19
+ var(--kui-safe-area-block-end, env(safe-area-inset-bottom, 0px))
20
+ );
21
+ --_kui-nav-stack-safe-inline-start: var(
22
+ --kui-edge-inset-inline-start,
23
+ var(--kui-safe-area-inline-start, env(safe-area-inset-left, 0px))
24
+ );
25
+ --_kui-nav-stack-safe-inline-end: var(
26
+ --kui-edge-inset-inline-end,
27
+ var(--kui-safe-area-inline-end, env(safe-area-inset-right, 0px))
28
+ );
10
29
  }
11
30
 
12
31
  .kui-nav-stack__chrome,
13
32
  .kui-nav-stack__bottom {
33
+ --kui-edge-inset-block-start: 0px;
34
+ --kui-edge-inset-block-end: 0px;
35
+ --kui-edge-inset-inline-start: 0px;
36
+ --kui-edge-inset-inline-end: 0px;
14
37
  transition: opacity var(--kui-nav-stack-transition-duration, 200ms) ease;
15
38
  }
16
39
 
@@ -38,8 +61,12 @@
38
61
  .kui-nav-stack__chrome {
39
62
  display: flex;
40
63
  min-width: 0;
41
- min-height: calc(2px + 2.625rem);
42
- padding-inline: var(--kui-space-xs);
64
+ min-height: calc(2px + 2.625rem + var(--_kui-nav-stack-safe-block-start));
65
+ padding-block-start: var(--_kui-nav-stack-safe-block-start);
66
+ padding-inline: calc(
67
+ var(--kui-space-xs) + var(--_kui-nav-stack-safe-inline-start)
68
+ )
69
+ calc(var(--kui-space-xs) + var(--_kui-nav-stack-safe-inline-end));
43
70
  align-items: center;
44
71
  gap: var(--kui-space-xs);
45
72
  background: var(--kui-color-surface);
@@ -94,12 +121,28 @@
94
121
  }
95
122
 
96
123
  .kui-nav-stack__viewport {
124
+ --kui-edge-inset-block-start: var(--_kui-nav-stack-safe-block-start);
125
+ --kui-edge-inset-block-end: var(--_kui-nav-stack-safe-block-end);
126
+ --kui-edge-inset-inline-start: var(--_kui-nav-stack-safe-inline-start);
127
+ --kui-edge-inset-inline-end: var(--_kui-nav-stack-safe-inline-end);
97
128
  position: relative;
98
129
  flex: 1;
99
130
  min-height: 0;
100
131
  overflow: hidden;
101
132
  }
102
133
 
134
+ /* The top chrome and the bottom toolbar own their edges; transient chrome
135
+ copies from a transition do not. */
136
+ .kui-nav-stack__chrome ~ .kui-nav-stack__viewport {
137
+ --kui-edge-inset-block-start: 0px;
138
+ }
139
+
140
+ .kui-nav-stack__viewport:has(
141
+ ~ .kui-nav-stack__bottom:not(.kui-nav-stack__chrome-copy)
142
+ ) {
143
+ --kui-edge-inset-block-end: 0px;
144
+ }
145
+
103
146
  .kui-nav-stack__view {
104
147
  position: absolute;
105
148
  overflow: auto;
@@ -108,6 +151,47 @@
108
151
  inset: 0;
109
152
  }
110
153
 
154
+ /* Each view is its region's scroll owner: block insets become padding inside
155
+ the scroller so content scrolls under an unsafe edge but can always be
156
+ scrolled clear. A view whose only child is a Pane or layout lets that child
157
+ own the insets instead. */
158
+ .kui-nav-stack__view:not(
159
+ :has(
160
+ > :is(
161
+ [data-component="pane"],
162
+ [data-component="nav-stack"],
163
+ [data-component="split-view"],
164
+ [data-component="workbench"],
165
+ [data-component="tab-scaffold"]
166
+ ):only-child
167
+ )
168
+ ) {
169
+ padding-block: var(--kui-edge-inset-block-start)
170
+ var(--kui-edge-inset-block-end);
171
+ padding-inline: var(--kui-edge-inset-inline-start)
172
+ var(--kui-edge-inset-inline-end);
173
+ scroll-padding-block: var(--kui-edge-inset-block-start)
174
+ var(--kui-edge-inset-block-end);
175
+ }
176
+
177
+ .kui-nav-stack__view:not(
178
+ :has(
179
+ > :is(
180
+ [data-component="pane"],
181
+ [data-component="nav-stack"],
182
+ [data-component="split-view"],
183
+ [data-component="workbench"],
184
+ [data-component="tab-scaffold"]
185
+ ):only-child
186
+ )
187
+ )
188
+ > * {
189
+ --kui-edge-inset-block-start: 0px;
190
+ --kui-edge-inset-block-end: 0px;
191
+ --kui-edge-inset-inline-start: 0px;
192
+ --kui-edge-inset-inline-end: 0px;
193
+ }
194
+
111
195
  /* Only the active view (and any view mid-exit) is shown; lower entries stay
112
196
  mounted for state/focus but hidden behind it. */
113
197
  .kui-nav-stack__view[data-nav-active="false"] {
@@ -127,6 +211,9 @@
127
211
  }
128
212
 
129
213
  .kui-nav-stack__bottom {
214
+ padding-block-end: var(--_kui-nav-stack-safe-block-end);
215
+ padding-inline: var(--_kui-nav-stack-safe-inline-start)
216
+ var(--_kui-nav-stack-safe-inline-end);
130
217
  border-block-start: 1px solid var(--kui-color-border);
131
218
  background: var(--kui-color-surface);
132
219
  }