@kerfjs/ui 5.0.0-beta.76 → 5.0.0-beta.78

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 (147) hide show
  1. package/README.md +15 -0
  2. package/ai/application-ui-diagnostic-ids-v1.json +3 -0
  3. package/ai/component-catalog.json +333 -28
  4. package/ai/component-composition.json +224 -17
  5. package/ai/components/nav-stack.md +4 -3
  6. package/ai/components/select.md +1 -0
  7. package/ai/components/split-view.md +1 -0
  8. package/ai/components/surface-scaffold.md +11 -2
  9. package/ai/components/text.md +4 -2
  10. package/ai/components/toolbar-control-group.md +22 -3
  11. package/ai/components/toolbar-text.md +21 -4
  12. package/ai/components/toolbar.md +12 -4
  13. package/ai/public-api-signatures-v1.md +99 -127
  14. package/ai/skill.md +37 -3
  15. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  16. package/dist/browser/nav-stack.js +2 -0
  17. package/dist/browser/split-view.js +2 -0
  18. package/dist/catalog-resources.d.ts +3 -3
  19. package/dist/catalog.d.ts +3 -3
  20. package/dist/catalog.js +6 -6
  21. package/dist/chunk-2GOCOLQL.js +270 -0
  22. package/dist/chunk-2GOCOLQL.js.map +1 -0
  23. package/dist/{chunk-ZVYA6TLR.js → chunk-4Y4L5YEI.js} +3 -1
  24. package/dist/chunk-4Y4L5YEI.js.map +1 -0
  25. package/dist/{chunk-XIZRXI7N.js → chunk-A7DYEERU.js} +13 -1
  26. package/dist/chunk-A7DYEERU.js.map +1 -0
  27. package/dist/{chunk-7QWIJEK5.js → chunk-DSLRNR43.js} +9 -8
  28. package/dist/chunk-DSLRNR43.js.map +1 -0
  29. package/dist/{chunk-AOAW55JN.js → chunk-DXQZ3BEO.js} +4 -1
  30. package/dist/chunk-DXQZ3BEO.js.map +1 -0
  31. package/dist/chunk-IKCUPTR2.js +170 -0
  32. package/dist/chunk-IKCUPTR2.js.map +1 -0
  33. package/dist/{chunk-HGSTMZZ6.js → chunk-JL4BBZP4.js} +5 -4
  34. package/dist/chunk-JL4BBZP4.js.map +1 -0
  35. package/dist/{chunk-IVLTEBBT.js → chunk-M6BWQMQY.js} +2 -2
  36. package/dist/{chunk-IVLTEBBT.js.map → chunk-M6BWQMQY.js.map} +1 -1
  37. package/dist/{chunk-72LLWYP2.js → chunk-NVKRZ5DI.js} +1 -1
  38. package/dist/chunk-NVKRZ5DI.js.map +1 -0
  39. package/dist/{chunk-GO2CZX4M.js → chunk-RRCD3A6G.js} +13 -5
  40. package/dist/chunk-RRCD3A6G.js.map +1 -0
  41. package/dist/{chunk-5PAVAR4B.js → chunk-VHXHLZ6R.js} +10 -1
  42. package/dist/chunk-VHXHLZ6R.js.map +1 -0
  43. package/dist/collapsible-panel.js +2 -2
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.js +9 -8
  46. package/dist/list-header.d.ts +1 -0
  47. package/dist/list-header.js +1 -1
  48. package/dist/nav-stack-B1Qc2e5q.d.ts +101 -0
  49. package/dist/nav-stack.d.ts +12 -93
  50. package/dist/nav-stack.js +5 -1
  51. package/dist/pane.d.ts +5 -2
  52. package/dist/pane.js +1 -1
  53. package/dist/select-register.js +3 -0
  54. package/dist/select-register.js.map +1 -1
  55. package/dist/select.d.ts +1 -0
  56. package/dist/select.js +1 -1
  57. package/dist/split-view.d.ts +9 -4
  58. package/dist/split-view.js +7 -3
  59. package/dist/split-view.js.map +1 -1
  60. package/dist/styles/components/actions/toolbar/toolbar.css +32 -0
  61. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +105 -7
  62. package/dist/styles/components/actions/toolbar-text/toolbar-text.css +17 -0
  63. package/dist/styles/components/layout/pane/pane.css +7 -0
  64. package/dist/styles/components/navigation/nav-stack/nav-stack.css +17 -12
  65. package/dist/styles/components/navigation/tab-scaffold/tab-scaffold.css +17 -0
  66. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +55 -0
  67. package/dist/styles/components/typography/text/text.css +19 -0
  68. package/dist/styles/nav-stack.css +17 -12
  69. package/dist/styles/pane.css +7 -0
  70. package/dist/styles/surface-scaffold.css +55 -0
  71. package/dist/styles/tab-scaffold.css +17 -0
  72. package/dist/styles/text.css +19 -0
  73. package/dist/styles/toolbar-control-group.css +105 -7
  74. package/dist/styles/toolbar-text.css +17 -0
  75. package/dist/styles/toolbar.css +32 -0
  76. package/dist/surface-scaffold-register.d.ts +4 -0
  77. package/dist/surface-scaffold-register.js +59 -0
  78. package/dist/surface-scaffold-register.js.map +1 -0
  79. package/dist/surface-scaffold.d.ts +10 -2
  80. package/dist/surface-scaffold.js +1 -1
  81. package/dist/tab-scaffold.d.ts +2 -0
  82. package/dist/tab-scaffold.js +1 -0
  83. package/dist/tab-scaffold.js.map +1 -1
  84. package/dist/text.d.ts +5 -2
  85. package/dist/text.js +1 -1
  86. package/dist/toolbar-control-group.d.ts +14 -2
  87. package/dist/toolbar-control-group.js +1 -1
  88. package/dist/toolbar-text.d.ts +10 -2
  89. package/dist/toolbar-text.js +1 -1
  90. package/dist/wire-nav-stack.js +58 -22
  91. package/dist/wire-nav-stack.js.map +1 -1
  92. package/dist/wire-toolbar-visibility.d.ts +13 -0
  93. package/dist/wire-toolbar-visibility.js +3 -0
  94. package/dist/wire-toolbar-visibility.js.map +1 -0
  95. package/dist/workbench.d.ts +5 -13
  96. package/dist/workbench.js +1 -1
  97. package/docs/accessibility.md +15 -0
  98. package/docs/collapsible-panel.md +4 -0
  99. package/docs/component-contract.md +38 -0
  100. package/docs/component-selection.md +56 -2
  101. package/docs/design/templates/toolbar/width-visibility-320-dark.svg +1 -0
  102. package/docs/design/templates/toolbar/width-visibility-320.svg +1 -0
  103. package/docs/design/templates/toolbar/width-visibility-600-dark.svg +1 -0
  104. package/docs/design/templates/toolbar/width-visibility-600.svg +1 -0
  105. package/docs/design/templates/toolbar-control-group/icon-tile-brand-dark.svg +1 -0
  106. package/docs/design/templates/toolbar-control-group/icon-tile-brand.svg +1 -0
  107. package/docs/design/templates/toolbar-control-group/icon-tile-danger-dark.svg +1 -0
  108. package/docs/design/templates/toolbar-control-group/icon-tile-danger.svg +1 -0
  109. package/docs/design/templates/toolbar-control-group/icon-tile-neutral-dark.svg +1 -0
  110. package/docs/design/templates/toolbar-control-group/icon-tile-neutral.svg +1 -0
  111. package/docs/design/templates/toolbar-control-group/icon-tile-success-dark.svg +1 -0
  112. package/docs/design/templates/toolbar-control-group/icon-tile-success.svg +1 -0
  113. package/docs/design/templates/toolbar-control-group/icon-tile-warning-dark.svg +1 -0
  114. package/docs/design/templates/toolbar-control-group/icon-tile-warning.svg +1 -0
  115. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  116. package/docs/design/templates/toolbar-control-group.svg +1 -1
  117. package/docs/design/templates/toolbar-dark.svg +1 -1
  118. package/docs/design/templates/toolbar-text/dark-tone-dark.svg +1 -0
  119. package/docs/design/templates/toolbar-text/dark-tone.svg +1 -0
  120. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  121. package/docs/design/templates/toolbar-text.svg +1 -1
  122. package/docs/design/templates/toolbar.svg +1 -1
  123. package/docs/design/templates.md +2 -0
  124. package/docs/layout.md +32 -1
  125. package/docs/nav-stack.md +35 -19
  126. package/docs/recipes.md +5 -1
  127. package/docs/split-view.md +7 -4
  128. package/docs/surface-scaffold.md +46 -0
  129. package/docs/tab-scaffold.md +57 -0
  130. package/docs/ui-doctor.md +23 -0
  131. package/docs/workbench.md +3 -2
  132. package/doctor/component-style-checks.mjs +224 -0
  133. package/doctor/index.mjs +42 -13
  134. package/llms.txt +34 -0
  135. package/package.json +11 -2
  136. package/ux-demo/recipes/list-detail-dialog.tsx +4 -0
  137. package/ux-demo/recipes/navigation-stack.tsx +2 -2
  138. package/dist/chunk-5PAVAR4B.js.map +0 -1
  139. package/dist/chunk-72LLWYP2.js.map +0 -1
  140. package/dist/chunk-7QWIJEK5.js.map +0 -1
  141. package/dist/chunk-AOAW55JN.js.map +0 -1
  142. package/dist/chunk-GO2CZX4M.js.map +0 -1
  143. package/dist/chunk-HGSTMZZ6.js.map +0 -1
  144. package/dist/chunk-SJY7KC45.js +0 -123
  145. package/dist/chunk-SJY7KC45.js.map +0 -1
  146. package/dist/chunk-XIZRXI7N.js.map +0 -1
  147. package/dist/chunk-ZVYA6TLR.js.map +0 -1
