@slotted/styles 0.1.0-beta.1 → 0.1.0-beta.11

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 (180) hide show
  1. package/package.json +1 -1
  2. package/src/accordion/accordion.css +10 -10
  3. package/src/action-bar/action-bar.css +35 -42
  4. package/src/action-sheet/action-sheet.css +36 -36
  5. package/src/alert/alert.css +43 -35
  6. package/src/alert/alert.tokens.json +5 -0
  7. package/src/app-shell/app-shell.css +356 -301
  8. package/src/aspect-ratio/aspect-ratio.css +9 -9
  9. package/src/attachment/attachment.css +91 -99
  10. package/src/avatar/avatar.css +71 -41
  11. package/src/avatar/avatar.tokens.json +7 -0
  12. package/src/avatar-group/avatar-group.css +21 -22
  13. package/src/badge/badge.css +45 -37
  14. package/src/badge/badge.tokens.json +5 -0
  15. package/src/breadcrumb/breadcrumb.css +37 -37
  16. package/src/button/button.css +149 -68
  17. package/src/button/button.tokens.json +10 -0
  18. package/src/button-group/button-group.css +79 -74
  19. package/src/button-group/button-group.tokens.json +2 -0
  20. package/src/calendar/calendar.css +93 -103
  21. package/src/card/card.css +54 -61
  22. package/src/carousel/carousel.css +150 -161
  23. package/src/cascader/cascader.css +152 -153
  24. package/src/cascader/cascader.tokens.json +1 -0
  25. package/src/chart/chart.css +242 -239
  26. package/src/chart/chart.tokens.json +1 -0
  27. package/src/checkbox/checkbox.css +119 -124
  28. package/src/checkbox-group/checkbox-group.css +7 -7
  29. package/src/code-block/code-block.css +81 -85
  30. package/src/collapsible/collapsible.css +69 -76
  31. package/src/color-picker/color-picker.css +83 -91
  32. package/src/column-manager/column-manager.css +106 -114
  33. package/src/combobox/combobox.css +132 -107
  34. package/src/combobox/combobox.tokens.json +1 -0
  35. package/src/command-palette/command-palette.css +103 -90
  36. package/src/command-palette/command-palette.tokens.json +1 -0
  37. package/src/container/container.css +24 -24
  38. package/src/context-menu/context-menu.css +8 -8
  39. package/src/currency-field/currency-field.css +210 -88
  40. package/src/currency-field/currency-field.tokens.json +13 -0
  41. package/src/dashboard-grid/dashboard-grid.css +148 -116
  42. package/src/data-grid/data-grid.css +535 -470
  43. package/src/data-grid/data-grid.tokens.json +4 -0
  44. package/src/data-list/data-list.css +81 -70
  45. package/src/data-list/data-list.tokens.json +2 -1
  46. package/src/date-field/date-field.css +55 -52
  47. package/src/date-field/date-field.tokens.json +1 -0
  48. package/src/date-picker/date-picker.css +40 -42
  49. package/src/date-range-picker/date-range-picker.css +114 -118
  50. package/src/datetime-field/datetime-field.css +78 -80
  51. package/src/datetime-field/datetime-field.tokens.json +1 -0
  52. package/src/datetime-picker/datetime-picker.css +61 -63
  53. package/src/datetime-range-picker/datetime-range-picker.css +149 -156
  54. package/src/description-list/description-list.css +61 -64
  55. package/src/detail-panel/detail-panel.css +86 -71
  56. package/src/detail-panel/detail-panel.tokens.json +2 -0
  57. package/src/dialog/dialog.css +372 -153
  58. package/src/dialog/dialog.tokens.json +6 -0
  59. package/src/divider/divider.css +18 -18
  60. package/src/drawer/drawer.css +205 -210
  61. package/src/drawer/drawer.tokens.json +1 -0
  62. package/src/entity-picker/entity-picker.css +238 -241
  63. package/src/entity-picker/entity-picker.tokens.json +1 -0
  64. package/src/event-calendar/event-calendar.css +343 -385
  65. package/src/field/field.css +74 -78
  66. package/src/field-group/field-group.css +22 -25
  67. package/src/filter-bar/filter-bar.css +193 -209
  68. package/src/form/form.css +34 -24
  69. package/src/form/form.tokens.json +1 -0
  70. package/src/fullscreen/fullscreen.css +24 -25
  71. package/src/grid/grid.css +28 -28
  72. package/src/highlight/highlight.css +6 -6
  73. package/src/hover-card/hover-card.css +6 -6
  74. package/src/icon/icon.css +19 -15
  75. package/src/icon/icon.tokens.json +1 -0
  76. package/src/image/image.css +39 -39
  77. package/src/import-export-center/import-export-center.css +135 -156
  78. package/src/inline/inline.css +27 -27
  79. package/src/inline-message/inline-message.css +52 -28
  80. package/src/inline-message/inline-message.tokens.json +4 -0
  81. package/src/kbd/kbd.css +30 -26
  82. package/src/link/link.css +15 -15
  83. package/src/list/list.css +44 -17
  84. package/src/list/list.tokens.json +4 -0
  85. package/src/list-item/list-item.css +80 -66
  86. package/src/list-item/list-item.tokens.json +7 -0
  87. package/src/listbox/listbox.css +56 -59
  88. package/src/loading-overlay/loading-overlay.css +75 -92
  89. package/src/markdown/markdown.css +17 -17
  90. package/src/menu/menu.css +83 -63
  91. package/src/menubar/menubar.css +13 -13
  92. package/src/message/message.css +182 -202
  93. package/src/message/message.tokens.json +5 -0
  94. package/src/message-scroller/message-scroller.css +41 -42
  95. package/src/multi-select/multi-select.css +152 -145
  96. package/src/multi-select/multi-select.tokens.json +1 -0
  97. package/src/navigation-menu/navigation-menu.css +167 -188
  98. package/src/notification-center/notification-center.css +140 -148
  99. package/src/notification-center/notification-center.tokens.json +3 -0
  100. package/src/number-field/number-field.css +84 -83
  101. package/src/number-field/number-field.tokens.json +1 -0
  102. package/src/otp-input/otp-input.css +67 -57
  103. package/src/otp-input/otp-input.tokens.json +1 -0
  104. package/src/page/page.css +39 -23
  105. package/src/page/page.tokens.json +1 -0
  106. package/src/page-header/page-header.css +323 -283
  107. package/src/page-header/page-header.tokens.json +5 -0
  108. package/src/pagination/pagination.css +68 -72
  109. package/src/popover/popover.css +40 -49
  110. package/src/progress-indicator/progress-indicator.css +177 -120
  111. package/src/progress-indicator/progress-indicator.tokens.json +1 -1
  112. package/src/prompt-composer/prompt-composer.css +221 -102
  113. package/src/prompt-composer/prompt-composer.tokens.json +15 -0
  114. package/src/prose/prose.css +71 -75
  115. package/src/radio-group/radio-group.css +98 -103
  116. package/src/rating/rating.css +136 -132
  117. package/src/rating/rating.tokens.json +1 -0
  118. package/src/result-state/result-state.css +74 -77
  119. package/src/runtime-properties.mjs +2 -0
  120. package/src/saved-views/saved-views.css +41 -47
  121. package/src/scroll-area/scroll-area.css +39 -32
  122. package/src/section/section.css +161 -41
  123. package/src/section/section.tokens.json +7 -2
  124. package/src/segmented-control/segmented-control.css +254 -97
  125. package/src/segmented-control/segmented-control.tokens.json +15 -11
  126. package/src/select/select.css +118 -117
  127. package/src/select/select.tokens.json +1 -0
  128. package/src/side-navigation/side-navigation.css +179 -182
  129. package/src/side-navigation/side-navigation.tokens.json +1 -0
  130. package/src/skeleton/skeleton.css +47 -48
  131. package/src/slider/slider.css +86 -88
  132. package/src/sparkline/sparkline.css +62 -40
  133. package/src/sparkline/sparkline.tokens.json +2 -0
  134. package/src/spinner/spinner.css +83 -180
  135. package/src/spinner/spinner.tokens.json +0 -1
  136. package/src/split-button/split-button.css +30 -31
  137. package/src/split-button/split-button.tokens.json +1 -0
  138. package/src/splitter/splitter.css +35 -35
  139. package/src/stack/stack.css +24 -24
  140. package/src/stat-card/stat-card.css +341 -224
  141. package/src/stat-card/stat-card.tokens.json +3 -0
  142. package/src/status-indicator/status-indicator.css +28 -28
  143. package/src/stepper/stepper.css +58 -62
  144. package/src/switch/switch.css +86 -92
  145. package/src/table/table.css +310 -165
  146. package/src/table/table.tokens.json +6 -0
  147. package/src/table-of-contents/table-of-contents.css +51 -54
  148. package/src/tabs/tabs.css +337 -302
  149. package/src/tabs/tabs.tokens.json +8 -5
  150. package/src/tag/tag.css +79 -71
  151. package/src/tag/tag.tokens.json +9 -0
  152. package/src/tags-input/tags-input.css +145 -82
  153. package/src/tags-input/tags-input.tokens.json +7 -1
  154. package/src/text-field/text-field.css +132 -136
  155. package/src/text-field/text-field.tokens.json +1 -0
  156. package/src/textarea/textarea.css +31 -28
  157. package/src/textarea/textarea.tokens.json +1 -0
  158. package/src/time-field/time-field.css +72 -69
  159. package/src/time-field/time-field.tokens.json +1 -0
  160. package/src/time-picker/time-picker.css +56 -58
  161. package/src/time-range-picker/time-range-picker.css +126 -133
  162. package/src/timeline/timeline.css +254 -266
  163. package/src/timeline/timeline.tokens.json +5 -0
  164. package/src/toast/toast.css +108 -90
  165. package/src/toast/toast.tokens.json +6 -0
  166. package/src/toggle/toggle.css +62 -25
  167. package/src/toggle/toggle.tokens.json +8 -0
  168. package/src/toggle-group/toggle-group.css +106 -60
  169. package/src/toggle-group/toggle-group.tokens.json +10 -0
  170. package/src/toolbar/toolbar.css +30 -35
  171. package/src/tooltip/tooltip.css +26 -26
  172. package/src/top-navigation/top-navigation.css +243 -259
  173. package/src/tour/tour.css +51 -60
  174. package/src/transfer/transfer.css +160 -164
  175. package/src/tree/tree.css +103 -111
  176. package/src/typography/typography.css +40 -36
  177. package/src/typography/typography.tokens.json +1 -0
  178. package/src/upload/upload.css +224 -224
  179. package/src/visually-hidden/visually-hidden.css +1 -1
  180. package/src/wizard/wizard.css +185 -197
