@slotted/styles 0.1.0-beta.0 → 0.1.0-beta.10

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 (176) hide show
  1. package/package.json +1 -1
  2. package/src/accordion/accordion.css +10 -10
  3. package/src/action-bar/action-bar.css +31 -32
  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 +139 -64
  17. package/src/button/button.tokens.json +10 -0
  18. package/src/button-group/button-group.css +76 -72
  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 +84 -90
  36. package/src/container/container.css +24 -24
  37. package/src/context-menu/context-menu.css +8 -8
  38. package/src/currency-field/currency-field.css +210 -88
  39. package/src/currency-field/currency-field.tokens.json +13 -0
  40. package/src/dashboard-grid/dashboard-grid.css +148 -116
  41. package/src/data-grid/data-grid.css +504 -469
  42. package/src/data-grid/data-grid.tokens.json +2 -0
  43. package/src/data-list/data-list.css +81 -70
  44. package/src/data-list/data-list.tokens.json +2 -1
  45. package/src/date-field/date-field.css +55 -52
  46. package/src/date-field/date-field.tokens.json +1 -0
  47. package/src/date-picker/date-picker.css +40 -42
  48. package/src/date-range-picker/date-range-picker.css +114 -118
  49. package/src/datetime-field/datetime-field.css +78 -80
  50. package/src/datetime-field/datetime-field.tokens.json +1 -0
  51. package/src/datetime-picker/datetime-picker.css +61 -63
  52. package/src/datetime-range-picker/datetime-range-picker.css +149 -156
  53. package/src/description-list/description-list.css +61 -64
  54. package/src/detail-panel/detail-panel.css +70 -70
  55. package/src/dialog/dialog.css +334 -153
  56. package/src/dialog/dialog.tokens.json +5 -0
  57. package/src/divider/divider.css +18 -18
  58. package/src/drawer/drawer.css +186 -209
  59. package/src/entity-picker/entity-picker.css +238 -241
  60. package/src/entity-picker/entity-picker.tokens.json +1 -0
  61. package/src/event-calendar/event-calendar.css +343 -385
  62. package/src/field/field.css +74 -78
  63. package/src/field-group/field-group.css +22 -25
  64. package/src/filter-bar/filter-bar.css +193 -209
  65. package/src/form/form.css +34 -24
  66. package/src/form/form.tokens.json +1 -0
  67. package/src/fullscreen/fullscreen.css +24 -25
  68. package/src/grid/grid.css +28 -28
  69. package/src/highlight/highlight.css +6 -6
  70. package/src/hover-card/hover-card.css +6 -6
  71. package/src/icon/icon.css +19 -15
  72. package/src/icon/icon.tokens.json +1 -0
  73. package/src/image/image.css +39 -39
  74. package/src/import-export-center/import-export-center.css +135 -156
  75. package/src/inline/inline.css +27 -27
  76. package/src/inline-message/inline-message.css +52 -28
  77. package/src/inline-message/inline-message.tokens.json +4 -0
  78. package/src/kbd/kbd.css +30 -26
  79. package/src/link/link.css +15 -15
  80. package/src/list/list.css +44 -17
  81. package/src/list/list.tokens.json +4 -0
  82. package/src/list-item/list-item.css +80 -66
  83. package/src/list-item/list-item.tokens.json +7 -0
  84. package/src/listbox/listbox.css +56 -59
  85. package/src/loading-overlay/loading-overlay.css +75 -92
  86. package/src/markdown/markdown.css +17 -17
  87. package/src/menu/menu.css +83 -63
  88. package/src/menubar/menubar.css +13 -13
  89. package/src/message/message.css +182 -202
  90. package/src/message/message.tokens.json +5 -0
  91. package/src/message-scroller/message-scroller.css +41 -42
  92. package/src/multi-select/multi-select.css +152 -145
  93. package/src/multi-select/multi-select.tokens.json +1 -0
  94. package/src/navigation-menu/navigation-menu.css +167 -188
  95. package/src/notification-center/notification-center.css +140 -148
  96. package/src/notification-center/notification-center.tokens.json +3 -0
  97. package/src/number-field/number-field.css +84 -83
  98. package/src/number-field/number-field.tokens.json +1 -0
  99. package/src/otp-input/otp-input.css +67 -57
  100. package/src/otp-input/otp-input.tokens.json +1 -0
  101. package/src/page/page.css +23 -23
  102. package/src/page-header/page-header.css +268 -281
  103. package/src/page-header/page-header.tokens.json +1 -0
  104. package/src/pagination/pagination.css +68 -72
  105. package/src/popover/popover.css +40 -49
  106. package/src/progress-indicator/progress-indicator.css +177 -120
  107. package/src/progress-indicator/progress-indicator.tokens.json +1 -1
  108. package/src/prompt-composer/prompt-composer.css +221 -102
  109. package/src/prompt-composer/prompt-composer.tokens.json +15 -0
  110. package/src/prose/prose.css +71 -75
  111. package/src/radio-group/radio-group.css +98 -103
  112. package/src/rating/rating.css +136 -132
  113. package/src/rating/rating.tokens.json +1 -0
  114. package/src/result-state/result-state.css +74 -77
  115. package/src/runtime-properties.mjs +2 -0
  116. package/src/saved-views/saved-views.css +41 -47
  117. package/src/scroll-area/scroll-area.css +39 -32
  118. package/src/section/section.css +161 -41
  119. package/src/section/section.tokens.json +7 -2
  120. package/src/segmented-control/segmented-control.css +254 -97
  121. package/src/segmented-control/segmented-control.tokens.json +15 -11
  122. package/src/select/select.css +118 -117
  123. package/src/select/select.tokens.json +1 -0
  124. package/src/side-navigation/side-navigation.css +179 -182
  125. package/src/side-navigation/side-navigation.tokens.json +1 -0
  126. package/src/skeleton/skeleton.css +47 -48
  127. package/src/slider/slider.css +86 -88
  128. package/src/sparkline/sparkline.css +62 -40
  129. package/src/sparkline/sparkline.tokens.json +2 -0
  130. package/src/spinner/spinner.css +39 -182
  131. package/src/spinner/spinner.tokens.json +0 -2
  132. package/src/split-button/split-button.css +30 -31
  133. package/src/split-button/split-button.tokens.json +1 -0
  134. package/src/splitter/splitter.css +35 -35
  135. package/src/stack/stack.css +24 -24
  136. package/src/stat-card/stat-card.css +341 -224
  137. package/src/stat-card/stat-card.tokens.json +3 -0
  138. package/src/status-indicator/status-indicator.css +28 -28
  139. package/src/stepper/stepper.css +58 -62
  140. package/src/switch/switch.css +86 -92
  141. package/src/table/table.css +310 -165
  142. package/src/table/table.tokens.json +6 -0
  143. package/src/table-of-contents/table-of-contents.css +51 -54
  144. package/src/tabs/tabs.css +330 -302
  145. package/src/tabs/tabs.tokens.json +8 -5
  146. package/src/tag/tag.css +79 -71
  147. package/src/tag/tag.tokens.json +9 -0
  148. package/src/tags-input/tags-input.css +145 -82
  149. package/src/tags-input/tags-input.tokens.json +7 -1
  150. package/src/text-field/text-field.css +132 -136
  151. package/src/text-field/text-field.tokens.json +1 -0
  152. package/src/textarea/textarea.css +31 -28
  153. package/src/textarea/textarea.tokens.json +1 -0
  154. package/src/time-field/time-field.css +72 -69
  155. package/src/time-field/time-field.tokens.json +1 -0
  156. package/src/time-picker/time-picker.css +56 -58
  157. package/src/time-range-picker/time-range-picker.css +126 -133
  158. package/src/timeline/timeline.css +254 -266
  159. package/src/timeline/timeline.tokens.json +5 -0
  160. package/src/toast/toast.css +89 -90
  161. package/src/toast/toast.tokens.json +5 -0
  162. package/src/toggle/toggle.css +62 -25
  163. package/src/toggle/toggle.tokens.json +8 -0
  164. package/src/toggle-group/toggle-group.css +106 -60
  165. package/src/toggle-group/toggle-group.tokens.json +10 -0
  166. package/src/toolbar/toolbar.css +30 -35
  167. package/src/tooltip/tooltip.css +26 -26
  168. package/src/top-navigation/top-navigation.css +243 -259
  169. package/src/tour/tour.css +51 -60
  170. package/src/transfer/transfer.css +160 -164
  171. package/src/tree/tree.css +103 -111
  172. package/src/typography/typography.css +40 -36
  173. package/src/typography/typography.tokens.json +1 -0
  174. package/src/upload/upload.css +224 -224
  175. package/src/visually-hidden/visually-hidden.css +1 -1
  176. package/src/wizard/wizard.css +185 -197