@@ -1,12 +1,67 @@
1
1
  .kui-dialog-surface > wa-dialog {
2
+ --_kui-dialog-surface-width: 35rem;
3
+ --_kui-dialog-surface-resolved-gutter: max(
4
+ 0px,
5
+ var(--_kui-dialog-surface-gutter, 1rem)
6
+ );
2
7
  --width: min(35rem, calc(100vw - 2rem));
3
8
  }
4
9
  .kui-dialog-surface[data-size="small"] > wa-dialog {
10
+ --_kui-dialog-surface-width: 25rem;
5
11
  --width: min(25rem, calc(100vw - 2rem));
6
12
  }
7
13
  .kui-dialog-surface[data-size="large"] > wa-dialog {
14
+ --_kui-dialog-surface-width: 50rem;
8
15
  --width: min(50rem, calc(100vw - 2rem));
9
16
  }
17
+ .kui-dialog-surface[data-presentation="modal"][data-preferred-width]
18
+ > wa-dialog {
19
+ --width: min(
20
+ var(--_kui-dialog-surface-preferred-width),
21
+ calc(100vw - 2rem)
22
+ );
23
+ }
24
+ .kui-dialog-surface[data-presentation="modal"][data-viewport-gutter]
25
+ > wa-dialog {
26
+ --width: min(
27
+ var(
28
+ --_kui-dialog-surface-preferred-width,
29
+ var(--_kui-dialog-surface-width)
30
+ ),
31
+ max(
32
+ 0px,
33
+ calc(
34
+ 100vw - var(--_kui-dialog-surface-resolved-gutter) -
35
+ var(--_kui-dialog-surface-resolved-gutter)
36
+ )
37
+ )
38
+ );
39
+ }
40
+ /* Web Awesome has a separate max-width clamp; setting --width alone cannot
41
+ provide an 8px phone gutter. Omitted props retain its legacy geometry. */
42
+ .kui-dialog-surface[data-presentation="modal"][data-viewport-gutter]
43
+ > wa-dialog::part(dialog) {
44
+ max-width: max(
45
+ 0px,
46
+ calc(
47
+ 100vw - var(--_kui-dialog-surface-resolved-gutter) -
48
+ var(--_kui-dialog-surface-resolved-gutter)
49
+ )
50
+ );
51
+ }
52
+ .kui-dialog-surface[data-presentation="modal"][data-max-height]
53
+ > wa-dialog::part(dialog) {
54
+ max-height: max(
55
+ 0px,
56
+ min(
57
+ var(--_kui-dialog-surface-max-height, 100dvh),
58
+ calc(
59
+ 100dvh - var(--_kui-dialog-surface-resolved-gutter) -
60
+ var(--_kui-dialog-surface-resolved-gutter)
61
+ )
62
+ )
63
+ );
64
+ }
10
65
  .kui-dialog-surface[data-body-inset="none"] > wa-dialog::part(body),
