@slotted/styles 0.0.0-stage → 0.1.0-beta.0

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 (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/package.json +276 -3
  4. package/src/accordion/accordion.css +96 -0
  5. package/src/accordion/accordion.tokens.json +14 -0
  6. package/src/action-bar/action-bar.css +455 -0
  7. package/src/action-bar/action-bar.tokens.json +50 -0
  8. package/src/action-sheet/action-sheet.css +462 -0
  9. package/src/action-sheet/action-sheet.tokens.json +59 -0
  10. package/src/alert/alert.css +764 -0
  11. package/src/alert/alert.tokens.json +77 -0
  12. package/src/app-shell/app-shell.css +1899 -0
  13. package/src/app-shell/app-shell.tokens.json +88 -0
  14. package/src/aspect-ratio/aspect-ratio.css +131 -0
  15. package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
  16. package/src/attachment/attachment.css +906 -0
  17. package/src/attachment/attachment.tokens.json +85 -0
  18. package/src/avatar/avatar.css +598 -0
  19. package/src/avatar/avatar.tokens.json +64 -0
  20. package/src/avatar-group/avatar-group.css +268 -0
  21. package/src/avatar-group/avatar-group.tokens.json +16 -0
  22. package/src/badge/badge.css +462 -0
  23. package/src/badge/badge.tokens.json +58 -0
  24. package/src/breadcrumb/breadcrumb.css +441 -0
  25. package/src/breadcrumb/breadcrumb.tokens.json +50 -0
  26. package/src/button/button.css +754 -0
  27. package/src/button/button.tokens.json +72 -0
  28. package/src/button-group/button-group.css +455 -0
  29. package/src/button-group/button-group.tokens.json +11 -0
  30. package/src/calendar/calendar.css +846 -0
  31. package/src/calendar/calendar.tokens.json +67 -0
  32. package/src/card/card.css +602 -0
  33. package/src/card/card.tokens.json +68 -0
  34. package/src/carousel/carousel.css +860 -0
  35. package/src/carousel/carousel.tokens.json +62 -0
  36. package/src/cascader/cascader.css +1320 -0
  37. package/src/cascader/cascader.tokens.json +103 -0
  38. package/src/chart/chart.css +1473 -0
  39. package/src/chart/chart.tokens.json +107 -0
  40. package/src/checkbox/checkbox.css +1011 -0
  41. package/src/checkbox/checkbox.tokens.json +102 -0
  42. package/src/checkbox-group/checkbox-group.css +70 -0
  43. package/src/checkbox-group/checkbox-group.tokens.json +6 -0
  44. package/src/code-block/code-block.css +555 -0
  45. package/src/code-block/code-block.tokens.json +69 -0
  46. package/src/collapsible/collapsible.css +546 -0
  47. package/src/collapsible/collapsible.tokens.json +55 -0
  48. package/src/color-picker/color-picker.css +607 -0
  49. package/src/color-picker/color-picker.tokens.json +58 -0
  50. package/src/column-manager/column-manager.css +452 -0
  51. package/src/column-manager/column-manager.tokens.json +43 -0
  52. package/src/combobox/combobox.css +1048 -0
  53. package/src/combobox/combobox.tokens.json +112 -0
  54. package/src/command-palette/command-palette.css +1000 -0
  55. package/src/command-palette/command-palette.tokens.json +75 -0
  56. package/src/container/container.css +198 -0
  57. package/src/container/container.tokens.json +18 -0
  58. package/src/context-menu/context-menu.css +104 -0
  59. package/src/context-menu/context-menu.tokens.json +11 -0
  60. package/src/currency-field/currency-field.css +787 -0
  61. package/src/currency-field/currency-field.tokens.json +80 -0
  62. package/src/dashboard-grid/dashboard-grid.css +748 -0
  63. package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
  64. package/src/data-grid/data-grid.css +2484 -0
  65. package/src/data-grid/data-grid.tokens.json +100 -0
  66. package/src/data-list/data-list.css +455 -0
  67. package/src/data-list/data-list.tokens.json +35 -0
  68. package/src/date-field/date-field.css +560 -0
  69. package/src/date-field/date-field.tokens.json +80 -0
  70. package/src/date-picker/date-picker.css +420 -0
  71. package/src/date-picker/date-picker.tokens.json +49 -0
  72. package/src/date-range-picker/date-range-picker.css +983 -0
  73. package/src/date-range-picker/date-range-picker.tokens.json +109 -0
  74. package/src/datetime-field/datetime-field.css +850 -0
  75. package/src/datetime-field/datetime-field.tokens.json +91 -0
  76. package/src/datetime-picker/datetime-picker.css +843 -0
  77. package/src/datetime-picker/datetime-picker.tokens.json +78 -0
  78. package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
  79. package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
  80. package/src/description-list/description-list.css +675 -0
  81. package/src/description-list/description-list.tokens.json +66 -0
  82. package/src/detail-panel/detail-panel.css +329 -0
  83. package/src/detail-panel/detail-panel.tokens.json +33 -0
  84. package/src/dialog/dialog.css +968 -0
  85. package/src/dialog/dialog.tokens.json +66 -0
  86. package/src/divider/divider.css +131 -0
  87. package/src/divider/divider.tokens.json +18 -0
  88. package/src/drawer/drawer.css +1303 -0
  89. package/src/drawer/drawer.tokens.json +75 -0
  90. package/src/entity-picker/entity-picker.css +1367 -0
  91. package/src/entity-picker/entity-picker.tokens.json +118 -0
  92. package/src/event-calendar/event-calendar.css +2148 -0
  93. package/src/event-calendar/event-calendar.tokens.json +87 -0
  94. package/src/field/field.css +707 -0
  95. package/src/field/field.tokens.json +58 -0
  96. package/src/field-group/field-group.css +226 -0
  97. package/src/field-group/field-group.tokens.json +33 -0
  98. package/src/filter-bar/filter-bar.css +1306 -0
  99. package/src/filter-bar/filter-bar.tokens.json +78 -0
  100. package/src/form/form.css +218 -0
  101. package/src/form/form.tokens.json +32 -0
  102. package/src/fullscreen/fullscreen.css +376 -0
  103. package/src/fullscreen/fullscreen.tokens.json +50 -0
  104. package/src/grid/grid.css +173 -0
  105. package/src/grid/grid.tokens.json +20 -0
  106. package/src/highlight/highlight.css +135 -0
  107. package/src/highlight/highlight.tokens.json +16 -0
  108. package/src/hover-card/hover-card.css +93 -0
  109. package/src/hover-card/hover-card.tokens.json +17 -0
  110. package/src/icon/icon.css +136 -0
  111. package/src/icon/icon.tokens.json +14 -0
  112. package/src/image/image.css +449 -0
  113. package/src/image/image.tokens.json +27 -0
  114. package/src/import-export-center/import-export-center.css +1175 -0
  115. package/src/import-export-center/import-export-center.tokens.json +84 -0
  116. package/src/inline/inline.css +149 -0
  117. package/src/inline/inline.tokens.json +14 -0
  118. package/src/inline-message/inline-message.css +396 -0
  119. package/src/inline-message/inline-message.tokens.json +19 -0
  120. package/src/kbd/kbd.css +490 -0
  121. package/src/kbd/kbd.tokens.json +40 -0
  122. package/src/link/link.css +237 -0
  123. package/src/link/link.tokens.json +25 -0
  124. package/src/list/list.css +240 -0
  125. package/src/list/list.tokens.json +42 -0
  126. package/src/list-item/list-item.css +937 -0
  127. package/src/list-item/list-item.tokens.json +114 -0
  128. package/src/listbox/listbox.css +674 -0
  129. package/src/listbox/listbox.tokens.json +72 -0
  130. package/src/loading-overlay/loading-overlay.css +730 -0
  131. package/src/loading-overlay/loading-overlay.tokens.json +48 -0
  132. package/src/markdown/markdown.css +168 -0
  133. package/src/markdown/markdown.tokens.json +18 -0
  134. package/src/menu/menu.css +817 -0
  135. package/src/menu/menu.tokens.json +78 -0
  136. package/src/menubar/menubar.css +170 -0
  137. package/src/menubar/menubar.tokens.json +32 -0
  138. package/src/message/message.css +1453 -0
  139. package/src/message/message.tokens.json +85 -0
  140. package/src/message-scroller/message-scroller.css +371 -0
  141. package/src/message-scroller/message-scroller.tokens.json +47 -0
  142. package/src/multi-select/multi-select.css +1427 -0
  143. package/src/multi-select/multi-select.tokens.json +114 -0
  144. package/src/navigation-menu/navigation-menu.css +1029 -0
  145. package/src/navigation-menu/navigation-menu.tokens.json +70 -0
  146. package/src/notification-center/notification-center.css +856 -0
  147. package/src/notification-center/notification-center.tokens.json +84 -0
  148. package/src/number-field/number-field.css +794 -0
  149. package/src/number-field/number-field.tokens.json +80 -0
  150. package/src/otp-input/otp-input.css +525 -0
  151. package/src/otp-input/otp-input.tokens.json +69 -0
  152. package/src/page/page.css +179 -0
  153. package/src/page/page.tokens.json +20 -0
  154. package/src/page-header/page-header.css +1181 -0
  155. package/src/page-header/page-header.tokens.json +71 -0
  156. package/src/pagination/pagination.css +500 -0
  157. package/src/pagination/pagination.tokens.json +56 -0
  158. package/src/popover/popover.css +526 -0
  159. package/src/popover/popover.tokens.json +60 -0
  160. package/src/progress-indicator/progress-indicator.css +1584 -0
  161. package/src/progress-indicator/progress-indicator.tokens.json +50 -0
  162. package/src/prompt-composer/prompt-composer.css +680 -0
  163. package/src/prompt-composer/prompt-composer.tokens.json +99 -0
  164. package/src/prose/prose.css +733 -0
  165. package/src/prose/prose.tokens.json +79 -0
  166. package/src/radio-group/radio-group.css +778 -0
  167. package/src/radio-group/radio-group.tokens.json +103 -0
  168. package/src/rating/rating.css +1071 -0
  169. package/src/rating/rating.tokens.json +88 -0
  170. package/src/result-state/result-state.css +980 -0
  171. package/src/result-state/result-state.tokens.json +63 -0
  172. package/src/runtime-properties.mjs +321 -0
  173. package/src/saved-views/saved-views.css +394 -0
  174. package/src/saved-views/saved-views.tokens.json +41 -0
  175. package/src/scroll-area/scroll-area.css +481 -0
  176. package/src/scroll-area/scroll-area.tokens.json +15 -0
  177. package/src/section/section.css +177 -0
  178. package/src/section/section.tokens.json +39 -0
  179. package/src/segmented-control/segmented-control.css +564 -0
  180. package/src/segmented-control/segmented-control.tokens.json +67 -0
  181. package/src/select/select.css +1593 -0
  182. package/src/select/select.tokens.json +110 -0
  183. package/src/side-navigation/side-navigation.css +1096 -0
  184. package/src/side-navigation/side-navigation.tokens.json +87 -0
  185. package/src/skeleton/skeleton.css +431 -0
  186. package/src/skeleton/skeleton.tokens.json +29 -0
  187. package/src/slider/slider.css +792 -0
  188. package/src/slider/slider.tokens.json +75 -0
  189. package/src/sparkline/sparkline.css +282 -0
  190. package/src/sparkline/sparkline.tokens.json +45 -0
  191. package/src/spinner/spinner.css +224 -0
  192. package/src/spinner/spinner.tokens.json +8 -0
  193. package/src/split-button/split-button.css +253 -0
  194. package/src/split-button/split-button.tokens.json +12 -0
  195. package/src/splitter/splitter.css +336 -0
  196. package/src/splitter/splitter.tokens.json +24 -0
  197. package/src/stack/stack.css +128 -0
  198. package/src/stack/stack.tokens.json +14 -0
  199. package/src/stat-card/stat-card.css +641 -0
  200. package/src/stat-card/stat-card.tokens.json +95 -0
  201. package/src/status-indicator/status-indicator.css +481 -0
  202. package/src/status-indicator/status-indicator.tokens.json +38 -0
  203. package/src/stepper/stepper.css +629 -0
  204. package/src/stepper/stepper.tokens.json +72 -0
  205. package/src/switch/switch.css +703 -0
  206. package/src/switch/switch.tokens.json +75 -0
  207. package/src/table/table.css +1482 -0
  208. package/src/table/table.tokens.json +69 -0
  209. package/src/table-of-contents/table-of-contents.css +533 -0
  210. package/src/table-of-contents/table-of-contents.tokens.json +53 -0
  211. package/src/tabs/tabs.css +998 -0
  212. package/src/tabs/tabs.tokens.json +82 -0
  213. package/src/tag/tag.css +768 -0
  214. package/src/tag/tag.tokens.json +85 -0
  215. package/src/tags-input/tags-input.css +757 -0
  216. package/src/tags-input/tags-input.tokens.json +84 -0
  217. package/src/text-field/text-field.css +900 -0
  218. package/src/text-field/text-field.tokens.json +87 -0
  219. package/src/textarea/textarea.css +365 -0
  220. package/src/textarea/textarea.tokens.json +59 -0
  221. package/src/time-field/time-field.css +719 -0
  222. package/src/time-field/time-field.tokens.json +86 -0
  223. package/src/time-picker/time-picker.css +665 -0
  224. package/src/time-picker/time-picker.tokens.json +66 -0
  225. package/src/time-range-picker/time-range-picker.css +1287 -0
  226. package/src/time-range-picker/time-range-picker.tokens.json +134 -0
  227. package/src/timeline/timeline.css +1596 -0
  228. package/src/timeline/timeline.tokens.json +96 -0
  229. package/src/toast/toast.css +943 -0
  230. package/src/toast/toast.tokens.json +90 -0
  231. package/src/toggle/toggle.css +290 -0
  232. package/src/toggle/toggle.tokens.json +54 -0
  233. package/src/toggle-group/toggle-group.css +463 -0
  234. package/src/toggle-group/toggle-group.tokens.json +54 -0
  235. package/src/toolbar/toolbar.css +434 -0
  236. package/src/toolbar/toolbar.tokens.json +49 -0
  237. package/src/tooltip/tooltip.css +368 -0
  238. package/src/tooltip/tooltip.tokens.json +39 -0
  239. package/src/top-navigation/top-navigation.css +1287 -0
  240. package/src/top-navigation/top-navigation.tokens.json +91 -0
  241. package/src/tour/tour.css +680 -0
  242. package/src/tour/tour.tokens.json +67 -0
  243. package/src/transfer/transfer.css +571 -0
  244. package/src/transfer/transfer.tokens.json +49 -0
  245. package/src/tree/tree.css +1000 -0
  246. package/src/tree/tree.tokens.json +80 -0
  247. package/src/typography/typography.css +211 -0
  248. package/src/typography/typography.tokens.json +31 -0
  249. package/src/upload/upload.css +1706 -0
  250. package/src/upload/upload.tokens.json +123 -0
  251. package/src/visually-hidden/visually-hidden.css +52 -0
  252. package/src/visually-hidden/visually-hidden.tokens.json +1 -0
  253. package/src/wizard/wizard.css +740 -0
  254. package/src/wizard/wizard.tokens.json +63 -0
@@ -0,0 +1,72 @@
1
+ [
2
+ "--slotted-border-subtle",
3
+ "--slotted-border-width-thin",
4
+ "--slotted-control-border-color",
5
+ "--slotted-control-border-width",
6
+ "--slotted-control-font-family",
7
+ "--slotted-control-font-size-lg",
8
+ "--slotted-control-font-size-md",
9
+ "--slotted-control-font-size-sm",
10
+ "--slotted-control-font-weight",
11
+ "--slotted-control-gap-lg",
12
+ "--slotted-control-gap-md",
13
+ "--slotted-control-gap-sm",
14
+ "--slotted-control-height-lg",
15
+ "--slotted-control-height-md",
16
+ "--slotted-control-height-sm",
17
+ "--slotted-control-icon-size-lg",
18
+ "--slotted-control-icon-size-md",
19
+ "--slotted-control-icon-size-sm",
20
+ "--slotted-control-letter-spacing",
21
+ "--slotted-control-line-height",
22
+ "--slotted-control-padding-inline-lg",
23
+ "--slotted-control-padding-inline-md",
24
+ "--slotted-control-padding-inline-sm",
25
+ "--slotted-control-radius",
26
+ "--slotted-control-shadow",
27
+ "--slotted-control-transition-duration",
28
+ "--slotted-control-transition-easing",
29
+ "--slotted-disabled-background",
30
+ "--slotted-disabled-border",
31
+ "--slotted-disabled-foreground",
32
+ "--slotted-duration-instant",
33
+ "--slotted-duration-loop",
34
+ "--slotted-focus-ring-color",
35
+ "--slotted-focus-ring-offset",
36
+ "--slotted-focus-ring-width",
37
+ "--slotted-line-height-sm",
38
+ "--slotted-radius-full",
39
+ "--slotted-space-2",
40
+ "--slotted-target-min",
41
+ "--slotted-text-muted",
42
+ "--slotted-tone-accent-border",
43
+ "--slotted-tone-accent-on-solid",
44
+ "--slotted-tone-accent-solid",
45
+ "--slotted-tone-accent-solid-active",
46
+ "--slotted-tone-accent-solid-hover",
47
+ "--slotted-tone-accent-subtle-active",
48
+ "--slotted-tone-accent-subtle-hover",
49
+ "--slotted-tone-accent-text",
50
+ "--slotted-tone-danger-on-solid",
51
+ "--slotted-tone-danger-solid",
52
+ "--slotted-tone-danger-solid-active",
53
+ "--slotted-tone-danger-solid-hover",
54
+ "--slotted-tone-danger-text",
55
+ "--slotted-tone-secondary-on-solid",
56
+ "--slotted-tone-secondary-solid",
57
+ "--slotted-tone-secondary-solid-active",
58
+ "--slotted-tone-secondary-solid-hover",
59
+ "--slotted-tone-secondary-subtle-active",
60
+ "--slotted-tone-secondary-subtle-hover",
61
+ "--slotted-tone-secondary-text",
62
+ "--slotted-tone-success-on-solid",
63
+ "--slotted-tone-success-solid",
64
+ "--slotted-tone-success-solid-active",
65
+ "--slotted-tone-success-solid-hover",
66
+ "--slotted-tone-success-text",
67
+ "--slotted-tone-warning-on-solid",
68
+ "--slotted-tone-warning-solid",
69
+ "--slotted-tone-warning-solid-active",
70
+ "--slotted-tone-warning-solid-hover",
71
+ "--slotted-tone-warning-text"
72
+ ]
@@ -0,0 +1,455 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A set of related actions joined into one shape. The family arranges what
4
+ * the consumer wrote and changes the semantics of nothing inside it: every
5
+ * button keeps its own role, its own state and its own place in the tab
6
+ * sequence. The arrow-key model of the APG Toolbar pattern belongs to
7
+ * `toolbar`, which is a different family for a different thing — a
8
+ * composite control with one tab stop.
9
+ *
10
+ * One shape and no other. The group used to join its controls when asked
11
+ * and to space them by a step on the space scale otherwise, which was two
12
+ * families under one name: a bar, and a `stack` of buttons. The second is
13
+ * what `inline` and `stack` already are, so the group keeps the one thing
14
+ * only it does — shared edges, one border in each shared column, corners on
15
+ * the ends of the bar and nowhere else — and states no distance between its
16
+ * controls. A joined bar does not wrap and does not stack: its shape is the
17
+ * whole point of it, and no surveyed implementation flips that shape at a
18
+ * width the consumer cannot see coming. A label that outgrows the bar wraps
19
+ * inside its own control, which is `button`'s rule and not this family's.
20
+ *
21
+ * The children are the consumer's own elements, selected as direct children.
22
+ * Primer wraps each child in a div of its own and pays for it twice: its
23
+ * stylesheet has to reach two levels down to find the control, and an empty
24
+ * wrapper needs a `:has(div:last-child:empty)` clause to undo the corners it
25
+ * caused. Nothing here wraps anything.
26
+ *
27
+ * The group paints one thing and only one: the seam between two joined solid
28
+ * controls. That rule is documented where it is written.
29
+ */
30
+ [data-ui='button-group'] {
31
+ --_radius: var(--slotted-control-radius, 4px);
32
+ --_overlap: calc(-1 * var(--slotted-control-border-width, 1px));
33
+ /**
34
+ * Which corner of a child survives, resolved once per axis so the four
35
+ * corner rules below are written once instead of twice.
36
+ *
37
+ * A child keeps the corners on the outside of the bar and loses the ones
38
+ * facing its neighbour. `--_join-main` is the corner on the axis the group
39
+ * runs along, `--_join-cross` the one across it, and swapping the two is
40
+ * the whole difference between a row and a column.
41
+ */
42
+ --_join-main: var(--_radius);
43
+ --_join-cross: 0;
44
+ --_overlap-inline: var(--_overlap);
45
+ --_overlap-block: 0;
46
+
47
+ /**
48
+ * One block size for the bar, so a consumer who mixes sizes gets a ragged
49
+ * width rather than a ragged edge. The same answer on both axes: a column
50
+ * of controls at their own natural widths is a ragged edge too.
51
+ */
52
+ align-items: stretch;
53
+ display: inline-flex;
54
+ flex-direction: row;
55
+ flex-wrap: nowrap;
56
+ gap: 0;
57
+ /* A child raised on interaction is raised inside the group, never over the page. */
58
+ isolation: isolate;
59
+ /**
60
+ * A flex or grid item is at least as wide as its content unless it is told
61
+ * otherwise, so a group nested in one would refuse to narrow below its
62
+ * widest child and push the layout past the edge instead of reflowing.
63
+ */
64
+ min-inline-size: 0;
65
+ }
66
+
67
+ /**
68
+ * A group that ends up holding nothing takes no room. An actions area built
69
+ * from a permission filter renders an empty group often, and no surveyed
70
+ * implementation answers it: the empty box still consumes a gap in the row
71
+ * that holds it, so the toolbar beside it drifts by half a centimetre for a
72
+ * reason nobody can see in the markup.
73
+ */
74
+ [data-ui='button-group']:empty {
75
+ display: none;
76
+ }
77
+
78
+ /* ── Orientation ──────────────────────────────────────────────────────── */
79
+
80
+ [data-ui='button-group'][data-orientation='vertical'] {
81
+ --_join-main: 0;
82
+ --_join-cross: var(--_radius);
83
+ --_overlap-inline: 0;
84
+ --_overlap-block: var(--_overlap);
85
+
86
+ flex-direction: column;
87
+ }
88
+
89
+ /* ── Across the flow ──────────────────────────────────────────────────── */
90
+
91
+ [data-ui='button-group'][data-align='start'] {
92
+ align-items: start;
93
+ }
94
+
95
+ [data-ui='button-group'][data-align='center'] {
96
+ align-items: center;
97
+ }
98
+
99
+ [data-ui='button-group'][data-align='end'] {
100
+ align-items: end;
101
+ }
102
+
103
+ [data-ui='button-group'][data-align='stretch'] {
104
+ align-items: stretch;
105
+ }
106
+
107
+ /**
108
+ * ── Along the flow ────────────────────────────────────────────────────
109
+ *
110
+ * Placing the buttons along the line only means something once the group
111
+ * holds the line, so asking for it is asking for that. A group left alone
112
+ * shrinks to its content, which is what a pair of actions beside a sentence
113
+ * needs and what every surveyed implementation defaults to.
114
+ */
115
+ [data-ui='button-group'][data-justify] {
116
+ display: flex;
117
+ inline-size: 100%;
118
+ }
119
+
120
+ [data-ui='button-group'][data-justify='start'] {
121
+ justify-content: start;
122
+ }
123
+
124
+ [data-ui='button-group'][data-justify='center'] {
125
+ justify-content: center;
126
+ }
127
+
128
+ [data-ui='button-group'][data-justify='end'] {
129
+ justify-content: end;
130
+ }
131
+
132
+ [data-ui='button-group'][data-justify='between'] {
133
+ justify-content: space-between;
134
+ }
135
+
136
+ /* ── Full width ───────────────────────────────────────────────────────── */
137
+
138
+ [data-ui='button-group'][data-full-width] {
139
+ display: flex;
140
+ inline-size: 100%;
141
+ }
142
+
143
+ /**
144
+ * Equal segments rather than proportional ones. A row where "Delete" is a
145
+ * third of the width of "Save and publish" is a row whose emphasis is set by
146
+ * the length of a translated string.
147
+ *
148
+ * A column is already stretched across its own axis, so growing its children
149
+ * along the flow would distribute height instead, which is not what anyone
150
+ * asking for a full-width group meant.
151
+ */
152
+ [data-ui='button-group'][data-full-width]:not([data-orientation='vertical'])
153
+ > :not([data-part='separator']) {
154
+ flex: 1 1 0;
155
+ min-inline-size: 0;
156
+ }
157
+
158
+ /**
159
+ * ── The shape ─────────────────────────────────────────────────────────
160
+ *
161
+ * One shape out of several controls: the corners facing a neighbour go, the
162
+ * two adjacent borders become one, and the bar takes a single block size.
163
+ *
164
+ * A label that outgrows the bar wraps inside its own control rather than
165
+ * pushing the bar past the container. An icon-only control is exempt: it is
166
+ * square from the control height, and shrinking it would take it below the
167
+ * target size its own family guarantees.
168
+ */
169
+ [data-ui='button-group'] > :not([data-part='separator']):not([data-shape]) {
170
+ min-inline-size: 0;
171
+ }
172
+
173
+ [data-ui='button-group'] > [data-shape] {
174
+ flex: none;
175
+ }
176
+
177
+ [data-ui='button-group'] > :not([data-ui='button-group']) {
178
+ border-radius: 0;
179
+ }
180
+
181
+ /**
182
+ * The overlap skips the separator, and only the separator: a rule inside the
183
+ * bar takes no column of its own, so the control after it shares an edge
184
+ * with the control before it exactly like every other neighbour pair.
185
+ */
186
+ [data-ui='button-group'] > :not(:first-child):not([data-part='separator']) {
187
+ margin-block-start: var(--_overlap-block);
188
+ margin-inline-start: var(--_overlap-inline);
189
+ }
190
+
191
+ [data-ui='button-group'] > :first-child {
192
+ border-end-start-radius: var(--_join-main);
193
+ border-start-end-radius: var(--_join-cross);
194
+ border-start-start-radius: var(--_radius);
195
+ }
196
+
197
+ [data-ui='button-group'] > :last-child {
198
+ border-end-end-radius: var(--_radius);
199
+ border-end-start-radius: var(--_join-cross);
200
+ border-start-end-radius: var(--_join-main);
201
+ }
202
+
203
+ /**
204
+ * A bar of one control is both ends of the bar, and the two rules above
205
+ * disagree about its inner corners: the second one wins, and the control
206
+ * came out with one square corner. Measured on the harness before this rule
207
+ * existed: 4/4/4/0. A lone control keeps all four.
208
+ */
209
+ [data-ui='button-group'] > :only-child {
210
+ border-radius: var(--_radius);
211
+ }
212
+
213
+ /**
214
+ * ── Runs ──────────────────────────────────────────────────────────────
215
+ *
216
+ * A nested group is a run of the bar with a name of its own, which is what
217
+ * an announced boundary cannot say: a separator says a boundary exists, a
218
+ * named group says what is on either side of it. Visually it is the same
219
+ * bar. The run's own corner rules would round its ends in the middle of the
220
+ * bar, so inside another group every corner of a run is squared and only
221
+ * the two on the ends of the outer bar are put back.
222
+ */
223
+ [data-ui='button-group'] [data-ui='button-group'] > :not([data-part='separator']) {
224
+ border-radius: 0;
225
+ }
226
+
227
+ [data-ui='button-group'] > [data-ui='button-group']:first-child > :first-child {
228
+ border-end-start-radius: var(--_join-main);
229
+ border-start-end-radius: var(--_join-cross);
230
+ border-start-start-radius: var(--_radius);
231
+ }
232
+
233
+ [data-ui='button-group'] > [data-ui='button-group']:last-child > :last-child {
234
+ border-end-end-radius: var(--_radius);
235
+ border-end-start-radius: var(--_join-cross);
236
+ border-start-end-radius: var(--_join-main);
237
+ }
238
+
239
+ /**
240
+ * ── The frame belongs to the group ────────────────────────────────────
241
+ *
242
+ * A joined bar has one edge, and one edge cannot be the sum of several
243
+ * controls' opinions about their own. `button` repaints its whole border
244
+ * when it is pressed or unavailable — correctly, for a control standing on
245
+ * its own — and inside a bar that punches a hole in the frame exactly where
246
+ * that control sits. It was visible before it was reasoned about: a vertical
247
+ * segmented control with the middle segment on had a gap in its left edge.
248
+ *
249
+ * So the group states the edge for the two variants that have one to state,
250
+ * and it states it for the states that would otherwise overwrite it. The
251
+ * control keeps its fill, its label and its cursor; what it gives up is the
252
+ * right to redraw the bar around itself.
253
+ *
254
+ * The root is selected by its part as well as its family. The rule has to
255
+ * outrank every border rule `button` writes for the same control whatever
256
+ * order the two sheets load in, and `button`'s pressed rule already carries
257
+ * four attribute-class selectors; the part the root always carries is the
258
+ * fifth, and it is a name the contract declares rather than a selector
259
+ * repeated for weight.
260
+ */
261
+ [data-ui='button-group'][data-part='root']
262
+ > [data-ui='button'][data-variant='outline'][data-pressed],
263
+ [data-ui='button-group'][data-part='root']
264
+ > [data-ui='button'][data-variant='outline'][data-disabled],
265
+ [data-ui='button-group'][data-part='root'] > [data-ui='button'][data-variant='outline']:disabled {
266
+ border-color: var(--slotted-control-border-color, ButtonBorder);
267
+ }
268
+
269
+ [data-ui='button-group'][data-part='root']
270
+ > [data-ui='button'][data-variant='ghost'][data-pressed],
271
+ [data-ui='button-group'][data-part='root']
272
+ > [data-ui='button'][data-variant='ghost'][data-disabled],
273
+ [data-ui='button-group'][data-part='root'] > [data-ui='button'][data-variant='ghost']:disabled {
274
+ border-color: transparent;
275
+ }
276
+
277
+ /**
278
+ * ── One border in each shared column ──────────────────────────────────
279
+ *
280
+ * The overlap puts two borders in the same one-pixel column. They are the
281
+ * same colour and the same width, so on paper they are one line — and they
282
+ * are, until the page is zoomed. The browser snaps each border box to device
283
+ * pixels on its own, so at 110%, 150% and 175% one boundary lands on a single
284
+ * column and the next lands across two, and a bar of equal dividers renders
285
+ * with one of them twice as thick as its neighbour. Measured, not guessed:
286
+ * five boundaries out of the fifteen photographed came out uneven before this
287
+ * rule existed.
288
+ *
289
+ * So exactly one border is painted there, and it is the leading edge of the
290
+ * control that follows. The trailing edge of the control before it is taken
291
+ * out of the paint entirely — not narrowed, because a narrower border box
292
+ * would move the glyph inside an icon-only control by half a pixel.
293
+ *
294
+ * A rule between two runs is that same column, so the control before it
295
+ * gives up its trailing edge in the same way, and the control after it
296
+ * paints the rule on its own leading one. The last control of a run that is
297
+ * not the last run gives it up too: the next run's first control follows it
298
+ * in the same column.
299
+ */
300
+ [data-ui='button-group'][data-part='root']:not([data-orientation='vertical'])
301
+ > [data-ui='button']:not(:last-child),
302
+ [data-ui='button-group'][data-part='root']:not([data-orientation='vertical'])
303
+ > [data-ui='button-group']:not(:last-child)
304
+ > [data-ui='button']:last-child {
305
+ border-inline-end-color: transparent;
306
+ }
307
+
308
+ [data-ui='button-group'][data-part='root'][data-orientation='vertical']
309
+ > [data-ui='button']:not(:last-child),
310
+ [data-ui='button-group'][data-part='root'][data-orientation='vertical']
311
+ > [data-ui='button-group']:not(:last-child)
312
+ > [data-ui='button']:last-child {
313
+ border-block-end-color: transparent;
314
+ }
315
+
316
+ /**
317
+ * ── The seam between two filled controls ──────────────────────────────
318
+ *
319
+ * `button` paints a filled control's edge with its own fill — deliberately,
320
+ * because a border a shade away from what it encloses reads as a halo — so
321
+ * two joined solid controls are one uninterrupted rectangle with no way to
322
+ * tell where the first ends.
323
+ *
324
+ * The seam is the darkest step of the control's own tone, which is what
325
+ * Primer, Material UI, Ant Design and Bootstrap all reach for. The first
326
+ * attempt used `--slotted-surface`, and the capture said no: a white line
327
+ * through a filled bar cuts it into separate buttons instead of dividing one
328
+ * bar, and on the boundary between an outline control and a filled one it
329
+ * appeared where no boundary belonged at all.
330
+ *
331
+ * It is painted on the leading edge only, because that is the one edge in
332
+ * the column the rule above leaves painted. The tone is read from
333
+ * `data-tone`, which `button` declares in public, so this is composition
334
+ * against a contract rather than against internal DOM.
335
+ */
336
+ [data-ui='button-group'] > [data-tone='accent'] {
337
+ --_seam: var(--slotted-tone-accent-solid-active);
338
+ }
339
+
340
+ [data-ui='button-group'] > [data-tone='secondary'] {
341
+ --_seam: var(--slotted-tone-secondary-solid-active);
342
+ }
343
+
344
+ [data-ui='button-group'] > [data-tone='success'] {
345
+ --_seam: var(--slotted-tone-success-solid-active);
346
+ }
347
+
348
+ [data-ui='button-group'] > [data-tone='warning'] {
349
+ --_seam: var(--slotted-tone-warning-solid-active);
350
+ }
351
+
352
+ [data-ui='button-group'] > [data-tone='danger'] {
353
+ --_seam: var(--slotted-tone-danger-solid-active);
354
+ }
355
+
356
+ [data-ui='button-group'][data-part='root']:not([data-orientation='vertical'])
357
+ > [data-ui='button'][data-variant='solid']:not(:first-child) {
358
+ border-inline-start-color: var(--_seam, ButtonBorder);
359
+ }
360
+
361
+ [data-ui='button-group'][data-part='root'][data-orientation='vertical']
362
+ > [data-ui='button'][data-variant='solid']:not(:first-child) {
363
+ border-block-start-color: var(--_seam, ButtonBorder);
364
+ }
365
+
366
+ /**
367
+ * Only focus is raised. The ring is an outline drawn two pixels outside the
368
+ * border box, so without this it is cut by whichever neighbour follows it in
369
+ * the DOM. Hover and press are raised by Primer, Polaris and shadcn because
370
+ * in those libraries a touched control repaints its own edge; here the edge
371
+ * is the group's, so nothing about it changes and there is nothing to lift.
372
+ */
373
+ [data-ui='button-group'] > *:focus-visible,
374
+ [data-ui='button-group'] > *:has(:focus-visible) {
375
+ z-index: 1;
376
+ }
377
+
378
+ /**
379
+ * Under forced colours the theme flattens every fill, so a segment that is
380
+ * on has only its edge left to say so. `button` makes that edge `Highlight`;
381
+ * the rule above would have taken it away, and this puts it back.
382
+ */
383
+ @media (forced-colors: active) {
384
+ [data-ui='button-group'][data-part='root'] > [data-ui='button'][data-pressed] {
385
+ border-color: Highlight;
386
+ }
387
+ }
388
+
389
+ /* ── Separator ────────────────────────────────────────────────────────── */
390
+
391
+ /**
392
+ * A rule between two runs of actions. It is a part of this family and not
393
+ * the `divider` family because its geometry belongs to the group: inside a
394
+ * joined bar it is the shared edge rather than a box standing next to one.
395
+ * Styling `divider`'s rendered box from this stylesheet would make one
396
+ * family's DOM a private API of another, which is the coupling
397
+ * `identity.test.mjs` exists to refuse.
398
+ *
399
+ * A joined bar is one shape, and a rule is not licence to cut it. So the
400
+ * rule is not a box at all: it takes no width, no height and no margin, and
401
+ * what it marks is painted on the leading edge of the control that follows
402
+ * it — the one edge the overlap already leaves in the shared column.
403
+ *
404
+ * The rule had a column of its own before this, and the bar paid for it
405
+ * twice. A control with its edge taken out still occupies the column it gave
406
+ * up, so the boundary came out three pixels wide beside seams of one: at
407
+ * every zoom step the capture showed one divider thicker than its
408
+ * neighbours, and in a framed bar that reads as a rendering defect rather
409
+ * than as a boundary. Nothing about the bar's shape changes now — the
410
+ * corners, the height and the overlap are what they are without the rule.
411
+ */
412
+ [data-ui='button-group'] > [data-part='separator'] {
413
+ background: none;
414
+ block-size: 0;
415
+ border: 0;
416
+ flex: none;
417
+ inline-size: 0;
418
+ margin: 0;
419
+ }
420
+
421
+ /**
422
+ * One step stronger than the seams around it, because a boundary painted in
423
+ * the seam's own colour is a seam. `--slotted-text-muted` is the quietest
424
+ * tone in the foundation that still reads darker than `--slotted-border-
425
+ * strong`, and it carries the difference the eye needs without the weight a
426
+ * thicker line would add. In a frameless bar it is the only line there is,
427
+ * which is the divider a quiet icon toolbar needs.
428
+ *
429
+ * A filled bar keeps its seam. Its edges are the tone's own darkest step,
430
+ * and a neutral grey laid between two saturated fills reads as a smudge
431
+ * rather than as a boundary — the capture showed it olive against accent
432
+ * blue. There is nothing darker than that step in the tone to reach for, so
433
+ * a rule inside a filled run is the seam, drawn where the consumer asked
434
+ * for it.
435
+ */
436
+ [data-ui='button-group'][data-part='root']:not([data-orientation='vertical'])
437
+ > [data-part='separator']
438
+ + [data-ui='button']:not([data-variant='solid']),
439
+ [data-ui='button-group'][data-part='root']:not([data-orientation='vertical'])
440
+ > [data-part='separator']
441
+ + [data-ui='button-group']
442
+ > [data-ui='button']:first-child:not([data-variant='solid']) {
443
+ border-inline-start-color: var(--slotted-text-muted, ButtonBorder);
444
+ }
445
+
446
+ [data-ui='button-group'][data-part='root'][data-orientation='vertical']
447
+ > [data-part='separator']
448
+ + [data-ui='button']:not([data-variant='solid']),
449
+ [data-ui='button-group'][data-part='root'][data-orientation='vertical']
450
+ > [data-part='separator']
451
+ + [data-ui='button-group']
452
+ > [data-ui='button']:first-child:not([data-variant='solid']) {
453
+ border-block-start-color: var(--slotted-text-muted, ButtonBorder);
454
+ }
455
+ }
@@ -0,0 +1,11 @@
1
+ [
2
+ "--slotted-control-border-color",
3
+ "--slotted-control-border-width",
4
+ "--slotted-control-radius",
5
+ "--slotted-text-muted",
6
+ "--slotted-tone-accent-solid-active",
7
+ "--slotted-tone-danger-solid-active",
8
+ "--slotted-tone-secondary-solid-active",
9
+ "--slotted-tone-success-solid-active",
10
+ "--slotted-tone-warning-solid-active"
11
+ ]