@@ -14,7 +14,7 @@
14
14
  * `prose` makes the same division for the same reason, and every token below
15
15
  * is declared here so both boxes and every cell read one set.
16
16
  */
17
- [data-ui='table'][data-part='root'] {
17
+ [data-ui-table][data-part-root] {
18
18
  /* Geometry. Every value is the control scale's, because a row is a
19
19
  control-sized thing: one theme decision retunes the rows, the buttons and
20
20
  the fields together, and `@media (pointer: coarse)` grows all three at
@@ -146,7 +146,7 @@
146
146
  max-inline-size: 100%;
147
147
  }
148
148
 
149
- [data-ui='table'][data-variant='framed'] {
149
+ [data-ui-table][data-variant='framed'] {
150
150
  border: var(--_rule-width) solid var(--_rule);
151
151
  border-radius: var(--_radius);
152
152
  /**
@@ -180,7 +180,7 @@
180
180
  * press Tab for nothing, and a wide table with no tab stop is unreachable
181
181
  * without a pointer.
182
182
  */
183
- [data-ui='table'] [data-part='scroll'] {
183
+ [data-ui-table] [data-part-scroll] {
184
184
  /* `overscroll-behavior-inline: contain` keeps a sideways flick inside the
185
185
  table instead of walking the page back a step, which is the gesture a
186
186
  wide table on a trackpad produces constantly. */
@@ -189,8 +189,14 @@
189
189
  }
190
190
 
191
191
  /* The ring is drawn inside, because a scroller flush against the frame would
192
- otherwise draw half its ring outside it. */
193
- [data-ui='table'] [data-part='scroll']:focus-visible {
192
+ otherwise draw half its ring outside it. It is drawn for a focus from the
193
+ keyboard: a click on a cell focuses the scroller too, and the next Shift
194
+ would make it match `:focus-visible` (`createFocusOrigin`). The browser's own
195
+ ring is suppressed for the same focus. */
196
+ [data-ui-table] [data-part-scroll]:focus {
197
+ outline: none;
198
+ }
199
+ [data-ui-table] [data-part-scroll]:focus-visible[data-focus-origin='keyboard'] {
194
200
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
195
201
  outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
196
202
  }
@@ -204,7 +210,7 @@
204
210
  * a border radius. The rules are therefore drawn as one edge on each cell,
205
211
  * never as a border on the row.
206
212
  */
207
- [data-ui='table'] [data-part='table'] {
213
+ [data-ui-table] [data-part-table] {
208
214
  border-collapse: separate;
209
215
  border-spacing: 0;
210
216
  /* The table fills the scroller and grows past it when its columns need
@@ -214,7 +220,7 @@
214
220
  text-align: start;
215
221
  }
216
222
 
217
- [data-ui='table'][data-layout='fixed'] [data-part='table'] {
223
+ [data-ui-table][data-layout='fixed'] [data-part-table] {
218
224
  inline-size: 100%;
219
225
  table-layout: fixed;
220
226
  }
@@ -227,12 +233,12 @@
227
233
  * consumer who hides it with `visually-hidden` loses the picture and keeps
228
234
  * the name.
229
235
  */
230
- [data-ui='table'] [data-part='caption'] {
236
+ [data-ui-table] [data-part-caption] {
231
237
  caption-side: block-start;
232
238
  color: var(--_muted);
233
239
  font-size: var(--slotted-font-size-xs, 0.75rem);
234
240
  padding-block: var(--_pad-block);
235
- padding-inline: var(--_pad-inline);
241
+ padding-inline: var(--_edge-inset);
236
242
  text-align: start;
237
243
  }
238
244
 
@@ -251,8 +257,8 @@
251
257
  column header above the alignment rule that comes after it — a header
252
258
  carrying data-align="end" computed text-align: start, so a numeric
253
259
  column's digits ended at one edge and their name at the other. */
254
- [data-ui='table'] [data-part='column-header'],
255
- [data-ui='table'] [data-part='head'] [data-part='selection'] {
260
+ [data-ui-table] [data-part-column-header],
261
+ [data-ui-table] [data-part-head] [data-part-selection] {
256
262
  background-color: var(--_head-surface);
257
263
  border-block-end: var(--_rule-width) solid var(--_rule-strong);
258
264
  color: var(--_text);
@@ -272,14 +278,14 @@
272
278
  vertical-align: bottom;
273
279
  }
274
280
 
275
- [data-ui='table'][data-variant='plain'] [data-part='column-header'] {
281
+ [data-ui-table][data-variant='plain'] [data-part-column-header] {
276
282
  background-color: transparent;
277
283
  }
278
284
 
279
285
  /**
280
286
  * ── Rows and cells ─────────────────────────────────────────────────────
281
287
  */
282
- [data-ui='table'] [data-part='row'] {
288
+ [data-ui-table] [data-part-row] {
283
289
  background-color: var(--_surface);
284
290
  }
285
291
 
@@ -299,35 +305,80 @@
299
305
  * because the cell that wants the other answer is usually one: the actions
300
306
  * pinned at the end of a tall row.
301
307
  */
302
- [data-ui='table'] {
308
+ [data-ui-table] {
303
309
  --_cell-align: middle;
304
310
  }
305
311
 
306
- [data-ui='table'][data-cell-align='start'] {
312
+ [data-ui-table][data-cell-align='start'] {
307
313
  --_cell-align: top;
308
314
  }
309
315
 
310
- [data-ui='table'][data-cell-align='end'] {
316
+ [data-ui-table][data-cell-align='end'] {
311
317
  --_cell-align: bottom;
312
318
  }
313
319
 
314
- [data-ui='table'] [data-part='cell'],
315
- [data-ui='table'] [data-part='row-header'] {
320
+ [data-ui-table] [data-part-cell],
321
+ [data-ui-table] [data-part-row-header] {
316
322
  padding-block: var(--_pad-block);
317
323
  padding-inline: var(--_pad-inline);
318
324
  text-align: start;
319
325
  vertical-align: var(--_cell-align);
320
326
  }
321
327
 
322
- [data-ui='table'] [data-block-align='start'] {
328
+ /**
329
+ * ── The outer edges ────────────────────────────────────────────────────
330
+ *
331
+ * The first and the last column take `--_edge-inset` on their outer side:
332
+ * the cell's own inset by default, and under `edges="flush"` the theme's
333
+ * `--slotted-table-edge-inset`, nought when it sets none — so a `plain` or
334
+ * `ruled` table lines its text up with the heading above it. A `framed`
335
+ * table keeps the inset whatever `edges` says: its text stands against a
336
+ * border, and text touching a border is the cramped picture the frame
337
+ * exists to avoid. A pinned column keeps its own offset, which is a
338
+ * position rather than a padding, and the selection cell keeps its own
339
+ * inset because it is not one of the parts named here — a table that leads
340
+ * with a chooser has no text at its edge.
341
+ */
342
+ [data-ui-table][data-part-root] {
343
+ --_edge-inset: var(--_pad-inline);
344
+ }
345
+
346
+ [data-ui-table][data-part-root][data-edges='flush']:is(
347
+ [data-variant='plain'],
348
+ [data-variant='ruled']
349
+ ) {
350
+ --_edge-inset: var(--slotted-table-edge-inset, 0px);
351
+ }
352
+
353
+ [data-ui-table]
354
+ [data-part-row]
355
+ > :is([data-part-cell], [data-part-row-header], [data-part-group-header]):first-child,
356
+ [data-ui-table]
357
+ [data-part-row]
358
+ > [data-part-column-header]:first-child:not(:has(> [data-part-sort])),
359
+ [data-ui-table] [data-part-row] > [data-part-column-header]:first-child > [data-part-sort] {
360
+ padding-inline-start: var(--_edge-inset);
361
+ }
362
+
363
+ [data-ui-table]
364
+ [data-part-row]
365
+ > :is([data-part-cell], [data-part-row-header], [data-part-group-header]):last-child,
366
+ [data-ui-table]
367
+ [data-part-row]
368
+ > [data-part-column-header]:last-child:not(:has(> [data-part-sort])),
369
+ [data-ui-table] [data-part-row] > [data-part-column-header]:last-child > [data-part-sort] {
370
+ padding-inline-end: var(--_edge-inset);
371
+ }
372
+
373
+ [data-ui-table] [data-block-align='start'] {
323
374
  vertical-align: top;
324
375
  }
325
376
 
326
- [data-ui='table'] [data-block-align='center'] {
377
+ [data-ui-table] [data-block-align='center'] {
327
378
  vertical-align: middle;
328
379
  }
329
380
 
330
- [data-ui='table'] [data-block-align='end'] {
381
+ [data-ui-table] [data-block-align='end'] {
331
382
  vertical-align: bottom;
332
383
  }
333
384
 
@@ -356,24 +407,24 @@
356
407
  * `timeline` or a nested table has a `plain` variant of its own and is not a
357
408
  * control. `table.styles.test.mjs` holds the two lists against each other.
358
409
  */
359
- [data-ui='table']
360
- :is([data-part='cell'], [data-part='row-header']):has(
361
- > [data-part='root'][data-variant='plain']:is(
362
- [data-ui='select'],
363
- [data-ui='combobox'],
364
- [data-ui='multi-select'],
365
- [data-ui='text-field'],
366
- [data-ui='textarea'],
367
- [data-ui='number-field'],
368
- [data-ui='currency-field'],
369
- [data-ui='date-field'],
370
- [data-ui='time-field'],
371
- [data-ui='datetime-field'],
372
- [data-ui='tags-input'],
373
- [data-ui='date-range-picker'],
374
- [data-ui='time-range-picker'],
375
- [data-ui='datetime-range-picker'],
376
- [data-ui='entity-picker']
410
+ [data-ui-table]
411
+ :is([data-part-cell], [data-part-row-header]):has(
412
+ > [data-part-root][data-variant='plain']:is(
413
+ [data-ui-select],
414
+ [data-ui-combobox],
415
+ [data-ui-multi-select],
416
+ [data-ui-text-field],
417
+ [data-ui-textarea],
418
+ [data-ui-number-field],
419
+ [data-ui-currency-field],
420
+ [data-ui-date-field],
421
+ [data-ui-time-field],
422
+ [data-ui-datetime-field],
423
+ [data-ui-tags-input],
424
+ [data-ui-date-range-picker],
425
+ [data-ui-time-range-picker],
426
+ [data-ui-datetime-range-picker],
427
+ [data-ui-entity-picker]
377
428
  )
378
429
  ) {
379
430
  --slotted-control-padding-inline-sm: var(--_pad-inline);
@@ -391,7 +442,7 @@
391
442
  against the row's ink, a selected record's name stayed the page's black
392
443
  while every value beside it turned accent, and a disabled row's name stayed
393
444
  at full strength — the two states the row paints and this cell refused. */
394
- [data-ui='table'] [data-part='row-header'] {
445
+ [data-ui-table] [data-part-row-header] {
395
446
  font-weight: var(--slotted-font-weight-medium, 500);
396
447
  }
397
448
 
@@ -399,16 +450,16 @@
399
450
  it under `border-collapse: separate`. The last row of the body gives its
400
451
  rule up unless a footer follows, so a `ruled` table does not end in a line
401
452
  with nothing under it. */
402
- [data-ui='table']:is([data-variant='ruled'], [data-variant='framed'])
403
- [data-part='body']
404
- > [data-part='row']
453
+ [data-ui-table]:is([data-variant='ruled'], [data-variant='framed'])
454
+ [data-part-body]
455
+ > [data-part-row]
405
456
  > * {
406
457
  border-block-end: var(--_rule-width) solid var(--_rule);
407
458
  }
408
459
 
409
- [data-ui='table']:is([data-variant='ruled'], [data-variant='framed'])
410
- [data-part='body']
411
- > [data-part='row']:last-child
460
+ [data-ui-table]:is([data-variant='ruled'], [data-variant='framed'])
461
+ [data-part-body]
462
+ > [data-part-row]:last-child
412
463
  > * {
413
464
  border-block-end-color: transparent;
414
465
  }
@@ -418,7 +469,7 @@
418
469
  `nth-child(even)` counts rows the family renders; a detail row is a row of
419
470
  the same body, so the stripe follows the record it belongs to rather than
420
471
  alternating around it — see the detail rule below. */
421
- [data-ui='table'][data-striped] [data-part='body'] > [data-part='row']:nth-child(even) {
472
+ [data-ui-table][data-striped] [data-part-body] > [data-part-row]:nth-child(even) {
422
473
  background-color: var(--_stripe);
423
474
  }
424
475
 
@@ -430,7 +481,7 @@
430
481
  * every pointer state rather than a rule of its own: a row a consumer has
431
482
  * disabled does not answer the pointer at all.
432
483
  */
433
- [data-ui='table'] [data-part='body'] > [data-part='row']:not([data-disabled]):hover {
484
+ [data-ui-table] [data-part-body] > [data-part-row]:not([data-disabled]):hover {
434
485
  background-color: var(--_hover);
435
486
  }
436
487
 
@@ -446,21 +497,97 @@
446
497
  * is what survives forced colours, which is where WCAG 2.2 SC 1.4.1 puts the
447
498
  * requirement anyway.
448
499
  */
449
- [data-ui='table'] [data-part='body'] > [data-part='row'][data-selected] {
500
+ [data-ui-table] [data-part-body] > [data-part-row][data-selected] {
450
501
  background-color: var(--_selected);
451
502
  color: var(--_selected-text);
452
503
  }
453
504
 
454
- [data-ui='table']
455
- [data-part='body']
456
- > [data-part='row'][data-selected]:not([data-disabled]):hover {
505
+ [data-ui-table] [data-part-body] > [data-part-row][data-selected]:not([data-disabled]):hover {
457
506
  background-color: var(--_selected-hover);
458
507
  }
459
508
 
460
- [data-ui='table'] [data-part='row'][data-disabled] {
509
+ [data-ui-table] [data-part-row][data-disabled] {
461
510
  color: var(--slotted-disabled-foreground, GrayText);
462
511
  }
463
512
 
513
+ /**
514
+ * The row the page shows elsewhere — the record open in a detail pane. A
515
+ * neutral ground a step beyond hover, so the pointer passing over it changes
516
+ * nothing, and no edge on the start side (R1). It is opaque, because a
517
+ * pinned cell copies the row's ground and a translucent one shows the
518
+ * scrolled cells through it. Selection is stronger and paints over it.
519
+ */
520
+ [data-ui-table] [data-part-body] > [data-part-row][data-current]:not([data-selected]) {
521
+ background-color: var(
522
+ --slotted-table-row-surface-current,
523
+ var(--slotted-tone-secondary-subtle-active, ButtonFace)
524
+ );
525
+ }
526
+
527
+ /* A row with an action of its own is a thing the pointer presses. */
528
+ [data-ui-table] [data-part-body] > [data-part-row][data-interactive]:not([data-disabled]) {
529
+ cursor: pointer;
530
+ }
531
+
532
+ /**
533
+ * ── The band that opens a group ────────────────────────────────────────
534
+ *
535
+ * One header cell across every column: the group's name at the start and
536
+ * its total at the end, in tabular figures so a column of daily nets lines
537
+ * up. The ground is the stripe's step, opaque for the same reason a pinned
538
+ * cell's is, and the band stays where it is when the pointer passes.
539
+ */
540
+ [data-ui-table] [data-part-group-header] {
541
+ background-color: var(--slotted-table-group-surface, var(--_stripe));
542
+ border-block-end: var(--_rule-width) solid var(--_rule);
543
+ color: var(--slotted-table-group-color, var(--_text));
544
+ font-weight: var(--slotted-font-weight-semibold, 600);
545
+ padding-block: var(--_pad-block);
546
+ padding-inline: var(--_pad-inline);
547
+ text-align: start;
548
+ }
549
+
550
+ [data-ui-table] [data-part-group-content] {
551
+ align-items: baseline;
552
+ display: flex;
553
+ gap: var(--_gap);
554
+ justify-content: space-between;
555
+ min-inline-size: 0;
556
+ }
557
+
558
+ [data-ui-table] [data-part-group-end] {
559
+ font-variant-numeric: tabular-nums;
560
+ text-align: end;
561
+ white-space: nowrap;
562
+ }
563
+
564
+ /**
565
+ * A sticky band stops under the header when the header sticks too. The
566
+ * header's height is one row of the control scale and its rule; a header
567
+ * that wraps sets `--slotted-table-group-sticky-offset` itself.
568
+ */
569
+ [data-ui-table] [data-part-group-row][data-sticky] > [data-part-group-header] {
570
+ inset-block-start: var(--slotted-table-group-sticky-offset, 0);
571
+ position: sticky;
572
+ z-index: var(--slotted-z-index-sticky, 100);
573
+ }
574
+
575
+ [data-ui-table][data-sticky-header]
576
+ [data-part-group-row][data-sticky]
577
+ > [data-part-group-header] {
578
+ inset-block-start: var(
579
+ --slotted-table-group-sticky-offset,
580
+ calc(var(--_row-min-block) + var(--_rule-width))
581
+ );
582
+ }
583
+
584
+ @media (forced-colors: active) {
585
+ [data-ui-table] [data-part-body] > [data-part-row][data-current] {
586
+ outline: var(--_rule-width) solid Highlight;
587
+ outline-offset: calc(var(--_rule-width) * -1);
588
+ }
589
+ }
590
+
464
591
  /**
465
592
  * ── Alignment and numerals ─────────────────────────────────────────────
466
593
  *
@@ -469,25 +596,25 @@
469
596
  * `start` and `end` are the logical values, so a column aligned to the end is
470
597
  * on the right in English and on the left in Arabic.
471
598
  */
472
- [data-ui='table'] [data-align='start'] {
599
+ [data-ui-table] [data-align='start'] {
473
600
  text-align: start;
474
601
  }
475
602
 
476
- [data-ui='table'] [data-align='center'] {
603
+ [data-ui-table] [data-align='center'] {
477
604
  text-align: center;
478
605
  }
479
606
 
480
- [data-ui='table'] [data-align='end'] {
607
+ [data-ui-table] [data-align='end'] {
481
608
  text-align: end;
482
609
  }
483
610
 
484
611
  /* A ten under a nine is only a column if the digits are the same width. The
485
612
  alignment is inferred once, and only where the consumer stated none. */
486
- [data-ui='table'] [data-numeric] {
613
+ [data-ui-table] [data-numeric] {
487
614
  font-variant-numeric: tabular-nums;
488
615
  }
489
616
 
490
- [data-ui='table'] [data-numeric]:not([data-align]) {
617
+ [data-ui-table] [data-numeric]:not([data-align]) {
491
618
  text-align: end;
492
619
  }
493
620
 
@@ -498,18 +625,18 @@
498
625
  * decision is that a truncated string stays readable to assistive technology
499
626
  * and reachable in full.
500
627
  */
501
- [data-ui='table'] [data-text='wrap'] {
628
+ [data-ui-table] [data-text='wrap'] {
502
629
  /* An identifier, a hash or a URL is one unbroken token, and every surveyed
503
630
  table lets it set the column's width and push the rest of the table
504
631
  sideways. */
505
632
  overflow-wrap: anywhere;
506
633
  }
507
634
 
508
- [data-ui='table'] [data-text='nowrap'] {
635
+ [data-ui-table] [data-text='nowrap'] {
509
636
  white-space: nowrap;
510
637
  }
511
638
 
512
- [data-ui='table'] [data-text='truncate'] {
639
+ [data-ui-table] [data-text='truncate'] {
513
640
  overflow: hidden;
514
641
  text-overflow: ellipsis;
515
642
  white-space: nowrap;
@@ -542,21 +669,33 @@
542
669
  * written where the choice is made: a table wider than the page pushes the
543
670
  * page sideways instead of scrolling inside its frame.
544
671
  *
545
- * The offset is where the header stops. An application with a bar of its own
546
- * sets `--slotted-table-sticky-offset` once, and every table under it stops
547
- * below the bar rather than under it.
672
+ * The offset is where the header stops under `page`. An application with a
673
+ * bar of its own sets `--slotted-table-sticky-offset` once, and every table
674
+ * under it stops below the bar rather than under it. Under `scroll` the
675
+ * header stops at the top of the family's own scroller and reads no offset:
676
+ * `page.css` binds the offset to the page's chrome for every table inside
677
+ * the page, and a header reading it stood that far below its own scroller's
678
+ * top, over the first rows. A `page` table inside a scrolling box of the
679
+ * consumer's own sets the offset on that box, `0` for its top edge.
548
680
  */
549
- [data-ui='table'][data-sticky-header] [data-part='column-header'],
681
+ [data-ui-table][data-sticky-header] [data-part-column-header],
550
682
  /* The chooser's own header is a `selection` cell and not a `column-header`,
551
683
  and a rule naming only the second left the select-all scrolling away while
552
684
  every column beside it stayed. */
553
- [data-ui='table'][data-sticky-header] [data-part='head'] [data-part='selection'] {
554
- inset-block-start: var(--slotted-table-sticky-offset, 0);
685
+ [data-ui-table][data-sticky-header] [data-part-head] [data-part-selection] {
686
+ inset-block-start: 0;
555
687
  position: sticky;
556
688
  z-index: calc(var(--slotted-z-index-sticky, 100) + 1);
557
689
  }
558
690
 
559
- [data-ui='table'][data-sticky-to='page'] [data-part='scroll'] {
691
+ [data-ui-table][data-sticky-to='page'][data-sticky-header] [data-part-column-header],
692
+ [data-ui-table][data-sticky-to='page'][data-sticky-header]
693
+ [data-part-head]
694
+ [data-part-selection] {
695
+ inset-block-start: var(--slotted-table-sticky-offset, 0);
696
+ }
697
+
698
+ [data-ui-table][data-sticky-to='page'] [data-part-scroll] {
560
699
  overflow: visible;
561
700
  }
562
701
 
@@ -571,19 +710,26 @@
571
710
  * It is the `tfoot` that is pinned and not the footer bar: the bar is a
572
711
  * sibling of the scroller and never leaves it in the first place.
573
712
  */
574
- [data-ui='table'][data-sticky-footer] [data-part='summary'] > [data-part='row'] > * {
575
- inset-block-end: var(--slotted-table-sticky-offset-end, 0);
713
+ [data-ui-table][data-sticky-footer] [data-part-summary] > [data-part-row] > * {
714
+ inset-block-end: 0;
576
715
  position: sticky;
577
716
  z-index: calc(var(--slotted-z-index-sticky, 100) + 1);
578
717
  }
579
718
 
719
+ [data-ui-table][data-sticky-to='page'][data-sticky-footer]
720
+ [data-part-summary]
721
+ > [data-part-row]
722
+ > * {
723
+ inset-block-end: var(--slotted-table-sticky-offset-end, 0);
724
+ }
725
+
580
726
  /* The corner again, at the bottom: a summary cell that is also a pinned
581
727
  column takes both insets and the step above both. */
582
- [data-ui='table'][data-sticky-footer] [data-part='summary'] [data-sticky] {
728
+ [data-ui-table][data-sticky-footer] [data-part-summary] [data-sticky] {
583
729
  z-index: calc(var(--slotted-z-index-sticky, 100) + 2);
584
730
  }
585
731
 
586
- [data-ui='table'] [data-sticky='start'] {
732
+ [data-ui-table] [data-sticky='start'] {
587
733
  /* The ground the row is painted with, whatever state it is in: `inherit`
588
734
  takes the row's own computed value, so a hovered, striped or selected row
589
735
  keeps one colour across the seam. */
@@ -601,15 +747,15 @@
601
747
  z-index: var(--slotted-z-index-sticky, 100);
602
748
  }
603
749
 
604
- [data-ui='table'] [data-sticky='end'] {
750
+ [data-ui-table] [data-sticky='end'] {
605
751
  background-color: inherit;
606
752
  inset-inline-end: var(--slotted-table-sticky-inset, 0);
607
753
  position: sticky;
608
754
  z-index: var(--slotted-z-index-sticky, 100);
609
755
  }
610
756
 
611
- [data-ui='table'][data-sticky-header] [data-part='column-header'][data-sticky],
612
- [data-ui='table'][data-sticky-header] [data-part='head'] [data-part='selection'][data-sticky] {
757
+ [data-ui-table][data-sticky-header] [data-part-column-header][data-sticky],
758
+ [data-ui-table][data-sticky-header] [data-part-head] [data-part-selection][data-sticky] {
613
759
  background-color: var(--_head-surface);
614
760
  z-index: calc(var(--slotted-z-index-sticky, 100) + 2);
615
761
  }
@@ -642,7 +788,7 @@
642
788
  positioned box, so the line below resolves against it — and a `relative`
643
789
  written at this point in the file would land after the sticky rules and
644
790
  unpin every pinned column. */
645
- [data-ui='table'] [data-sticky]::after {
791
+ [data-ui-table] [data-sticky]::after {
646
792
  background-color: var(--slotted-table-border-color, var(--slotted-border-subtle));
647
793
  content: '';
648
794
  inline-size: var(--_rule-width);
@@ -651,11 +797,11 @@
651
797
  z-index: 1;
652
798
  }
653
799
 
654
- [data-ui='table'] [data-sticky='start']::after {
800
+ [data-ui-table] [data-sticky='start']::after {
655
801
  inset-inline-end: 0;
656
802
  }
657
803
 
658
- [data-ui='table'] [data-sticky='end']::after {
804
+ [data-ui-table] [data-sticky='end']::after {
659
805
  inset-inline-start: 0;
660
806
  }
661
807
 
@@ -694,7 +840,7 @@
694
840
  * so does this: the header cell gives up its inset and the control takes it,
695
841
  * so the whole header is the target rather than the words inside it.
696
842
  */
697
- [data-ui='table'] [data-part='column-header']:has(> [data-part='sort']) {
843
+ [data-ui-table] [data-part-column-header]:has(> [data-part-sort]) {
698
844
  padding: 0;
699
845
  }
700
846
 
@@ -728,7 +874,7 @@
728
874
  * consumer's own, so their width is not a fact this family holds, and
729
875
  * measuring it would mean reading the layout during render.
730
876
  */
731
- [data-ui='table'] [data-part='column-header']:has(> [data-part='column-actions']) {
877
+ [data-ui-table] [data-part-column-header]:has(> [data-part-column-actions]) {
732
878
  /**
733
879
  * The room is what the slot actually occupies plus one gap: the slot stands
734
880
  * `--_pad-inline` in from the cell's edge and its control is one target
@@ -745,7 +891,7 @@
745
891
  padding-inline-end: var(--_actions-room);
746
892
  }
747
893
 
748
- [data-ui='table'] [data-part='column-actions'] {
894
+ [data-ui-table] [data-part-column-actions] {
749
895
  align-items: center;
750
896
  display: inline-flex;
751
897
  gap: var(--_gap);
@@ -754,7 +900,7 @@
754
900
  position: absolute;
755
901
  }
756
902
 
757
- [data-ui='table'] [data-part='sort'] {
903
+ [data-ui-table] [data-part-sort] {
758
904
  /* The mark is centred on the text rather than dropped at the top of the
759
905
  line: with the default `stretch` resolving to `flex-start` under a stated
760
906
  block size, the chevron sat two pixels above the capitals and read as a
@@ -790,11 +936,11 @@
790
936
  /* An aligned column keeps its name over its values, and the mark follows the
791
937
  name rather than the cell: a centred column reads as a centred pair, and a
792
938
  numeric column's name and mark both stand at the edge its digits end on. */
793
- [data-ui='table'] [data-align='center'] > [data-part='sort'] {
939
+ [data-ui-table] [data-align='center'] > [data-part-sort] {
794
940
  justify-content: center;
795
941
  }
796
942
 
797
- [data-ui='table'] :is([data-align='end'], [data-numeric]:not([data-align])) > [data-part='sort'] {
943
+ [data-ui-table] :is([data-align='end'], [data-numeric]:not([data-align])) > [data-part-sort] {
798
944
  justify-content: end;
799
945
  }
800
946
 
@@ -814,20 +960,20 @@
814
960
  * scale holds a ground one step under this one, and mixing keeps the
815
961
  * direction right in every scheme.
816
962
  */
817
- [data-ui='table'] [data-part='column-header']:has(> [data-part='sort']:not(:disabled)):hover {
963
+ [data-ui-table] [data-part-column-header]:has(> [data-part-sort]:not(:disabled)):hover {
818
964
  background-color: var(
819
965
  --slotted-table-header-surface-hover,
820
966
  color-mix(in oklab, var(--slotted-text, CanvasText) 7%, var(--_head-surface))
821
967
  );
822
968
  }
823
969
 
824
- [data-ui='table'] [data-part='column-header'] {
970
+ [data-ui-table] [data-part-column-header] {
825
971
  transition: background-color
826
972
  var(--slotted-control-transition-duration, var(--slotted-duration-short))
827
973
  var(--slotted-control-transition-easing, var(--slotted-easing-standard));
828
974
  }
829
975
 
830
- [data-ui='table'] [data-part='sort']:focus-visible {
976
+ [data-ui-table] [data-part-sort]:focus-visible {
831
977
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
832
978
  outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
833
979
  }
@@ -842,7 +988,7 @@
842
988
  * pointer or under focus, so the column says it can be sorted without twelve
843
989
  * marks competing with twelve names. The sorted column keeps its mark drawn.
844
990
  */
845
- [data-ui='table'] [data-part='sort'] > [data-ui='icon'] {
991
+ [data-ui-table] [data-part-sort] > [data-ui-icon] {
846
992
  --slotted-icon-size: var(--_icon-size);
847
993
  opacity: 0;
848
994
  transition:
@@ -852,19 +998,19 @@
852
998
  var(--slotted-control-transition-easing, var(--slotted-easing-standard));
853
999
  }
854
1000
 
855
- [data-ui='table'] [data-part='sort']:not(:disabled):hover > [data-ui='icon'],
856
- [data-ui='table'] [data-part='sort']:focus-visible > [data-ui='icon'] {
1001
+ [data-ui-table] [data-part-sort]:not(:disabled):hover > [data-ui-icon],
1002
+ [data-ui-table] [data-part-sort]:focus-visible > [data-ui-icon] {
857
1003
  opacity: 0.55;
858
1004
  }
859
1005
 
860
- [data-ui='table'] [aria-sort] > [data-part='sort'] > [data-ui='icon'] {
1006
+ [data-ui-table] [aria-sort] > [data-part-sort] > [data-ui-icon] {
861
1007
  opacity: 1;
862
1008
  }
863
1009
 
864
1010
  /* Ascending is the same mark turned over. The rotation is animated because it
865
1011
  is the one movement in this family a reader has to follow: the column did
866
1012
  not change, its direction did. */
867
- [data-ui='table'] [aria-sort='ascending'] > [data-part='sort'] > [data-ui='icon'] {
1013
+ [data-ui-table] [aria-sort='ascending'] > [data-part-sort] > [data-ui-icon] {
868
1014
  rotate: 180deg;
869
1015
  }
870
1016
 
@@ -884,7 +1030,7 @@
884
1030
  * past the chooser and over the column behind it. Now one name answers both
885
1031
  * places: this width, and the `stickyOffset` beside it.
886
1032
  */
887
- [data-ui='table'] [data-part='selection'] {
1033
+ [data-ui-table] [data-part-selection] {
888
1034
  inline-size: var(--slotted-table-selection-size, 3rem);
889
1035
  padding-block: var(--_pad-block);
890
1036
  padding-inline: var(--_pad-inline);
@@ -898,7 +1044,7 @@
898
1044
 
899
1045
  /* The control is one line tall whatever the row is, so it sits on the line
900
1046
  the axis puts it on rather than stretching down a three-line cell. */
901
- [data-ui='table'] [data-part='selection'] > [data-ui='checkbox'] {
1047
+ [data-ui-table] [data-part-selection] > [data-ui-checkbox] {
902
1048
  block-size: var(--_line);
903
1049
  }
904
1050
 
@@ -917,7 +1063,7 @@
917
1063
  * be this stylesheet deciding how that one looks. The target is unaffected —
918
1064
  * it is a separate pseudo-element, and it stays at the WCAG minimum.
919
1065
  */
920
- [data-ui='table'] [data-part='selection'] {
1066
+ [data-ui-table] [data-part-selection] {
921
1067
  --slotted-selection-halo-opacity: 0;
922
1068
  }
923
1069
 
@@ -929,7 +1075,7 @@
929
1075
  * expander inside the row header moved one identifier and left the column
930
1076
  * ragged.
931
1077
  */
932
- [data-ui='table'] [data-part='cell']:has(> [data-part='expander']) {
1078
+ [data-ui-table] [data-part-cell]:has(> [data-part-expander]) {
933
1079
  inline-size: 1%;
934
1080
  padding-inline-end: 0;
935
1081
  white-space: nowrap;
@@ -945,7 +1091,7 @@
945
1091
  * out. `display: contents` removes both wrappers from layout and leaves the
946
1092
  * button as the slot's own child, which is the box tree React already has.
947
1093
  */
948
- [data-ui='table'] [data-part='column-actions'] *:has([data-part='filter']) {
1094
+ [data-ui-table] [data-part-column-actions] *:has([data-part-filter]) {
949
1095
  display: contents;
950
1096
  }
951
1097
 
@@ -957,10 +1103,10 @@
957
1103
  * turn when the detail is open — and mirrored in a right-to-left document,
958
1104
  * where a closed disclosure points the other way.
959
1105
  */
960
- [data-ui='table'] [data-part='expander'],
961
- [data-ui='table'] [data-part='column-actions'] > button,
962
- [data-ui='table'] button[data-part='column-actions'],
963
- [data-ui='table'] [data-part='filter'] {
1106
+ [data-ui-table] [data-part-expander],
1107
+ [data-ui-table] [data-part-column-actions] > button,
1108
+ [data-ui-table] button[data-part-column-actions],
1109
+ [data-ui-table] [data-part-filter] {
964
1110
  background-color: transparent;
965
1111
  border: 0;
966
1112
  border-radius: var(--slotted-table-control-radius, var(--slotted-radius-sm, 4px));
@@ -1001,39 +1147,39 @@
1001
1147
  * back across them.
1002
1148
  */
1003
1149
  @media (pointer: coarse) {
1004
- [data-ui='table'] [data-part='expander'],
1005
- [data-ui='table'] [data-part='column-actions'] > button,
1006
- [data-ui='table'] button[data-part='column-actions'],
1007
- [data-ui='table'] [data-part='filter'] {
1150
+ [data-ui-table] [data-part-expander],
1151
+ [data-ui-table] [data-part-column-actions] > button,
1152
+ [data-ui-table] button[data-part-column-actions],
1153
+ [data-ui-table] [data-part-filter] {
1008
1154
  position: relative;
1009
1155
  }
1010
1156
 
1011
- [data-ui='table'] [data-part='expander']::after,
1012
- [data-ui='table'] [data-part='column-actions'] > button::after,
1013
- [data-ui='table'] button[data-part='column-actions']::after,
1014
- [data-ui='table'] [data-part='filter']::after {
1157
+ [data-ui-table] [data-part-expander]::after,
1158
+ [data-ui-table] [data-part-column-actions] > button::after,
1159
+ [data-ui-table] button[data-part-column-actions]::after,
1160
+ [data-ui-table] [data-part-filter]::after {
1015
1161
  content: '';
1016
1162
  inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
1017
1163
  position: absolute;
1018
1164
  }
1019
1165
 
1020
- [data-ui='table'] [data-part='column-actions'] > button::after,
1021
- [data-ui='table'] button[data-part='column-actions']::after,
1022
- [data-ui='table'] [data-part='filter']::after {
1166
+ [data-ui-table] [data-part-column-actions] > button::after,
1167
+ [data-ui-table] button[data-part-column-actions]::after,
1168
+ [data-ui-table] [data-part-filter]::after {
1023
1169
  inset-inline-start: 0;
1024
1170
  }
1025
1171
  }
1026
1172
 
1027
- [data-ui='table'] [data-part='expander']:not(:disabled):hover {
1173
+ [data-ui-table] [data-part-expander]:not(:disabled):hover {
1028
1174
  background-color: var(--_hover);
1029
1175
  color: var(--_text);
1030
1176
  }
1031
1177
 
1032
1178
  /* On the band the hover darkens, for the reason the sort control's does: the
1033
1179
  step a row hovers with is lighter than the band and reads as a hole in it. */
1034
- [data-ui='table'] [data-part='column-actions'] > button:not(:disabled):hover,
1035
- [data-ui='table'] button[data-part='column-actions']:not(:disabled):hover,
1036
- [data-ui='table'] [data-part='filter']:not(:disabled):hover {
1180
+ [data-ui-table] [data-part-column-actions] > button:not(:disabled):hover,
1181
+ [data-ui-table] button[data-part-column-actions]:not(:disabled):hover,
1182
+ [data-ui-table] [data-part-filter]:not(:disabled):hover {
1037
1183
  background-color: var(
1038
1184
  --slotted-table-header-surface-hover,
1039
1185
  color-mix(in oklab, var(--slotted-text, CanvasText) 7%, var(--_head-surface))
@@ -1046,10 +1192,10 @@
1046
1192
  because both stand against an edge the ring would otherwise cross; these
1047
1193
  are 24-pixel boxes inside a cell's own inset, and read at that size an
1048
1194
  inward ring closed on the chevron. */
1049
- [data-ui='table'] [data-part='expander']:focus-visible,
1050
- [data-ui='table'] [data-part='column-actions'] > button:focus-visible,
1051
- [data-ui='table'] button[data-part='column-actions']:focus-visible,
1052
- [data-ui='table'] [data-part='filter']:focus-visible {
1195
+ [data-ui-table] [data-part-expander]:focus-visible,
1196
+ [data-ui-table] [data-part-column-actions] > button:focus-visible,
1197
+ [data-ui-table] button[data-part-column-actions]:focus-visible,
1198
+ [data-ui-table] [data-part-filter]:focus-visible {
1053
1199
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
1054
1200
  outline-offset: 0;
1055
1201
  }
@@ -1062,7 +1208,7 @@
1062
1208
  * nineteen and a half — the control scale is right for a control in a form
1063
1209
  * and wrong for a mark in a header.
1064
1210
  */
1065
- [data-ui='table'] :is([data-part='column-actions'], [data-part='filter']) :is(svg, img) {
1211
+ [data-ui-table] :is([data-part-column-actions], [data-part-filter]) :is(svg, img) {
1066
1212
  block-size: var(--_icon-size);
1067
1213
  inline-size: var(--_icon-size);
1068
1214
  }
@@ -1075,8 +1221,8 @@
1075
1221
  * accent ink and holds it whether the pointer is on it or not, because a
1076
1222
  * filter a reader forgot they set is the defect this state exists to prevent.
1077
1223
  */
1078
- [data-ui='table'] [data-part='filter'][data-filtered],
1079
- [data-ui='table'] [data-part='filter'][data-filtered]:not(:disabled):hover {
1224
+ [data-ui-table] [data-part-filter][data-filtered],
1225
+ [data-ui-table] [data-part-filter][data-filtered]:not(:disabled):hover {
1080
1226
  color: var(--slotted-tone-accent-text, LinkText);
1081
1227
  }
1082
1228
 
@@ -1084,17 +1230,17 @@
1084
1230
  * The panel, which is drawn wherever `popover` puts it — inside the header or
1085
1231
  * portalled to the end of the document. So it carries the family's identity
1086
1232
  * on itself rather than reading it from an ancestor: a rule that reached for
1087
- * `[data-ui='table']` above would style the panel in one of the two.
1233
+ * `[data-ui-table]` above would style the panel in one of the two.
1088
1234
  *
1089
1235
  * And it declares the values it uses rather than reading them from the root,
1090
- * for the same reason. Every `--_` above is declared on `[data-part='root']`;
1236
+ * for the same reason. Every `--_` above is declared on `[data-part-root]`;
1091
1237
  * portalled to the end of the document the panel is not inside it, and a
1092
1238
  * custom property that resolves to nothing takes its whole declaration with
1093
1239
  * it — `gap: var(--_gap)` became no gap at all, silently, in the one
1094
1240
  * arrangement `popover` uses by default. The eight below are the panel's own,
1095
1241
  * from the same theme tokens the root reads.
1096
1242
  */
1097
- [data-ui='table'][data-part='filter-panel'] {
1243
+ [data-ui-table][data-part-filter-panel] {
1098
1244
  --_gap: var(--slotted-table-gap, var(--slotted-control-gap-md, 8px));
1099
1245
  --_font-size: var(--slotted-table-font-size, var(--slotted-control-font-size-md, 13px));
1100
1246
  --_line: calc(var(--_font-size) * var(--slotted-line-height-md, 1.5));
@@ -1164,13 +1310,13 @@
1164
1310
  * rather than set on a row, because an operator name is the widest thing in
1165
1311
  * the panel and a row would give the value what was left of 15rem.
1166
1312
  */
1167
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-condition'] {
1313
+ [data-ui-table][data-part-filter-panel] [data-part-filter-condition] {
1168
1314
  display: grid;
1169
1315
  gap: calc(var(--_gap) / 2);
1170
1316
  }
1171
1317
 
1172
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operator'],
1173
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-search'] {
1318
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operator],
1319
+ [data-ui-table][data-part-filter-panel] [data-part-filter-search] {
1174
1320
  display: block;
1175
1321
  }
1176
1322
 
@@ -1185,14 +1331,14 @@
1185
1331
  * way round: two number fields side by side in a 15rem panel are two fields
1186
1332
  * nobody can read.
1187
1333
  */
1188
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operand'] {
1334
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operand] {
1189
1335
  display: grid;
1190
1336
  gap: calc(var(--_gap) / 2);
1191
1337
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
1192
1338
  }
1193
1339
 
1194
1340
  /* A set's operand is a search and a list, which own their own stacking. */
1195
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operand'][data-kind='set'] {
1341
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operand][data-kind='set'] {
1196
1342
  grid-template-columns: 1fr;
1197
1343
  gap: var(--_gap);
1198
1344
  }
@@ -1210,9 +1356,9 @@
1210
1356
  * one field, and the field takes the panel's width so the wrapping has
1211
1357
  * somewhere to go.
1212
1358
  */
1213
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operand'][data-range],
1214
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operand'][data-kind='datetime'],
1215
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-operand'][data-operand='tags'] {
1359
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operand][data-range],
1360
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operand][data-kind='datetime'],
1361
+ [data-ui-table][data-part-filter-panel] [data-part-filter-operand][data-operand='tags'] {
1216
1362
  grid-template-columns: 1fr;
1217
1363
  }
1218
1364
 
@@ -1225,8 +1371,8 @@
1225
1371
  * `between` is one range picker, marked `data-range`, and this rule stands
1226
1372
  * after the one above, so it steps aside for that mark.
1227
1373
  */
1228
- [data-ui='table'][data-part='filter-panel']
1229
- [data-part='filter-operand'][data-kind='time']:not([data-range]) {
1374
+ [data-ui-table][data-part-filter-panel]
1375
+ [data-part-filter-operand][data-kind='time']:not([data-range]) {
1230
1376
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
1231
1377
  }
1232
1378
 
@@ -1235,7 +1381,7 @@
1235
1381
  * conditions are separate boxes and the reader has to see where the first ends
1236
1382
  * before being asked how the second joins it.
1237
1383
  */
1238
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-join'] {
1384
+ [data-ui-table][data-part-filter-panel] [data-part-filter-join] {
1239
1385
  align-items: center;
1240
1386
  border-block-start: var(--_rule-width) solid var(--_rule);
1241
1387
  display: flex;
@@ -1244,7 +1390,7 @@
1244
1390
  padding-block-start: var(--_gap);
1245
1391
  }
1246
1392
 
1247
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-join'] > [data-label] {
1393
+ [data-ui-table][data-part-filter-panel] [data-part-filter-join] > [data-label] {
1248
1394
  color: var(--_muted);
1249
1395
  }
1250
1396
 
@@ -1253,7 +1399,7 @@
1253
1399
  * than the screen. Eight rows is the height a set filter opens at in AG Grid
1254
1400
  * and about the height Excel's opens at; past that the reader searches.
1255
1401
  */
1256
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-list'] {
1402
+ [data-ui-table][data-part-filter-panel] [data-part-filter-list] {
1257
1403
  display: grid;
1258
1404
  gap: var(--_gap);
1259
1405
  max-block-size: calc(var(--_line) * 9);
@@ -1273,7 +1419,7 @@
1273
1419
  * pulled out to the panel's inset so it reads as a row rather than as a box
1274
1420
  * around the words.
1275
1421
  */
1276
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-option'] {
1422
+ [data-ui-table][data-part-filter-panel] [data-part-filter-option] {
1277
1423
  --slotted-selection-halo-opacity: 0;
1278
1424
 
1279
1425
  align-items: center;
@@ -1285,26 +1431,25 @@
1285
1431
  padding-inline: var(--_gap);
1286
1432
  }
1287
1433
 
1288
- [data-ui='table'][data-part='filter-panel']
1289
- [data-part='filter-option']:not([data-disabled]):hover {
1434
+ [data-ui-table][data-part-filter-panel] [data-part-filter-option]:not([data-disabled]):hover {
1290
1435
  background-color: var(--_hover);
1291
1436
  }
1292
1437
 
1293
1438
  /* The count is the consumer's fact about their own query, so it is set apart
1294
1439
  from the value rather than beside it as a second label. */
1295
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-option'] > [data-count] {
1440
+ [data-ui-table][data-part-filter-panel] [data-part-filter-option] > [data-count] {
1296
1441
  color: var(--_muted);
1297
1442
  font-variant-numeric: tabular-nums;
1298
1443
  }
1299
1444
 
1300
1445
  /* The covering checkbox is a row of the same list and a boundary at once: it
1301
1446
  answers for everything under it, so the rule says where "everything" ends. */
1302
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-option'][data-covering] {
1447
+ [data-ui-table][data-part-filter-panel] [data-part-filter-option][data-covering] {
1303
1448
  border-block-end: var(--_rule-width) solid var(--_rule);
1304
1449
  padding-block-end: var(--_gap);
1305
1450
  }
1306
1451
 
1307
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-empty'] {
1452
+ [data-ui-table][data-part-filter-panel] [data-part-filter-empty] {
1308
1453
  color: var(--_muted);
1309
1454
  padding-block: var(--_pad-block);
1310
1455
  text-align: center;
@@ -1312,7 +1457,7 @@
1312
1457
 
1313
1458
  /* Clear at the leading edge and Apply at the trailing one: the destructive
1314
1459
  answer is farthest from the one a reader presses without reading. */
1315
- [data-ui='table'][data-part='filter-panel'] [data-part='filter-actions'] {
1460
+ [data-ui-table][data-part-filter-panel] [data-part-filter-actions] {
1316
1461
  border-block-start: var(--_rule-width) solid var(--_rule);
1317
1462
  display: flex;
1318
1463
  gap: var(--_gap);
@@ -1321,25 +1466,25 @@
1321
1466
  }
1322
1467
 
1323
1468
  /* The chevron is the `Glyph` the binding draws inside the button. */
1324
- [data-ui='table'] [data-part='expander'] > [data-ui='icon'] {
1469
+ [data-ui-table] [data-part-expander] > [data-ui-icon] {
1325
1470
  --slotted-icon-size: var(--_icon-size);
1326
1471
  rotate: 0deg;
1327
1472
  transition: rotate var(--slotted-control-transition-duration, var(--slotted-duration-short))
1328
1473
  var(--slotted-control-transition-easing, var(--slotted-easing-standard));
1329
1474
  }
1330
1475
 
1331
- [data-ui='table'] [data-part='expander'][aria-expanded='true'] > [data-ui='icon'] {
1476
+ [data-ui-table] [data-part-expander][aria-expanded='true'] > [data-ui-icon] {
1332
1477
  rotate: 90deg;
1333
1478
  }
1334
1479
 
1335
1480
  /* A chevron is direction-sensitive artwork, so it is mirrored rather than
1336
1481
  reused: a closed row in Arabic points to the start of the line, which is
1337
1482
  the right-hand side. */
1338
- [data-ui='table']:dir(rtl) [data-part='expander'] > [data-ui='icon'] {
1483
+ [data-ui-table]:dir(rtl) [data-part-expander] > [data-ui-icon] {
1339
1484
  scale: -1 1;
1340
1485
  }
1341
1486
 
1342
- [data-ui='table']:dir(rtl) [data-part='expander'][aria-expanded='true'] > [data-ui='icon'] {
1487
+ [data-ui-table]:dir(rtl) [data-part-expander][aria-expanded='true'] > [data-ui-icon] {
1343
1488
  rotate: -90deg;
1344
1489
  }
1345
1490
 
@@ -1351,25 +1496,25 @@
1351
1496
  * reach, and it takes the sunken ground so the record it belongs to still
1352
1497
  * reads as one thing.
1353
1498
  */
1354
- [data-ui='table'] [data-part='detail'] {
1499
+ [data-ui-table] [data-part-detail] {
1355
1500
  background-color: var(--_stripe);
1356
1501
  }
1357
1502
 
1358
- [data-ui='table'] [data-part='detail'] > [data-part='cell'] {
1503
+ [data-ui-table] [data-part-detail] > [data-part-cell] {
1359
1504
  padding-block: var(--_pad-block);
1360
1505
  padding-inline: var(--_pad-inline);
1361
1506
  }
1362
1507
 
1363
1508
  /* A detail row is not a record, so it takes no stripe of its own and never
1364
1509
  shifts the alternation of the rows around it. */
1365
- [data-ui='table'][data-striped] [data-part='body'] > [data-part='detail'] {
1510
+ [data-ui-table][data-striped] [data-part-body] > [data-part-detail] {
1366
1511
  background-color: var(--_stripe);
1367
1512
  }
1368
1513
 
1369
1514
  /**
1370
1515
  * ── Empty and loading ──────────────────────────────────────────────────
1371
1516
  */
1372
- [data-ui='table'] [data-part='empty'] {
1517
+ [data-ui-table] [data-part-empty] {
1373
1518
  color: var(--_muted);
1374
1519
  padding-block: var(--slotted-space-9, 2rem);
1375
1520
  padding-inline: var(--_pad-inline);
@@ -1379,7 +1524,7 @@
1379
1524
  /* The skeleton rows of a first load. The shapes are `skeleton`'s own; what
1380
1525
  this family supplies is the row that holds them, so the table's shape is
1381
1526
  visible before its content arrives. */
1382
- [data-ui='table'] [data-part='loader'] > [data-part='row'] > * {
1527
+ [data-ui-table] [data-part-loader] > [data-part-row] > * {
1383
1528
  padding-block: var(--_pad-block);
1384
1529
  padding-inline: var(--_pad-inline);
1385
1530
  }
@@ -1389,7 +1534,7 @@
1389
1534
  * body dims and nothing moves: the column widths are the table's, the scroll
1390
1535
  * position is the reader's, and swapping rows for skeletons would take both.
1391
1536
  */
1392
- [data-ui='table'][data-loading] [data-part='body'] {
1537
+ [data-ui-table][data-loading] [data-part-body] {
1393
1538
  opacity: 0.55;
1394
1539
  transition: opacity var(--slotted-control-transition-duration, var(--slotted-duration-short))
1395
1540
  var(--slotted-control-transition-easing, var(--slotted-easing-standard));
@@ -1404,7 +1549,7 @@
1404
1549
  * kind of boundary — and it is not the `footer`, which is a bar outside the
1405
1550
  * table holding the consumer's own controls.
1406
1551
  */
1407
- [data-ui='table'] [data-part='summary'] > [data-part='row'] > * {
1552
+ [data-ui-table] [data-part-summary] > [data-part-row] > * {
1408
1553
  background-color: var(--_head-surface);
1409
1554
  border-block-start: var(--_rule-width) solid var(--_rule-strong);
1410
1555
  font-weight: var(--slotted-font-weight-medium, 500);
@@ -1412,7 +1557,7 @@
1412
1557
  padding-inline: var(--_pad-inline);
1413
1558
  }
1414
1559
 
1415
- [data-ui='table'][data-variant='plain'] [data-part='summary'] > [data-part='row'] > * {
1560
+ [data-ui-table][data-variant='plain'] [data-part-summary] > [data-part-row] > * {
1416
1561
  background-color: transparent;
1417
1562
  }
1418
1563
 
@@ -1428,7 +1573,7 @@
1428
1573
  * a count, a bulk-action bar. The family draws the rule above it and the
1429
1574
  * inset around it, and nothing else.
1430
1575
  */
1431
- [data-ui='table'] [data-part='footer'] {
1576
+ [data-ui-table] [data-part-footer] {
1432
1577
  align-items: center;
1433
1578
  border-block-start: var(--_rule-width) solid var(--_rule);
1434
1579
  display: flex;
@@ -1450,7 +1595,7 @@
1450
1595
  * So the bar states the room instead of asking for it, and a control that
1451
1596
  * wants to sit at the trailing edge justifies itself there.
1452
1597
  */
1453
- [data-ui='table'] [data-part='footer'] > :last-child {
1598
+ [data-ui-table] [data-part-footer] > :last-child {
1454
1599
  flex: 1 1 auto;
1455
1600
  /* And it aligns its own content to the trailing edge, which is where a
1456
1601
  listing screen puts its paging control. The rule is structural rather
@@ -1467,15 +1612,15 @@
1467
1612
  * repeats at the top of every printed page.
1468
1613
  */
1469
1614
  @media print {
1470
- [data-ui='table'] [data-part='scroll'] {
1615
+ [data-ui-table] [data-part-scroll] {
1471
1616
  max-block-size: none;
1472
1617
  overflow: visible;
1473
1618
  }
1474
1619
 
1475
- [data-ui='table'][data-sticky-header] [data-part='column-header'],
1476
- [data-ui='table'][data-sticky-header] [data-part='head'] [data-part='selection'],
1477
- [data-ui='table'][data-sticky-footer] [data-part='summary'] > [data-part='row'] > *,
1478
- [data-ui='table'] [data-sticky] {
1620
+ [data-ui-table][data-sticky-header] [data-part-column-header],
1621
+ [data-ui-table][data-sticky-header] [data-part-head] [data-part-selection],
1622
+ [data-ui-table][data-sticky-footer] [data-part-summary] > [data-part-row] > *,
1623
+ [data-ui-table] [data-sticky] {
1479
1624
  position: static;
1480
1625
  }
1481
1626
  }