11
66
  .kui-dialog-surface[data-footer-inset="none"] > wa-dialog::part(footer) {
12
67
  padding: 0;
@@ -137,3 +137,22 @@ span.kui-text {
137
137
  .kui-list-header[data-size="xl"] .kui-list-header__title > .kui-text {
138
138
  font-size: var(--kui-font-xl);
139
139
  }
140
+
141
+ /* Exterior control gutters preserve the Text-owned border and padding.
142
+ These rules follow inline/context resets so an explicit prop also works there. */
143
+
144
+ .kui-text[data-control-margins*="t"] {
145
+ margin-top: var(--kui-layout-inline-margin, 0.5rem);
146
+ }
147
+
148
+ .kui-text[data-control-margins*="r"] {
149
+ margin-right: var(--kui-layout-inline-margin, 0.5rem);
150
+ }
151
+
152
+ .kui-text[data-control-margins*="b"] {
153
+ margin-bottom: var(--kui-layout-inline-margin, 0.5rem);
154
+ }
155
+
156
+ .kui-text[data-control-margins*="l"] {
157
+ margin-left: var(--kui-layout-inline-margin, 0.5rem);
158
+ }
@@ -37,7 +37,9 @@
37
37
  transition: opacity var(--kui-nav-stack-transition-duration, 200ms) ease;
38
38
  }
39
39
 
40
+ /* Establish the starting opacity instantly; only the release animates. */
40
41
  .kui-nav-stack__chrome--entering {
42
+ transition: none;
41
43
  opacity: 0;
42
44
  }
43
45
 
@@ -69,10 +71,6 @@
69
71
  background: var(--kui-color-surface);
70
72
  }
71
73
 
72
- .kui-nav-stack__header {
73
- min-width: 0;
74
- }
75
-
76
74
  .kui-nav-stack__viewport {
77
75
  --kui-edge-inset-block-start: var(--_kui-nav-stack-safe-block-start);
78
76
  --kui-edge-inset-block-end: var(--_kui-nav-stack-safe-block-end);
@@ -104,11 +102,9 @@
104
102
  inset: 0;
105
103
  }
106
104
 