@@ -36,7 +36,7 @@
36
36
  * description-list has a `description`, a card has `actions` and a `header`
37
37
  * — and a descendant rule would restyle theirs.
38
38
  */
39
- [data-ui='page-header'] {
39
+ [data-ui-page-header] {
40
40
  --_surface: var(--slotted-page-header-background, var(--slotted-surface, Canvas));
41
41
  /* What the root and the title area paint; `plain` takes it away at rest. */
42
42
  --_ground: var(--_surface);
@@ -77,6 +77,14 @@
77
77
  --_rule-size: var(--slotted-border-width-thin, 1px);
78
78
  --_duration: var(--slotted-duration-medium);
79
79
  --_easing: var(--slotted-easing-standard, cubic-bezier(0.2, 0, 0, 1));
80
+ /**
81
+ * How the ground changes when `plain` takes it back, or a material takes
82
+ * over, as content passes under the chrome. A duration of nought makes the
83
+ * change instant.
84
+ */
85
+ --_ground-transition: background-color
86
+ var(--slotted-page-header-ground-duration, var(--_duration))
87
+ var(--slotted-page-header-ground-easing, var(--_easing));
80
88
 
81
89
  background-color: var(--_ground);
82
90
  color: var(--_text);
@@ -98,7 +106,9 @@
98
106
  * is the slide of the header area under the title and never a fight with
99
107
  * the scroll, during which the offset is constant.
100
108
  */
101
- transition: inset-block-start var(--_duration) var(--_easing);
109
+ transition:
110
+ inset-block-start var(--_duration) var(--_easing),
111
+ var(--_ground-transition);
102
112
  /**
103
113
  * Above every other sticky thing on the page — a section's header, a
104
114
  * table's column header, a pinned corner — so what a short section pushes
@@ -117,7 +127,7 @@
117
127
  overflow-x: clip;
118
128
  }
119
129
 
120
- [data-ui='page-header'][data-size='sm'] {
130
+ [data-ui-page-header][data-size='sm'] {
121
131
  --_heading-size: var(
122
132
  --slotted-page-header-heading-size-sm,
123
133
  var(--slotted-font-size-xl, 1.125rem)
@@ -130,7 +140,7 @@
130
140
  --_snapped-heading-line-height: var(--slotted-line-height-md, 1.5);
131
141
  }
132
142
 
133
- [data-ui='page-header'][data-size='lg'] {
143
+ [data-ui-page-header][data-size='lg'] {
134
144
  --_heading-size: var(
135
145
  --slotted-page-header-heading-size-lg,
136
146
  var(--slotted-font-size-3xl, 1.5rem)
@@ -150,9 +160,9 @@
150
160
  * and `pressed` is its word. Under `collapse="manual"` the offset is what
151
161
  * the scroll would use, so it is zero for good.
152
162
  */
153
- [data-ui='page-header'][data-revealed],
154
- [data-ui='page-header'][data-collapse='manual'],
155
- [data-ui='page-header']:has([data-part='pin'][data-pressed]) {
163
+ [data-ui-page-header][data-revealed],
164
+ [data-ui-page-header][data-collapse='manual'],
165
+ [data-ui-page-header]:has([data-part-pin][data-pressed]) {
156
166
  inset-block-start: var(--_sticky-inset);
157
167
  }
158
168
 
@@ -166,7 +176,7 @@
166
176
  * ground would show the page through its title. The hairline stays in
167
177
  * both: it is where the chrome ends.
168
178
  */
169
- [data-ui='page-header'][data-variant='plain']:not([data-stuck]) {
179
+ [data-ui-page-header][data-variant='plain']:not([data-stuck]) {
170
180
  --_ground: transparent;
171
181
  }
172
182
 
@@ -179,19 +189,21 @@
179
189
  * sat an inset down over the content under it. The title area paints its
180
190
  * own ground, so its top corners follow the root's, and it slides with it.
181
191
  */
182
- [data-ui='page-header'][data-variant='floating'] {
192
+ [data-ui-page-header][data-variant='floating'] {
183
193
  --_radius: var(--slotted-page-header-radius, var(--slotted-radius-lg, 8px));
184
194
 
185
195
  border: var(--_rule-size) solid var(--_rule);
186
196
  border-radius: var(--_radius);
187
197
  box-sizing: border-box;
188
198
  }
189
- [data-ui='page-header'][data-variant='floating'] > * > [data-part='title-area'] {
199
+ [data-ui-page-header][data-variant='floating'] > * > [data-part-title-area] {
190
200
  border-start-end-radius: calc(var(--_radius) - var(--_rule-size));
191
201
  border-start-start-radius: calc(var(--_radius) - var(--_rule-size));
192
- transition: inset-block-start var(--_duration) var(--_easing);
202
+ transition:
203
+ inset-block-start var(--_duration) var(--_easing),
204
+ var(--_ground-transition);
193
205
  }
194
- [data-ui='page-header'][data-variant='floating'][data-stuck] {
206
+ [data-ui-page-header][data-variant='floating'][data-stuck] {
195
207
  --_sticky-inset: var(--slotted-page-header-inset, var(--slotted-space-4, 0.5rem));
196
208
 
197
209
  box-shadow: var(--slotted-page-header-shadow, var(--slotted-shadow-sm));
@@ -203,14 +215,14 @@
203
215
  * record's header and tabs held 40 per cent of the screen for the whole
204
216
  * scroll. `relative` and not `static`, so the root keeps the stacking the
205
217
  * pill straddling its edge relies on, at an offset of nought. The binding
206
- * writes the attribute from the root's own width: a container query cannot
207
- * select the container it measures.
218
+ * writes the attribute from the frame's width, so a header in a narrow pane
219
+ * of a wide frame still sticks inside the pane.
208
220
  */
209
- [data-ui='page-header'][data-compact] {
221
+ [data-ui-page-header][data-compact] {
210
222
  inset-block-start: 0;
211
223
  position: relative;
212
224
  }
213
- [data-ui='page-header'][data-compact] > * > [data-part='title-area'] {
225
+ [data-ui-page-header][data-compact] > * > [data-part-title-area] {
214
226
  inset-block-start: 0;
215
227
  position: relative;
216
228
  }
@@ -222,7 +234,7 @@
222
234
  * renders to align its rows to the page's measure, and it is not named,
223
235
  * because a family does not select another family's identity.
224
236
  */
225
- [data-ui='page-header'] > * > [data-part='title-area'] {
237
+ [data-ui-page-header] > * > [data-part-title-area] {
226
238
  background-color: var(--_ground);
227
239
  display: flex;
228
240
  flex-direction: column;
@@ -231,9 +243,61 @@
231
243
  overflow-anchor: none;
232
244
  padding-block: var(--_padding-block) var(--slotted-space-4, 0.5rem);
233
245
  position: sticky;
246
+ transition: var(--_ground-transition);
234
247
  z-index: 2;
235
248
  }
236
249
 
250
+ /**
251
+ * The title area paints the ground only where something passes under it:
252
+ * the header area, which it covers while it collapses. Without one, the
253
+ * root's ground is the one layer under the title, so a ground with alpha is
254
+ * composited once — painted on both, 0.5 read as 0.75. With a header area
255
+ * the title keeps its own ground, because a title that let the area show
256
+ * through would print the area's words under its own.
257
+ */
258
+ [data-ui-page-header]:not(:has(> * > [data-part-header-area])) > * > [data-part-title-area] {
259
+ background-color: transparent;
260
+ }
261
+
262
+ /**
263
+ * Stuck, the chrome stands over the content, and a theme may blur what passes
264
+ * under it: `--slotted-page-header-backdrop-filter`, `none` unless set. It
265
+ * composes with a ground that has alpha, which is what lets the content show
266
+ * at all. The filter is the stuck state's alone, so a `plain` header,
267
+ * transparent at rest, filters nothing at rest.
268
+ */
269
+ [data-ui-page-header][data-stuck] {
270
+ -webkit-backdrop-filter: var(--slotted-page-header-backdrop-filter, var(--_backdrop, none));
271
+ backdrop-filter: var(--slotted-page-header-backdrop-filter, var(--_backdrop, none));
272
+ }
273
+
274
+ /**
275
+ * `material`: stuck, the chrome is the theme's material — the ground
276
+ * `--slotted-surface-material` and the filter `--slotted-material-backdrop`
277
+ * — in every variant. At rest each variant keeps its own ground. The family
278
+ * tokens still win, and the pill keeps the opaque surface, because half of
279
+ * it hangs below the root over content the root's filter does not reach.
280
+ * The theme makes both material tokens opaque under reduced transparency.
281
+ */
282
+ [data-ui-page-header][data-material][data-stuck] {
283
+ --_ground: var(
284
+ --slotted-page-header-background,
285
+ var(--slotted-surface-material, var(--_surface))
286
+ );
287
+ --_backdrop: var(--slotted-material-backdrop, none);
288
+ }
289
+
290
+ /**
291
+ * A reader who asked for less transparency gets none of the effect, and under
292
+ * forced colours the ground is the system's and the blur has nothing to add.
293
+ */
294
+ @media (prefers-reduced-transparency: reduce), (forced-colors: active) {
295
+ [data-ui-page-header][data-stuck] {
296
+ -webkit-backdrop-filter: none;
297
+ backdrop-filter: none;
298
+ }
299
+ }
300
+
237
301
  /**
238
302
  * The pill straddles the chrome's edge, so whatever ends at that edge
239
303
  * keeps half a pill clear of it: the title area, because stuck the edge
@@ -242,11 +306,11 @@
242
306
  * because a padding that changed at the snap would move the tabs by that
243
307
  * much at the moment the header area left.
244
308
  */
245
- [data-ui='page-header']:has([data-part='controls'] > *) > * > [data-part='title-area'] {
309
+ [data-ui-page-header]:has([data-part-controls] > *) > * > [data-part-title-area] {
246
310
  padding-block-end: calc(var(--slotted-space-4, 0.5rem) + var(--_control-size) / 2);
247
311
  }
248
312
 
249
- [data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav'] {
313
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-nav] {
250
314
  align-items: center;
251
315
  color: var(--_muted);
252
316
  display: flex;
@@ -255,7 +319,7 @@
255
319
  line-height: var(--slotted-line-height-sm, 1.45);
256
320
  min-inline-size: 0;
257
321
  }
258
- [data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav']:empty {
322
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-nav]:empty {
259
323
  display: none;
260
324
  }
261
325
  /**
@@ -264,11 +328,11 @@
264
328
  * breadcrumbs go and the arrow is what remains. A consumer writes both
265
329
  * once and the width decides.
266
330
  */
267
- [data-ui='page-header']
331
+ [data-ui-page-header]
268
332
  > *
269
- > [data-part='title-area']
270
- > [data-part='nav']
271
- > [data-part='back']:has(~ [data-part='breadcrumbs']:not(:empty)) {
333
+ > [data-part-title-area]
334
+ > [data-part-nav]
335
+ > [data-part-back]:has(~ [data-part-breadcrumbs]:not(:empty)) {
272
336
  display: none;
273
337
  }
274
338
 
@@ -278,7 +342,7 @@
278
342
  * label, and it points along the reading direction, so its `directional`
279
343
  * box mirrors it in a right-to-left document.
280
344
  */
281
- [data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav'] > [data-part='back'] {
345
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-nav] > [data-part-back] {
282
346
  --slotted-icon-size: var(--_icon-size);
283
347
 
284
348
  align-items: center;
@@ -302,11 +366,7 @@
302
366
  reaches the comfortable size by padding and gives it back as a negative
303
367
  margin, so the nav row keeps its height and nothing under it moves. */
304
368
  @media (pointer: coarse) {
305
- [data-ui='page-header']
306
- > *
307
- > [data-part='title-area']
308
- > [data-part='nav']
309
- > [data-part='back'] {
369
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-nav] > [data-part-back] {
310
370
  --_reach: max(
311
371
  0px,
312
372
  calc((var(--slotted-target-comfortable, 44px) - var(--slotted-target-min, 24px)) / 2)
@@ -316,27 +376,23 @@
316
376
  padding-block: var(--_reach);
317
377
  }
318
378
  }
319
- [data-ui='page-header']
379
+ [data-ui-page-header]
320
380
  > *
321
- > [data-part='title-area']
322
- > [data-part='nav']
323
- > [data-part='back']:not([data-disabled]):not(:disabled):hover {
381
+ > [data-part-title-area]
382
+ > [data-part-nav]
383
+ > [data-part-back]:not([data-disabled]):not(:disabled):hover {
324
384
  color: var(--_text);
325
385
  }
326
- [data-ui='page-header']
386
+ [data-ui-page-header]
327
387
  > *
328
- > [data-part='title-area']
329
- > [data-part='nav']
330
- > [data-part='back']:focus-visible {
388
+ > [data-part-title-area]
389
+ > [data-part-nav]
390
+ > [data-part-back]:focus-visible {
331
391
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
332
392
  outline-offset: var(--slotted-focus-ring-offset, 2px);
333
393
  }
334
394
 
335
- [data-ui='page-header']
336
- > *
337
- > [data-part='title-area']
338
- > [data-part='nav']
339
- > [data-part='breadcrumbs'] {
395
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-nav] > [data-part-breadcrumbs] {
340
396
  min-inline-size: 0;
341
397
  }
342
398
 
@@ -345,7 +401,7 @@
345
401
  * navigation pushed to the end. Everything after the title wraps under it
346
402
  * when the width runs out, in the order it has in the DOM.
347
403
  */
348
- [data-ui='page-header'] > * > [data-part='title-area'] > [data-part='title-row'] {
404
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-title-row] {
349
405
  align-items: flex-start;
350
406
  display: flex;
351
407
  flex-wrap: wrap;
@@ -353,11 +409,7 @@
353
409
  min-inline-size: 0;
354
410
  }
355
411
 
356
- [data-ui='page-header']
357
- > *
358
- > [data-part='title-area']
359
- > [data-part='title-row']
360
- > [data-part='visual'] {
412
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-title-row] > [data-part-visual] {
361
413
  align-items: center;
362
414
  /* On the title's first line, as Primer's LeadingVisual: centred on the
363
415
  whole column it sank beside the second line of a wrapped title. */
@@ -372,11 +424,7 @@
372
424
  * aligned to the text" asks for — and the subheading, forced to a full
373
425
  * row, takes the line under it.
374
426
  */
375
- [data-ui='page-header']
376
- > *
377
- > [data-part='title-area']
378
- > [data-part='title-row']
379
- > [data-part='title'] {
427
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-title-row] > [data-part-title] {
380
428
  align-items: center;
381
429
  column-gap: var(--slotted-space-4, 0.5rem);
382
430
  display: flex;
@@ -403,12 +451,12 @@
403
451
  * width to the flex algorithm that is not the width it then paints, and the
404
452
  * marks beside it ran past the container in the first capture.
405
453
  */
406
- [data-ui='page-header']
454
+ [data-ui-page-header]
407
455
  > *
408
- > [data-part='title-area']
409
- > [data-part='title-row']
410
- > [data-part='title']
411
- > [data-part='heading'] {
456
+ > [data-part-title-area]
457
+ > [data-part-title-row]
458
+ > [data-part-title]
459
+ > [data-part-heading] {
412
460
  -webkit-box-orient: vertical;
413
461
  color: var(--_text);
414
462
  display: -webkit-box;
@@ -429,12 +477,12 @@
429
477
  /* No transition on the size: text re-rasterised on every frame of a
430
478
  font-size change blurs and settles with a jolt, so the step is a cut. */
431
479
  }
432
- [data-ui='page-header']:not([data-expanded])
480
+ [data-ui-page-header]:not([data-expanded])
433
481
  > *
434
- > [data-part='title-area']
435
- > [data-part='title-row']
436
- > [data-part='title']
437
- > [data-part='heading'] {
482
+ > [data-part-title-area]
483
+ > [data-part-title-row]
484
+ > [data-part-title]
485
+ > [data-part-heading] {
438
486
  display: block;
439
487
  flex: 0 1 auto;
440
488
  font-size: var(--_snapped-heading-size);
@@ -443,33 +491,33 @@
443
491
  white-space: nowrap;
444
492
  }
445
493
 
446
- [data-ui='page-header']
494
+ [data-ui-page-header]
447
495
  > *
448
- > [data-part='title-area']
449
- > [data-part='title-row']
450
- > [data-part='title']
451
- > [data-part='marks'] {
496
+ > [data-part-title-area]
497
+ > [data-part-title-row]
498
+ > [data-part-title]
499
+ > [data-part-marks] {
452
500
  align-items: center;
453
501
  display: inline-flex;
454
502
  flex-wrap: wrap;
455
503
  gap: var(--slotted-space-2, 0.25rem);
456
504
  min-inline-size: 0;
457
505
  }
458
- [data-ui='page-header']
506
+ [data-ui-page-header]
459
507
  > *
460
- > [data-part='title-area']
461
- > [data-part='title-row']
462
- > [data-part='title']
463
- > [data-part='marks']:empty {
508
+ > [data-part-title-area]
509
+ > [data-part-title-row]
510
+ > [data-part-title]
511
+ > [data-part-marks]:empty {
464
512
  display: none;
465
513
  }
466
514
 
467
- [data-ui='page-header']
515
+ [data-ui-page-header]
468
516
  > *
469
- > [data-part='title-area']
470
- > [data-part='title-row']
471
- > [data-part='title']
472
- > [data-part='subheading'] {
517
+ > [data-part-title-area]
518
+ > [data-part-title-row]
519
+ > [data-part-title]
520
+ > [data-part-subheading] {
473
521
  -webkit-box-orient: vertical;
474
522
  color: var(--_muted);
475
523
  display: -webkit-box;
@@ -485,23 +533,19 @@
485
533
  text-wrap: pretty;
486
534
  }
487
535
  /* Snapped, the bar has one line to give it. */
488
- [data-ui='page-header']:not([data-expanded])
536
+ [data-ui-page-header]:not([data-expanded])
489
537
  > *
490
- > [data-part='title-area']
491
- > [data-part='title-row']
492
- > [data-part='title']
493
- > [data-part='subheading'] {
538
+ > [data-part-title-area]
539
+ > [data-part-title-row]
540
+ > [data-part-title]
541
+ > [data-part-subheading] {
494
542
  display: block;
495
543
  overflow: hidden;
496
544
  text-overflow: ellipsis;
497
545
  white-space: nowrap;
498
546
  }
499
547
 
500
- [data-ui='page-header']
501
- > *
502
- > [data-part='title-area']
503
- > [data-part='title-row']
504
- > [data-part='actions'] {
548
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-title-row] > [data-part-actions] {
505
549
  align-items: center;
506
550
  display: flex;
507
551
  flex: 0 1 auto;
@@ -522,20 +566,20 @@
522
566
  * that scrolls — written from the same public tokens; a framed bar keeps
523
567
  * its box.
524
568
  */
525
- [data-ui='page-header']
569
+ [data-ui-page-header]
526
570
  > *
527
- > [data-part='title-area']
528
- > [data-part='title-row']
529
- > [data-part='actions']
530
- > [data-ui='toolbar']:not([data-variant='outline']) {
571
+ > [data-part-title-area]
572
+ > [data-part-title-row]
573
+ > [data-part-actions]
574
+ > [data-ui-toolbar]:not([data-variant='outline']) {
531
575
  margin-block: calc(-1 * var(--slotted-toolbar-inset, var(--slotted-space-2, 0.25rem)));
532
576
  }
533
- [data-ui='page-header']
577
+ [data-ui-page-header]
534
578
  > *
535
- > [data-part='title-area']
536
- > [data-part='title-row']
537
- > [data-part='actions']
538
- > [data-ui='toolbar']:not([data-variant='outline']):is(
579
+ > [data-part-title-area]
580
+ > [data-part-title-row]
581
+ > [data-part-actions]
582
+ > [data-ui-toolbar]:not([data-variant='outline']):is(
539
583
  [data-overflow='menu'],
540
584
  [data-overflow='scroll']
541
585
  ) {
@@ -564,12 +608,10 @@
564
608
  * its observer never fired, and the actions stayed folded after the window
565
609
  * widened again, while a fresh load at the same width showed them all.
566
610
  */
567
- [data-ui='page-header']
611
+ [data-ui-page-header]
568
612
  > *
569
- > [data-part='title-area']
570
- > [data-part='title-row']:has(
571
- > [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
572
- ) {
613
+ > [data-part-title-area]
614
+ > [data-part-title-row]:has(> [data-part-actions] > [data-ui-toolbar][data-overflow='menu']) {
573
615
  flex-wrap: nowrap;
574
616
  }
575
617
  /* The title starts from its own width, not from the basis a wrapping row gives it, at every
@@ -580,48 +622,42 @@
580
622
  of 420, and the toolbar beside them folded every command into its menu next to empty room. As
581
623
  a grid — the heading and its marks on the first line, the subheading across both columns
582
624
  under them — the title is as wide as its widest line. */
583
- [data-ui='page-header']
625
+ [data-ui-page-header]
584
626
  > *
585
- > [data-part='title-area']
586
- > [data-part='title-row']:has(
587
- > [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
588
- )
589
- > [data-part='title'] {
627
+ > [data-part-title-area]
628
+ > [data-part-title-row]:has(> [data-part-actions] > [data-ui-toolbar][data-overflow='menu'])
629
+ > [data-part-title] {
590
630
  align-items: center;
591
631
  display: grid;
592
632
  flex: 0 1 auto;
593
633
  grid-template-columns: minmax(0, auto) auto;
594
634
  justify-content: start;
595
635
  }
596
- [data-ui='page-header']
636
+ [data-ui-page-header]
597
637
  > *
598
- > [data-part='title-area']
599
- > [data-part='title-row']:has(
600
- > [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
601
- )
602
- > [data-part='title']
603
- > [data-part='heading'] {
638
+ > [data-part-title-area]
639
+ > [data-part-title-row]:has(> [data-part-actions] > [data-ui-toolbar][data-overflow='menu'])
640
+ > [data-part-title]
641
+ > [data-part-heading] {
604
642
  grid-column: 1;
605
643
  }
606
- [data-ui='page-header']
644
+ [data-ui-page-header]
607
645
  > *
608
- > [data-part='title-area']
609
- > [data-part='title-row']:has(
610
- > [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
611
- )
612
- > [data-part='title']
613
- > [data-part='subheading'] {
646
+ > [data-part-title-area]
647
+ > [data-part-title-row]:has(> [data-part-actions] > [data-ui-toolbar][data-overflow='menu'])
648
+ > [data-part-title]
649
+ > [data-part-subheading] {
614
650
  grid-column: 1 / -1;
615
651
  }
616
652
  /* The cluster starts from nothing and grows into what the title leaves, so its width follows
617
653
  the row's in both directions. It never gives way below its menu's trigger, a medium control,
618
654
  and the toolbar's inset on either side: under it the trigger stood past the screen's edge on
619
655
  a phone. */
620
- [data-ui='page-header']
656
+ [data-ui-page-header]
621
657
  > *
622
- > [data-part='title-area']
623
- > [data-part='title-row']
624
- > [data-part='actions']:has(> [data-ui='toolbar'][data-overflow='menu']) {
658
+ > [data-part-title-area]
659
+ > [data-part-title-row]
660
+ > [data-part-actions]:has(> [data-ui-toolbar][data-overflow='menu']) {
625
661
  flex: 1 1 0%;
626
662
  min-inline-size: calc(
627
663
  var(--slotted-control-height-md, 34px) + 2 *
@@ -638,12 +674,12 @@
638
674
  stand at the end of the row as they did when the cluster was their own width. `safe`, because
639
675
  a bar of pinned controls wider than the room scrolls, and overflow at the start of a scroll
640
676
  container cannot be scrolled to. */
641
- [data-ui='page-header']
677
+ [data-ui-page-header]
642
678
  > *
643
- > [data-part='title-area']
644
- > [data-part='title-row']
645
- > [data-part='actions']
646
- > [data-ui='toolbar'][data-overflow='menu'] {
679
+ > [data-part-title-area]
680
+ > [data-part-title-row]
681
+ > [data-part-actions]
682
+ > [data-ui-toolbar][data-overflow='menu'] {
647
683
  flex: 1 1 0%;
648
684
  justify-content: safe flex-end;
649
685
  }
@@ -652,11 +688,11 @@
652
688
  * Previous and next record, after the actions and set apart from them by a
653
689
  * rule, which is where sap.f puts navigationActions.
654
690
  */
655
- [data-ui='page-header']
691
+ [data-ui-page-header]
656
692
  > *
657
- > [data-part='title-area']
658
- > [data-part='title-row']
659
- > [data-part='navigation'] {
693
+ > [data-part-title-area]
694
+ > [data-part-title-row]
695
+ > [data-part-navigation] {
660
696
  align-items: center;
661
697
  border-inline-start: var(--_rule-size) solid var(--_rule);
662
698
  display: flex;
@@ -676,12 +712,12 @@
676
712
  * for the other direction. The glyphs mirror; the order is already the
677
713
  * row's.
678
714
  */
679
- [data-ui='page-header']
715
+ [data-ui-page-header]
680
716
  > *
681
- > [data-part='title-area']
682
- > [data-part='title-row']
683
- > [data-part='navigation']:dir(rtl)
684
- :is(svg, [data-ui='icon']) {
717
+ > [data-part-title-area]
718
+ > [data-part-title-row]
719
+ > [data-part-navigation]:dir(rtl)
720
+ :is(svg, [data-ui-icon]) {
685
721
  scale: -1 1;
686
722
  }
687
723
 
@@ -692,7 +728,7 @@
692
728
  * to 0fr — the content's own height at every frame, nothing measured — as
693
729
  * collapsible does. Figure and content share the row.
694
730
  */
695
- [data-ui='page-header'] > * > [data-part='header-area'] {
731
+ [data-ui-page-header] > * > [data-part-header-area] {
696
732
  display: grid;
697
733
  grid-template-columns: auto minmax(0, 1fr);
698
734
  grid-template-rows: 1fr;
@@ -702,10 +738,10 @@
702
738
  grid-template-rows var(--_duration) var(--_easing),
703
739
  opacity var(--_duration) var(--_easing);
704
740
  }
705
- [data-ui='page-header'] > * > [data-part='header-area']:not(:has([data-part='figure'])) {
741
+ [data-ui-page-header] > * > [data-part-header-area]:not(:has([data-part-figure])) {
706
742
  grid-template-columns: minmax(0, 1fr);
707
743
  }
708
- [data-ui='page-header'] > * > [data-part='header-area'] > * {
744
+ [data-ui-page-header] > * > [data-part-header-area] > * {
709
745
  min-block-size: 0;
710
746
  }
711
747
 
@@ -715,26 +751,24 @@
715
751
  * offset is what hides the area — a 0fr row there would shorten the root
716
752
  * under the reader and jump the scroll by the area's height.
717
753
  */
718
- [data-ui='page-header']:not([data-expanded]):not([data-scrolled])
719
- > *
720
- > [data-part='header-area'] {
754
+ [data-ui-page-header]:not([data-expanded]):not([data-scrolled]) > * > [data-part-header-area] {
721
755
  grid-template-rows: 0fr;
722
756
  opacity: 0;
723
757
  }
724
- [data-ui='page-header']:not([data-expanded]):not([data-scrolled])
758
+ [data-ui-page-header]:not([data-expanded]):not([data-scrolled])
725
759
  > *
726
- > [data-part='header-area']
760
+ > [data-part-header-area]
727
761
  > * {
728
762
  overflow: clip;
729
763
  }
730
764
 
731
- [data-ui='page-header'] > * > [data-part='header-area'] > [data-part='figure'] {
765
+ [data-ui-page-header] > * > [data-part-header-area] > [data-part-figure] {
732
766
  display: flex;
733
767
  padding-block-end: var(--_padding-block);
734
768
  padding-inline-end: var(--slotted-space-6, 1rem);
735
769
  }
736
770
 
737
- [data-ui='page-header'] > * > [data-part='header-area'] > [data-part='content'] {
771
+ [data-ui-page-header] > * > [data-part-header-area] > [data-part-content] {
738
772
  display: flex;
739
773
  flex-direction: column;
740
774
  gap: var(--_gap);
@@ -742,11 +776,11 @@
742
776
  padding-block-end: var(--_padding-block);
743
777
  }
744
778
 
745
- [data-ui='page-header']
779
+ [data-ui-page-header]
746
780
  > *
747
- > [data-part='header-area']
748
- > [data-part='content']
749
- > [data-part='description'] {
781
+ > [data-part-header-area]
782
+ > [data-part-content]
783
+ > [data-part-description] {
750
784
  -webkit-box-orient: vertical;
751
785
  color: var(--_muted);
752
786
  display: -webkit-box;
@@ -761,14 +795,14 @@
761
795
 
762
796
  /* The half pill the header area's columns keep clear of the edge; see the
763
797
  title area's reservation above. After the columns' own rules, so it wins. */
764
- [data-ui='page-header']:has([data-part='controls'] > *)
798
+ [data-ui-page-header]:has([data-part-controls] > *)
765
799
  > *
766
- > [data-part='header-area']
767
- > [data-part='figure'],
768
- [data-ui='page-header']:has([data-part='controls'] > *)
800
+ > [data-part-header-area]
801
+ > [data-part-figure],
802
+ [data-ui-page-header]:has([data-part-controls] > *)
769
803
  > *
770
- > [data-part='header-area']
771
- > [data-part='content'] {
804
+ > [data-part-header-area]
805
+ > [data-part-content] {
772
806
  padding-block-end: calc(var(--_padding-block) + var(--_control-size) / 2);
773
807
  }
774
808
 
@@ -777,9 +811,9 @@
777
811
  * reserved would keep the area 27 pixels tall: the reservation goes with
778
812
  * the content, and the pill straddles the title area's own reservation.
779
813
  */
780
- [data-ui='page-header']:not([data-expanded]):not([data-scrolled])
814
+ [data-ui-page-header]:not([data-expanded]):not([data-scrolled])
781
815
  > *
782
- > [data-part='header-area']
816
+ > [data-part-header-area]
783
817
  > * {
784
818
  padding-block: 0;
785
819
  }
@@ -797,7 +831,7 @@
797
831
  * The row is `relative` and the pill `absolute`, so the tabs under it lay
798
832
  * out as if it were not there; the pill overlaps their top edge.
799
833
  */
800
- [data-ui='page-header'] > * > [data-part='controls'] {
834
+ [data-ui-page-header] > * > [data-part-controls] {
801
835
  block-size: 0;
802
836
  position: relative;
803
837
  /* Above the title area, and above the wait drawn on the same edge. */
@@ -810,16 +844,14 @@
810
844
  * Without tabs the root's edge is where the pill sits, and no second rule
811
845
  * is drawn.
812
846
  */
813
- [data-ui='page-header']:has(> * > [data-part='tabs']:not(:empty))
814
- > *
815
- > [data-part='controls']::before {
847
+ [data-ui-page-header]:has(> * > [data-part-tabs]:not(:empty)) > * > [data-part-controls]::before {
816
848
  border-block-start: var(--_rule-size) solid var(--_rule);
817
849
  content: '';
818
850
  inset-block-start: 0;
819
851
  inset-inline: -50vw;
820
852
  position: absolute;
821
853
  }
822
- [data-ui='page-header'] > * > [data-part='controls'] > * {
854
+ [data-ui-page-header] > * > [data-part-controls] > * {
823
855
  /* The pill is a control on the hairline: it keeps the surface in every variant. */
824
856
  background-color: var(--_surface);
825
857
  border: var(--_rule-size) solid var(--_rule);
@@ -842,8 +874,8 @@
842
874
  * and nothing else does; press goes one step further; both hold back for a
843
875
  * disabled control.
844
876
  */
845
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle'],
846
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'] {
877
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle],
878
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin] {
847
879
  /* One step above the nav row's arrow: a mark on a hairline pill reads
848
880
  thin at the small step, and the reviewer asked for weight. The marks
849
881
  are the Glyph elements the bindings render — `chevron-down` in the
@@ -869,20 +901,15 @@
869
901
  background-color var(--slotted-duration-short) var(--_easing),
870
902
  color var(--slotted-duration-short) var(--_easing);
871
903
  }
872
- [data-ui='page-header']
873
- > *
874
- > [data-part='controls']
875
- > *
876
- > [data-part='toggle']
877
- > [data-ui='icon'] {
904
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle] > [data-ui-icon] {
878
905
  transition: rotate var(--slotted-duration-short) var(--_easing);
879
906
  }
880
- [data-ui='page-header'][data-expanded]
907
+ [data-ui-page-header][data-expanded]
881
908
  > *
882
- > [data-part='controls']
909
+ > [data-part-controls]
883
910
  > *
884
- > [data-part='toggle']
885
- > [data-ui='icon'] {
911
+ > [data-part-toggle]
912
+ > [data-ui-icon] {
886
913
  rotate: 180deg;
887
914
  }
888
915
  /**
@@ -892,54 +919,38 @@
892
919
  * read, so the pin is not offered — the binding writes the fact and lets a
893
920
  * held header go — and below 600 pixels the sheet hides it on its own.
894
921
  */
895
- [data-ui='page-header'][data-tall] > * > [data-part='controls'] > * > [data-part='pin'] {
922
+ [data-ui-page-header][data-tall] > * > [data-part-controls] > * > [data-part-pin] {
896
923
  display: none;
897
924
  }
898
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'][data-pressed] {
925
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin][data-pressed] {
899
926
  background-color: var(--slotted-tone-accent-surface, transparent);
900
927
  color: var(--slotted-tone-accent-text, LinkText);
901
928
  }
902
- [data-ui='page-header']
929
+ [data-ui-page-header]
903
930
  > *
904
- > [data-part='controls']
931
+ > [data-part-controls]
905
932
  > *
906
- > [data-part='pin'][data-pressed]:not(:disabled):hover {
933
+ > [data-part-pin][data-pressed]:not(:disabled):hover {
907
934
  background-color: var(--slotted-tone-accent-subtle-hover, ButtonFace);
908
935
  color: var(--slotted-tone-accent-text, LinkText);
909
936
  }
910
937
 
911
- [data-ui='page-header']
912
- > *
913
- > [data-part='controls']
914
- > *
915
- > [data-part='toggle']:not(:disabled):hover,
916
- [data-ui='page-header']
917
- > *
918
- > [data-part='controls']
919
- > *
920
- > [data-part='pin']:not(:disabled):hover {
938
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle]:not(:disabled):hover,
939
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin]:not(:disabled):hover {
921
940
  background-color: var(--slotted-tone-secondary-subtle-hover, ButtonFace);
922
941
  color: var(--_text);
923
942
  }
924
- [data-ui='page-header']
925
- > *
926
- > [data-part='controls']
927
- > *
928
- > [data-part='toggle']:not(:disabled):active,
929
- [data-ui='page-header']
930
- > *
931
- > [data-part='controls']
932
- > *
933
- > [data-part='pin']:not(:disabled):active {
943
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle]:not(:disabled):active,
944
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin]:not(:disabled):active {
934
945
  background-color: var(--slotted-tone-secondary-subtle-active, ButtonFace);
935
946
  }
936
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle']:focus-visible,
937
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin']:focus-visible {
947
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle]:focus-visible,
948
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin]:focus-visible {
938
949
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
939
950
  outline-offset: var(--slotted-focus-ring-offset, 2px);
940
951
  }
941
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle']:disabled,
942
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin']:disabled {
952
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-toggle]:disabled,
953
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin]:disabled {
943
954
  color: var(--slotted-disabled-foreground, GrayText);
944
955
  cursor: default;
945
956
  }
@@ -951,18 +962,18 @@
951
962
  * why it needs no sticky of its own. The pill above it needs the room its
952
963
  * lower half takes, so the slot starts half a control down.
953
964
  */
954
- [data-ui='page-header'] > * > [data-part='tabs'] {
965
+ [data-ui-page-header] > * > [data-part-tabs] {
955
966
  /* The root's bottom edge is the tabs' track: one line, the whole width. */
956
967
  --slotted-tabs-track-color: transparent;
957
968
 
958
969
  min-inline-size: 0;
959
970
  padding-block-start: calc(var(--_control-size) / 2 + var(--slotted-space-2, 0.25rem));
960
971
  }
961
- [data-ui='page-header'] > * > [data-part='tabs']:empty {
972
+ [data-ui-page-header] > * > [data-part-tabs]:empty {
962
973
  display: none;
963
974
  }
964
975
  /* Without a pill the tabs sit on the rule. */
965
- [data-ui='page-header'] > * > [data-part='controls']:empty + [data-part='tabs'] {
976
+ [data-ui-page-header] > * > [data-part-controls]:empty + [data-part-tabs] {
966
977
  padding-block-start: 0;
967
978
  }
968
979
 
@@ -979,13 +990,13 @@
979
990
  * well and comes later. It is honoured on parts, which is where a consumer
980
991
  * writes it.
981
992
  */
982
- [data-ui='page-header'][data-expanded]
993
+ [data-ui-page-header][data-expanded]
983
994
  > *
984
- > [data-part='title-area']
995
+ > [data-part-title-area]
985
996
  [data-part][data-when='snapped'][data-when],
986
- [data-ui='page-header']:not([data-expanded])
997
+ [data-ui-page-header]:not([data-expanded])
987
998
  > *
988
- > [data-part='title-area']
999
+ > [data-part-title-area]
989
1000
  [data-part][data-when='expanded'][data-when] {
990
1001
  display: none;
991
1002
  }
@@ -998,31 +1009,31 @@
998
1009
  * of the text it replaces, which is what keeps the height still when the
999
1010
  * title arrives.
1000
1011
  */
1001
- [data-ui='page-header'][data-loading]
1012
+ [data-ui-page-header][data-loading]
1002
1013
  > *
1003
- > [data-part='title-area']
1004
- > [data-part='title-row']
1005
- > [data-part='title']
1006
- > [data-part='heading'] {
1014
+ > [data-part-title-area]
1015
+ > [data-part-title-row]
1016
+ > [data-part-title]
1017
+ > [data-part-heading] {
1007
1018
  /* The width a title has, and no growth: grown, the heading pushed the
1008
1019
  marks to the far end and they jumped back beside it when the title
1009
1020
  arrived. The bar is what the words will be. */
1010
1021
  --slotted-skeleton-inline-size: 24ch;
1011
1022
  flex: 0 1 auto;
1012
1023
  }
1013
- [data-ui='page-header'][data-loading]
1024
+ [data-ui-page-header][data-loading]
1014
1025
  > *
1015
- > [data-part='title-area']
1016
- > [data-part='title-row']
1017
- > [data-part='title']
1018
- > [data-part='subheading'] {
1026
+ > [data-part-title-area]
1027
+ > [data-part-title-row]
1028
+ > [data-part-title]
1029
+ > [data-part-subheading] {
1019
1030
  --slotted-skeleton-inline-size: 32ch;
1020
1031
  }
1021
- [data-ui='page-header'][data-loading]
1032
+ [data-ui-page-header][data-loading]
1022
1033
  > *
1023
- > [data-part='header-area']
1024
- > [data-part='content']
1025
- > [data-part='description'] {
1034
+ > [data-part-header-area]
1035
+ > [data-part-content]
1036
+ > [data-part-description] {
1026
1037
  --slotted-skeleton-inline-size: 40ch;
1027
1038
  }
1028
1039
 
@@ -1036,26 +1047,37 @@
1036
1047
  * the lines the container's own breakout part spans — so the bar is the
1037
1048
  * header's width, as the line is, and not the measure's.
1038
1049
  *
1039
- * Written after the controls and before the tabs, as the controls are, it
1040
- * stands on the edge the pill straddles; on the root's own edge the bar
1041
- * would run under the tabs and read as the selected tab's indicator drawn
1042
- * across the whole row. With no tabs it is the root's own hairline. The bar
1043
- * stands on the frame's block-end edge and its thickness rises above it,
1044
- * which is why the frame does not clip.
1050
+ * With a pill over the tabs it stands on the edge the pill straddles, the
1051
+ * line the reader already sees as the chrome's end. Otherwise it is the
1052
+ * root's own hairline, under the tabs when there are tabs: it is the only
1053
+ * line there is. The bar stands on the frame's block-end edge and its
1054
+ * thickness rises above it, which is why the frame does not clip.
1045
1055
  *
1046
1056
  * A snapped header keeps its edge on screen, and the frame keeps its place
1047
1057
  * beside the controls, so the bar stays with it. Above the title area,
1048
1058
  * whose ground would cover the bar's upper pixel once the area has slid
1049
1059
  * under the title; below the controls, whose pill straddles the same line.
1060
+ *
1061
+ * The place is the stylesheet's, not the order the parts are written in:
1062
+ * the frame is the last row, on the root's hairline, and with a pill over
1063
+ * the tabs the tabs move after it, so the frame is the row under the
1064
+ * controls. Written before the tabs with no pill, the frame was a row of
1065
+ * its own between the title and the strip, over no line.
1050
1066
  */
1051
- [data-ui='page-header'] > * > [data-part='progress'] {
1067
+ [data-ui-page-header] > * > [data-part-progress] {
1052
1068
  block-size: var(--_rule-size);
1053
1069
  grid-column: full;
1054
1070
  margin-block-end: calc(-1 * var(--_rule-size));
1071
+ order: 1;
1055
1072
  pointer-events: none;
1056
1073
  position: relative;
1057
1074
  z-index: 3;
1058
1075
  }
1076
+ [data-ui-page-header]:has(> * > [data-part-controls]):has(> * > [data-part-tabs]:not(:empty))
1077
+ > *
1078
+ > [data-part-tabs] {
1079
+ order: 2;
1080
+ }
1059
1081
 
1060
1082
  /**
1061
1083
  * One wait at a time. The title's skeleton is the first load, and a
@@ -1064,8 +1086,8 @@
1064
1086
  * inside it. The second rule reads an ARIA state rather than another
1065
1087
  * family's identity, so any region a consumer marks busy takes it.
1066
1088
  */
1067
- [data-ui='page-header'][data-loading] > * > [data-part='progress'],
1068
- [aria-busy='true'] [data-ui='page-header'] > * > [data-part='progress'] {
1089
+ [data-ui-page-header][data-loading] > * > [data-part-progress],
1090
+ [aria-busy='true'] [data-ui-page-header] > * > [data-part-progress] {
1069
1091
  display: none;
1070
1092
  }
1071
1093
 
@@ -1079,7 +1101,7 @@
1079
1101
  * against the component's own width rather than the viewport.
1080
1102
  */
1081
1103
  @container page-header (width < 600px) {
1082
- [data-ui='page-header'] {
1104
+ [data-ui-page-header] {
1083
1105
  --_heading-size: var(
1084
1106
  --slotted-page-header-heading-size-sm,
1085
1107
  var(--slotted-font-size-xl, 1.125rem)
@@ -1091,29 +1113,25 @@
1091
1113
  );
1092
1114
  --_snapped-heading-line-height: var(--slotted-line-height-md, 1.5);
1093
1115
  }
1094
- [data-ui='page-header']
1116
+ [data-ui-page-header]
1095
1117
  > *
1096
- > [data-part='title-area']
1097
- > [data-part='nav']
1098
- > [data-part='back']
1099
- ~ [data-part='breadcrumbs'] {
1118
+ > [data-part-title-area]
1119
+ > [data-part-nav]
1120
+ > [data-part-back]
1121
+ ~ [data-part-breadcrumbs] {
1100
1122
  display: none;
1101
1123
  }
1102
- [data-ui='page-header']
1124
+ [data-ui-page-header]
1103
1125
  > *
1104
- > [data-part='title-area']
1105
- > [data-part='nav']
1106
- > [data-part='back']:has(~ [data-part='breadcrumbs']:not(:empty)) {
1126
+ > [data-part-title-area]
1127
+ > [data-part-nav]
1128
+ > [data-part-back]:has(~ [data-part-breadcrumbs]:not(:empty)) {
1107
1129
  display: inline-flex;
1108
1130
  }
1109
- [data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'] {
1131
+ [data-ui-page-header] > * > [data-part-controls] > * > [data-part-pin] {
1110
1132
  display: none;
1111
1133
  }
1112
- [data-ui='page-header']
1113
- > *
1114
- > [data-part='title-area']
1115
- > [data-part='title-row']
1116
- > [data-part='title'] {
1134
+ [data-ui-page-header] > * > [data-part-title-area] > [data-part-title-row] > [data-part-title] {
1117
1135
  flex: 1 1 0;
1118
1136
  min-inline-size: 60%;
1119
1137
  }
@@ -1123,57 +1141,79 @@
1123
1141
  * title that leaves it room, the grown box put one button at its own
1124
1142
  * start, mid-row, off the edge every other width aligns it to.
1125
1143
  */
1126
- [data-ui='page-header']
1144
+ [data-ui-page-header]
1127
1145
  > *
1128
- > [data-part='title-area']
1129
- > [data-part='title-row']
1130
- > [data-part='actions'] {
1146
+ > [data-part-title-area]
1147
+ > [data-part-title-row]
1148
+ > [data-part-actions] {
1131
1149
  flex: 1 1 auto;
1132
1150
  justify-content: flex-end;
1133
1151
  }
1134
1152
  /* The marks take a line of their own under the heading: beside it their break depends on
1135
1153
  what they say, which the waiting header cannot know, and the header grew when it landed. */
1136
- [data-ui='page-header']
1154
+ [data-ui-page-header]
1137
1155
  > *
1138
- > [data-part='title-area']
1139
- > [data-part='title-row']
1140
- > [data-part='title']
1141
- > [data-part='marks'] {
1156
+ > [data-part-title-area]
1157
+ > [data-part-title-row]
1158
+ > [data-part-title]
1159
+ > [data-part-marks] {
1142
1160
  flex-basis: 100%;
1143
1161
  }
1144
- [data-ui='page-header']
1162
+ [data-ui-page-header]
1145
1163
  > *
1146
- > [data-part='title-area']
1147
- > [data-part='title-row']:has(
1148
- > [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
1149
- )
1150
- > [data-part='title']
1151
- > [data-part='marks'] {
1164
+ > [data-part-title-area]
1165
+ > [data-part-title-row]:has(> [data-part-actions] > [data-ui-toolbar][data-overflow='menu'])
1166
+ > [data-part-title]
1167
+ > [data-part-marks] {
1152
1168
  grid-column: 1 / -1;
1153
1169
  }
1170
+ /* A rich title — a visual, marks or a subheading — leaves a whole row free under it, and a toolbar
1171
+ that folds takes that row: beside the title it had the 40 per cent the title left, and
1172
+ folded every action on a 390 pixel phone. Beside a plain title it stays on the title row. */
1173
+ [data-ui-page-header]
1174
+ > *
1175
+ > [data-part-title-area]
1176
+ > [data-part-title-row]:has(
1177
+ > [data-part-actions] > [data-ui-toolbar][data-overflow='menu']
1178
+ ):has(
1179
+ > [data-part-visual],
1180
+ > [data-part-title] > :is([data-part-subheading], [data-part-marks]:not(:empty))
1181
+ ) {
1182
+ flex-wrap: wrap;
1183
+ }
1184
+ [data-ui-page-header]
1185
+ > *
1186
+ > [data-part-title-area]
1187
+ > [data-part-title-row]:has(
1188
+ > [data-part-visual],
1189
+ > [data-part-title] > :is([data-part-subheading], [data-part-marks]:not(:empty))
1190
+ )
1191
+ > [data-part-actions]:has(> [data-ui-toolbar][data-overflow='menu']) {
1192
+ flex: 1 1 100%;
1193
+ }
1154
1194
  /* One line, as snapped: the phone has no second one to give it. */
1155
- [data-ui='page-header']
1195
+ [data-ui-page-header]
1156
1196
  > *
1157
- > [data-part='title-area']
1158
- > [data-part='title-row']
1159
- > [data-part='title']
1160
- > [data-part='subheading'] {
1197
+ > [data-part-title-area]
1198
+ > [data-part-title-row]
1199
+ > [data-part-title]
1200
+ > [data-part-subheading] {
1161
1201
  display: block;
1162
1202
  text-overflow: ellipsis;
1163
1203
  white-space: nowrap;
1164
1204
  }
1165
- [data-ui='page-header']
1205
+ [data-ui-page-header]
1166
1206
  > *
1167
- > [data-part='title-area']
1168
- > [data-part='title-row']
1169
- > [data-part='navigation'] {
1207
+ > [data-part-title-area]
1208
+ > [data-part-title-row]
1209
+ > [data-part-navigation] {
1170
1210
  border-inline-start: 0;
1171
1211
  padding-inline-start: 0;
1172
1212
  }
1173
- [data-ui='page-header'] > * > [data-part='header-area'] {
1213
+ [data-ui-page-header] > * > [data-part-header-area] {
1174
1214
  grid-template-columns: minmax(0, 1fr);
1175
1215
  }
1176
- [data-ui='page-header'] > * > [data-part='header-area'] > [data-part='figure'] {
1216
+ [data-ui-page-header] > * > [data-part-header-area] > [data-part-figure] {
1177
1217
  padding-block-end: var(--_gap);
1178
1218
  padding-inline-end: 0;
1179
1219
  }