107
- .kui-nav-stack__view[data-appearance="sunken"] {
108
- background: var(
109
- --kui-sunken-panel-background,
110
- var(--kui-color-surface-lowered)
111
- );
105
+ .kui-nav-stack__view:has(> [data-component="pane"]) {
106
+ display: grid;
107
+ overflow: hidden;
112
108
  }
113
109
 
114
110
  /* Each view is its region's scroll owner: block insets become padding inside
@@ -156,6 +152,10 @@
156
152
  visibility: hidden;
157
153
  }
158
154
 
155
+ .kui-nav-stack__view[data-nav-revealed="true"] {
156
+ visibility: visible;
157
+ }
158
+
159
159
  .kui-nav-stack__view[data-nav-exiting="true"] {
160
160
  z-index: 1;
161
161
  visibility: visible;
@@ -163,15 +163,20 @@
163
163
 
164
164
  /* Push: the incoming view starts off the trailing edge and slides to rest.
165
165
  Pop: the outgoing view starts at rest and slides off the trailing edge. */
166
+ /* Establish the offscreen start without first transitioning toward it. */
167
+ .kui-nav-stack__view--entering {
168
+ transition: none;
169
+ }
170
+
166
171
  .kui-nav-stack__view--entering,
167
172
  .kui-nav-stack__view--exiting {
168
173
  transform: translateX(100%);
169
174
  }
170
175
 
171
176
  .kui-nav-stack__bottom {
172
- padding-block-end: var(--_kui-nav-stack-safe-block-end);
173
- padding-inline: var(--_kui-nav-stack-safe-inline-start)
174
- var(--_kui-nav-stack-safe-inline-end);
177
+ --kui-edge-inset-block-end: var(--_kui-nav-stack-safe-block-end);
178
+ --kui-edge-inset-inline-start: var(--_kui-nav-stack-safe-inline-start);
179
+ --kui-edge-inset-inline-end: var(--_kui-nav-stack-safe-inline-end);
175
180
  border-block-start: 1px solid
176
181
  var(--_kui-nav-stack-bottom-divider, transparent);
177
182
  background: var(--kui-color-surface);
@@ -158,6 +158,13 @@
158
158
  var(--_kui-pane-content-block-end);
159
159
  }
160
160
 
161
+ /* Add one outer gutter without changing the inherited item-margin token.
162
+ Nested Row, List, and Grid therefore keep their own default 8px insets. */
163
+ .kui-pane[data-deep-inset="true"] > .kui-pane__content {
164
+ padding-inline: calc(var(--_kui-pane-safe-inline-start) + var(--kui-space-xs))
165
+ calc(var(--_kui-pane-safe-inline-end) + var(--kui-space-xs));
166
+ }
167
+
161
168
  .kui-pane__header ~ .kui-pane__content {
162
169
  --_kui-pane-content-block-start: 0px;
163
170
  }
@@ -1,12 +1,67 @@
1
1
  .kui-dialog-surface > wa-dialog {
2
+ --_kui-dialog-surface-width: 35rem;
3
+ --_kui-dialog-surface-resolved-gutter: max(
4
+ 0px,
5
+ var(--_kui-dialog-surface-gutter, 1rem)
6
+ );
2
7
  --width: min(35rem, calc(100vw - 2rem));
3
8
  }
4
9
  .kui-dialog-surface[data-size="small"] > wa-dialog {
10
+ --_kui-dialog-surface-width: 25rem;
5
11
  --width: min(25rem, calc(100vw - 2rem));
6
12
  }
7
13
  .kui-dialog-surface[data-size="large"] > wa-dialog {
14
+ --_kui-dialog-surface-width: 50rem;
8
15
  --width: min(50rem, calc(100vw - 2rem));
9
16
  }
17
+ .kui-dialog-surface[data-presentation="modal"][data-preferred-width]
18
+ > wa-dialog {
19
+ --width: min(
20
+ var(--_kui-dialog-surface-preferred-width),
21
+ calc(100vw - 2rem)
22
+ );
23
+ }
24
+ .kui-dialog-surface[data-presentation="modal"][data-viewport-gutter]
25
+ > wa-dialog {
26
+ --width: min(
27
+ var(
28
+ --_kui-dialog-surface-preferred-width,
29
+ var(--_kui-dialog-surface-width)
30
+ ),
31
+ max(
32
+ 0px,
33
+ calc(
34
+ 100vw - var(--_kui-dialog-surface-resolved-gutter) -
35
+ var(--_kui-dialog-surface-resolved-gutter)
36
+ )
37
+ )
38
+ );
39
+ }
40
+ /* Web Awesome has a separate max-width clamp; setting --width alone cannot
41
+ provide an 8px phone gutter. Omitted props retain its legacy geometry. */
42
+ .kui-dialog-surface[data-presentation="modal"][data-viewport-gutter]
43
+ > wa-dialog::part(dialog) {
44
+ max-width: max(
45
+ 0px,
46
+ calc(
47
+ 100vw - var(--_kui-dialog-surface-resolved-gutter) -
48
+ var(--_kui-dialog-surface-resolved-gutter)
49
+ )
50
+ );
51
+ }
52
+ .kui-dialog-surface[data-presentation="modal"][data-max-height]
53
+ > wa-dialog::part(dialog) {
54
+ max-height: max(
55
+ 0px,
56
+ min(
57
+ var(--_kui-dialog-surface-max-height, 100dvh),
58
+ calc(
59
+ 100dvh - var(--_kui-dialog-surface-resolved-gutter) -
60
+ var(--_kui-dialog-surface-resolved-gutter)
61
+ )
62
+ )
63
+ );
64
+ }
10
65
  .kui-dialog-surface[data-body-inset="none"] > wa-dialog::part(body),
11
66
  .kui-dialog-surface[data-footer-inset="none"] > wa-dialog::part(footer) {
12
67
  padding: 0;
@@ -73,6 +73,23 @@
73
73
  scroll-padding-block-start: var(--kui-edge-inset-block-start);
74
74
  }
75
75
 
76
+ /* A plain scene adds one outer gutter; a sole Pane or layout keeps ownership
77
+ of its own content and may opt into deepInset there. */
78
+ .kui-tab-scaffold__scene[data-deep-inset="true"]:not(
79
+ :has(
80
+ > :is(
81
+ [data-component="pane"],
82
+ [data-component="nav-stack"],
83
+ [data-component="split-view"],
84
+ [data-component="workbench"],
85
+ [data-component="tab-scaffold"]
86
+ ):only-child
87
+ )
88
+ ) {
89
+ padding-inline: calc(var(--kui-edge-inset-inline-start) + var(--kui-space-xs))
90
+ calc(var(--kui-edge-inset-inline-end) + var(--kui-space-xs));
91
+ }
92
+
76
93
  .kui-tab-scaffold__scene
77
94
  > :not(
78
95
  :is(
@@ -137,3 +137,22 @@ span.kui-text {
137
137
  .kui-list-header[data-size="xl"] .kui-list-header__title > .kui-text {
138
138
  font-size: var(--kui-font-xl);
139
139
  }
140
+
141
+ /* Exterior control gutters preserve the Text-owned border and padding.
142
+ These rules follow inline/context resets so an explicit prop also works there. */
143
+
144
+ .kui-text[data-control-margins*="t"] {
145
+ margin-top: var(--kui-layout-inline-margin, 0.5rem);
146
+ }
147
+
148
+ .kui-text[data-control-margins*="r"] {
149
+ margin-right: var(--kui-layout-inline-margin, 0.5rem);
150
+ }
151
+
152
+ .kui-text[data-control-margins*="b"] {
153
+ margin-bottom: var(--kui-layout-inline-margin, 0.5rem);
154
+ }
155
+
156
+ .kui-text[data-control-margins*="l"] {
157
+ margin-left: var(--kui-layout-inline-margin, 0.5rem);
158
+ }
@@ -196,6 +196,10 @@
196
196
  ) {
197
197
  overflow: visible;
198
198
  }
199
+ .kui-toolbar-control-group[data-content="search"][data-expanded="true"][data-expanded-overflow="visible"] {
200
+ height: auto;
201
+ overflow: visible;
202
+ }
199
203
  .kui-toolbar-control-group[data-content="search"][data-expanded="false"] {
200
204
  width: var(--kui-toolbar-group-size, calc(2px + 2.625rem));
201
205
  flex: 0 0 auto;
@@ -606,17 +610,23 @@
606
610
  wa-button,
607
611
  .kui-select,
608
612
  .kui-toolbar-action-link
609
- ):only-child
613
+ ):nth-child(1 of :where(:not([data-toolbar-visibility-state]))):where(
614
+ :nth-last-child(1 of :not([data-toolbar-visibility-state]))
615
+ )
610
616
  ):hover {
611
617
  background-color: var(--kui-toolbar-control-hover-background);
612
618
  }
613
619
 
614
620
  .kui-toolbar-control-group:not([data-single="true"]):has(
615
- > :is(button, wa-button, .kui-toolbar-action-link):only-child
621
+ > :is(button, wa-button, .kui-toolbar-action-link):nth-child(
622
+ 1 of :where(:not([data-toolbar-visibility-state]))
623
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
616
624
  )
617
625
  > :is(button, wa-button, .kui-toolbar-action-link):hover,
618
626
  .kui-toolbar-control-group:not([data-single="true"]):has(
619
- > :is(button, wa-button):only-child
627
+ > :is(button, wa-button):nth-child(
628
+ 1 of :where(:not([data-toolbar-visibility-state]))
629
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
620
630
  )
621
631
  > wa-button:hover::part(base) {
622
632
  border-color: transparent;
@@ -761,25 +771,41 @@
761
771
 
762
772
  .kui-toolbar-control-group[data-content="avatar"]:is(
763
773
  [data-single="true"],
764
- :has(> :is(button, wa-button):only-child)
774
+ :has(
775
+ > :is(button, wa-button):nth-child(
776
+ 1 of :where(:not([data-toolbar-visibility-state]))
777
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
778
+ )
765
779
  ) {
766
780
  background-image: var(--kui-toolbar-avatar-image);
767
781
  }
768
782
 
769
783
  .kui-toolbar-control-group[data-content="avatar"]:is(
770
784
  [data-single="true"],
771
- :has(> :is(button, wa-button):only-child)
785
+ :has(
786
+ > :is(button, wa-button):nth-child(
787
+ 1 of :where(:not([data-toolbar-visibility-state]))
788
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
789
+ )
772
790
  )
773
791
  > :is(button, wa-button) {
774
792
  color: transparent;
775
793
  }
776
794
 
777
795
  .kui-toolbar-control-group[data-content="avatar"][data-single="false"]:not(
778
- :has(> :is(button, wa-button):only-child)
796
+ :has(
797
+ > :is(button, wa-button):nth-child(
798
+ 1 of :where(:not([data-toolbar-visibility-state]))
799
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
800
+ )
779
801
  )
780
802
  > button[aria-pressed="true"],
781
803
  .kui-toolbar-control-group[data-content="avatar"][data-single="false"]:not(
782
- :has(> :is(button, wa-button):only-child)
804
+ :has(
805
+ > :is(button, wa-button):nth-child(
806
+ 1 of :where(:not([data-toolbar-visibility-state]))
807
+ ):where(:nth-last-child(1 of :not([data-toolbar-visibility-state])))
808
+ )
783
809
  )
784
810
  > wa-button[aria-pressed="true"]::part(base) {
785
811
  color: transparent;
@@ -788,3 +814,75 @@
788
814
  background-repeat: no-repeat;
789
815
  background-size: contain;
790
816
  }
817
+
818
+ /* Explicit quiet palette only for a lone decorative icon tile. The structural
819
+ guard keeps interactive, mixed and borderless groups on their existing chrome. */
820
+
821
+ .kui-toolbar-control-group[data-single="true"][data-appearance="contained"][data-tile-tone="neutral"]:has(
822
+ > svg[aria-hidden="true"]
823
+ ):not(
824
+ :has(> :not(svg[aria-hidden="true"], [data-toolbar-visibility-state])),
825
+ :has(> svg ~ svg)
826
+ ) {
827
+ background: var(--kui-color-neutral-fill-quiet);
828
+ border-color: var(--kui-color-neutral-border-quiet);
829
+ color: var(--kui-color-neutral-on-quiet);
830
+ }
831
+
832
+ .kui-toolbar-control-group[data-single="true"][data-appearance="contained"][data-tile-tone="brand"]:has(
833
+ > svg[aria-hidden="true"]
834
+ ):not(
835
+ :has(> :not(svg[aria-hidden="true"], [data-toolbar-visibility-state])),
836
+ :has(> svg ~ svg)
837
+ ) {
838
+ background: var(--kui-color-brand-fill-quiet);
839
+ border-color: var(--kui-color-brand-border-quiet);
840
+ color: var(--kui-color-brand-on-quiet);
841
+ }
842
+
843
+ .kui-toolbar-control-group[data-single="true"][data-appearance="contained"][data-tile-tone="success"]:has(
844
+ > svg[aria-hidden="true"]
845
+ ):not(
846
+ :has(> :not(svg[aria-hidden="true"], [data-toolbar-visibility-state])),
847
+ :has(> svg ~ svg)
848
+ ) {
849
+ background: var(--kui-color-success-fill-quiet);
850
+ border-color: var(--kui-color-success-border-quiet);
851
+ color: var(--kui-color-success-on-quiet);
852
+ }
853
+
854
+ .kui-toolbar-control-group[data-single="true"][data-appearance="contained"][data-tile-tone="warning"]:has(
855
+ > svg[aria-hidden="true"]
856
+ ):not(
857
+ :has(> :not(svg[aria-hidden="true"], [data-toolbar-visibility-state])),
858
+ :has(> svg ~ svg)
859
+ ) {
860
+ background: var(--kui-color-warning-fill-quiet);
861
+ border-color: var(--kui-color-warning-border-quiet);
862
+ color: var(--kui-color-warning-on-quiet);
863
+ }
864
+
865
+ .kui-toolbar-control-group[data-single="true"][data-appearance="contained"][data-tile-tone="danger"]:has(
866
+ > svg[aria-hidden="true"]
867
+ ):not(
868
+ :has(> :not(svg[aria-hidden="true"], [data-toolbar-visibility-state])),
869
+ :has(> svg ~ svg)
870
+ ) {
871
+ background: var(--kui-color-danger-fill-quiet);
872
+ border-color: var(--kui-color-danger-border-quiet);
873
+ color: var(--kui-color-danger-on-quiet);
874
+ }
875
+ .kui-toolbar-control-group[data-toolbar-width-hidden="true"],
876
+ .kui-toolbar-control-group__busy-status[data-toolbar-width-hidden="true"],
877
+ .kui-toolbar-control-group:has(
878
+ > [data-toolbar-visibility-state][data-toolbar-width-hidden="true"]
879
+ ),
880
+ .kui-toolbar-control-group__busy-status:has(
881
+ > [data-toolbar-visibility-state][data-toolbar-width-hidden="true"]
882
+ ) {
883
+ display: none;
884
+ }
885
+ .kui-toolbar-control-group > [data-toolbar-visibility-state],
886
+ .kui-toolbar-control-group__busy-status > [data-toolbar-visibility-state] {
887
+ display: none;
888
+ }
@@ -105,6 +105,14 @@ button.kui-toolbar-text:focus-visible {
105
105
  font-size: var(--kui-font-2xs);
106
106
  }
107
107
 
108
+ .kui-toolbar-text[data-tone="dark"] {
109
+ color: var(--kui-color-neutral-on-loud);
110
+ }
111
+
112
+ button.kui-toolbar-text[data-tone="dark"]:hover {
113
+ background: color-mix(in srgb, var(--kui-color-surface) 28%, transparent);
114
+ }
115
+
108
116
  /* Single line, no ellipsis: hard-clip the overflow instead of showing …. */
109
117
  .kui-toolbar-text[data-ellipsis="false"] .kui-toolbar-text__text {
110
118
  text-overflow: clip;
@@ -151,3 +159,12 @@ button.kui-toolbar-text:focus-visible {
151
159
  line-clamp: none;
152
160
  max-height: calc(var(--kui-toolbar-text-max-lines) * 1.25em);
153
161
  }
162
+ .kui-toolbar-text[data-toolbar-width-hidden="true"],
163
+ .kui-toolbar-text:has(
164
+ > [data-toolbar-visibility-state][data-toolbar-width-hidden="true"]
165
+ ) {
166
+ display: none;
167
+ }
168
+ .kui-toolbar-text > [data-toolbar-visibility-state] {
169
+ display: none;
170
+ }
@@ -390,3 +390,35 @@
390
390
  display: none;
391
391
  }
392
392
  }
393
+ .kui-toolbar > [data-toolbar-visibility-probe] {
394
+ position: fixed;
395
+ left: 0;
396
+ top: 0;
397
+ height: 0;
398
+ overflow: hidden;
399
+ visibility: hidden;
400
+ pointer-events: none;
401
+ contain: layout paint;
402
+ }
403
+
404
+ .kui-toolbar > [data-toolbar-visibility-probe] > div {
405
+ position: absolute;
406
+ top: 0;
407
+ height: 0;
408
+ min-width: 0;
409
+ max-width: none;
410
+ padding: 0;
411
+ border: 0;
412
+ margin: 0;
413
+ }
414
+
415
+ /* A typed sentinel distinguishes invalid/unresolved input from a valid zero. */
416
+ @property --kui-toolbar-visibility-length {
417
+ syntax: "<length-percentage>";
418
+ inherits: false;
419
+ initial-value: -1px;
420
+ }
421
+ .kui-toolbar > [data-toolbar-visibility-probe] > div {
422
+ left: var(--kui-toolbar-visibility-length);
423
+ width: var(--kui-toolbar-visibility-length);
424
+ }
@@ -0,0 +1,4 @@
1
+ /** Importing this module registers wa-dialog and repairs wrapped native names. */
2
+ declare const surfaceScaffoldElementsRegistered = true;
3
+
4
+ export { surfaceScaffoldElementsRegistered };
@@ -0,0 +1,59 @@
1
+ import WaDialog from '@awesome.me/webawesome/dist/components/dialog/dialog.js';
2
+
3
+ // src/components/surfaces/surface-scaffold/register/surface-scaffold-register.ts
4
+
5
+ // src/components/surfaces/surface-scaffold/internal/install-dialog-label.ts
6
+ var installed = /* @__PURE__ */ new WeakSet();
7
+ var observed = /* @__PURE__ */ new WeakSet();
8
+ var ownedNames = /* @__PURE__ */ new WeakMap();
9
+ function installDialogLabel(prototype) {
10
+ if (installed.has(prototype)) return;
11
+ installed.add(prototype);
12
+ const target = prototype;
13
+ const update = target.update;
14
+ target.update = function(changed) {
15
+ update.call(this, changed);
16
+ if (!this.parentElement?.classList.contains("kui-dialog-surface")) return;
17
+ if (!observed.has(this)) {
18
+ observed.add(this);
19
+ new MutationObserver(() => syncDialogLabel(this)).observe(this, {
20
+ attributes: true,
21
+ attributeFilter: ["aria-label"]
22
+ });
23
+ }
24
+ syncDialogLabel(this);
25
+ };
26
+ }
27
+ function syncDialogLabel(host) {
28
+ if (!host.parentElement?.classList.contains("kui-dialog-surface")) return;
29
+ const panel = host.dialog;
30
+ if (!panel) return;
31
+ const previous = ownedNames.get(panel);
32
+ const externalName = ["aria-label", "aria-labelledby"].some((attribute2) => {
33
+ const value2 = panel.getAttribute(attribute2);
34
+ return value2 !== null && !(previous?.attribute === attribute2 && previous.value === value2);
35
+ });
36
+ if (externalName) {
37
+ if (previous && panel.getAttribute(previous.attribute) === previous.value)
38
+ panel.removeAttribute(previous.attribute);
39
+ ownedNames.delete(panel);
40
+ return;
41
+ }
42
+ const explicit = host.getAttribute("aria-label")?.trim();
43
+ const title = host.shadowRoot?.getElementById("title");
44
+ const attribute = !explicit && !host.withoutHeader && title ? "aria-labelledby" : "aria-label";
45
+ const value = attribute === "aria-labelledby" ? "title" : explicit || host.label;
46
+ if (previous && previous.attribute !== attribute)
47
+ panel.removeAttribute(previous.attribute);
48
+ if (panel.getAttribute(attribute) !== value)
49
+ panel.setAttribute(attribute, value);
50
+ ownedNames.set(panel, { attribute, value });
51
+ }
52
+
53
+ // src/components/surfaces/surface-scaffold/register/surface-scaffold-register.ts
54
+ installDialogLabel(WaDialog.prototype);
55
+ var surfaceScaffoldElementsRegistered = true;
56
+
57
+ export { surfaceScaffoldElementsRegistered };
58
+
59
+ //# sourceMappingURL=surface-scaffold-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/surfaces/surface-scaffold/internal/install-dialog-label.ts","../src/components/surfaces/surface-scaffold/register/surface-scaffold-register.ts"],"names":["attribute","value"],"mappings":"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","file":"surface-scaffold-register.js","sourcesContent":["interface NamedDialog extends HTMLElement {\n dialog?: HTMLDialogElement;\n label: string;\n withoutHeader: boolean;\n update(changed: unknown): void;\n}\n\nconst installed = new WeakSet<object>();\nconst observed = new WeakSet<HTMLElement>();\nconst ownedNames = new WeakMap<\n HTMLElement,\n { attribute: string; value: string }\n>();\n\n/** Repair Web Awesome's missing native dialog name before its first modal open. */\nexport function installDialogLabel(prototype: object): void {\n if (installed.has(prototype)) return;\n installed.add(prototype);\n const target = prototype as NamedDialog;\n const update = target.update;\n target.update = function (this: NamedDialog, changed: unknown): void {\n update.call(this, changed);\n if (!this.parentElement?.classList.contains('kui-dialog-surface')) return;\n if (!observed.has(this)) {\n observed.add(this);\n new MutationObserver(() => syncDialogLabel(this)).observe(this, {\n attributes: true,\n attributeFilter: ['aria-label'],\n });\n }\n syncDialogLabel(this);\n };\n}\n\nfunction syncDialogLabel(host: NamedDialog): void {\n if (!host.parentElement?.classList.contains('kui-dialog-surface')) return;\n const panel = host.dialog;\n if (!panel) return;\n const previous = ownedNames.get(panel);\n const externalName = ['aria-label', 'aria-labelledby'].some((attribute) => {\n const value = panel.getAttribute(attribute);\n return (\n value !== null &&\n !(previous?.attribute === attribute && previous.value === value)\n );\n });\n if (externalName) {\n if (previous && panel.getAttribute(previous.attribute) === previous.value)\n panel.removeAttribute(previous.attribute);\n ownedNames.delete(panel);\n return;\n }\n const explicit = host.getAttribute('aria-label')?.trim();\n const title = host.shadowRoot?.getElementById('title');\n const attribute =\n !explicit && !host.withoutHeader && title\n ? 'aria-labelledby'\n : 'aria-label';\n const value =\n attribute === 'aria-labelledby' ? 'title' : explicit || host.label;\n if (previous && previous.attribute !== attribute)\n panel.removeAttribute(previous.attribute);\n if (panel.getAttribute(attribute) !== value)\n panel.setAttribute(attribute, value);\n ownedNames.set(panel, { attribute, value });\n}\n","import WaDialog from '@awesome.me/webawesome/dist/components/dialog/dialog.js';\n\nimport { installDialogLabel } from '../internal/install-dialog-label.js';\n\ninstallDialogLabel(WaDialog.prototype);\n\n/** Importing this module registers wa-dialog and repairs wrapped native names. */\nexport const surfaceScaffoldElementsRegistered = true;\n"]}
@@ -1,21 +1,29 @@
1
1
  import * as kerfjs from 'kerfjs';
2
2
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3
+ import { CssLength } from './css-values.js';
3
4
 
4
5
  type DialogSurfaceSize = 'small' | 'medium' | 'large';
5
6
  type DialogSurfacePresentation = 'modal' | 'side-sheet' | 'fullscreen';
6
7
  type SurfaceInset = 'none' | 'compact' | 'comfortable';
8
+ type DialogSurfaceMaxHeight = CssLength | 'viewport';
7
9
  interface DialogSurfaceProps {
8
10
  children: KerfUiContent;
9
11
  size?: DialogSurfaceSize;
10
12
  presentation?: DialogSurfacePresentation;
13
+ /** Modal preferred width. Takes precedence over size, within the viewport cap. */
14
+ preferredWidth?: CssLength;
11
15
  bodyInset?: SurfaceInset;
12
16
  footerInset?: SurfaceInset;
17
+ /** Modal edge clearance. Omit to retain Web Awesome's default viewport cap. */
18
+ viewportGutter?: CssLength;
19
+ /** Modal height cap, bounded by the dynamic viewport minus both gutters. */
20
+ maxHeight?: DialogSurfaceMaxHeight;
13
21
  className?: string;
14
22
  /** Native named-slot assignment when composed inside a web component. */
15
23
  slot?: string;
16
24
  }
17
25
  /** Configure recurring Web Awesome dialog geometry without consumer ::part() CSS. */
18
- declare function DialogSurface({ children, size, presentation, bodyInset, footerInset, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
26
+ declare function DialogSurface({ children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
19
27
  type PopupSurfaceInset = 'standard' | 'compact' | 'list-zero';
20
28
  interface PopupSurfaceProps {
21
29
  children: KerfUiContent;
@@ -27,4 +35,4 @@ interface PopupSurfaceProps {
27
35
  /** Configure recurring Web Awesome dropdown-menu geometry without consumer ::part() CSS. */
28
36
  declare function PopupSurface({ children, inset, className, slot, }: PopupSurfaceProps): kerfjs.SafeHtml;
29
37
 
30
- export { DialogSurface, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceSize, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset };
38
+ export { DialogSurface, type DialogSurfaceMaxHeight, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceSize, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset };
@@ -1,3 +1,3 @@
1
- export { DialogSurface, PopupSurface } from './chunk-XIZRXI7N.js';
1
+ export { DialogSurface, PopupSurface } from './chunk-A7DYEERU.js';
2
2
 
3
3
  //# sourceMappingURL=surface-scaffold.js.map
@@ -11,6 +11,8 @@ interface TabScaffoldTabBase<Id extends string> {
11
11
  content: KerfUiContent;
12
12
  /** Background of this scene's scrolling work surface. */
13
13
  appearance?: PaneAppearance;
14
+ /** Add an extra 8px inline gutter to a plain scene's scrolling content. */
15
+ deepInset?: boolean;
14
16
  }
15
17
  /** A tab with an optional count or short-status badge. */
16
18
  interface TabScaffoldTabCountBadge {
@@ -27,6 +27,7 @@ function TabScaffold({
27
27
  "data-tab-scaffold-scene": tab.id,
28
28
  "data-active": String(tab.id === active),
29
29
  "data-appearance": tab.appearance === "sunken" ? "sunken" : void 0,
30
+ "data-deep-inset": tab.deepInset ? "true" : void 0,
30
31
  "aria-hidden": String(tab.id !== active),
31
32
  children: tab.content
32
33
  }