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

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,253 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── An action and the menu of its alternatives, joined ─────────────────
4
+ *
5
+ * The root is a `div` carrying `role="group"`; inside it stand two elements
6
+ * of the `button` family — a Button carrying this family's `primary` part
7
+ * and an IconButton carrying `menu-trigger`, rendered as `menu`'s trigger.
8
+ * `button` selects itself by `data-ui` alone and reads no `data-part` on
9
+ * its root, so the part slot is free for the family that owns the
10
+ * composition; what this sheet writes is composition against `button`'s
11
+ * public contract — `data-ui`, `data-tone`, `data-variant`, `data-shape`,
12
+ * `data-pressed`, `data-disabled` — and never against its inner DOM, save
13
+ * the one part `button` declares in public for its icon.
14
+ *
15
+ * The family paints two things and only two: the join, and the chevron.
16
+ * Every tone, variant, size, density, hover, press, focus ring, loading
17
+ * indicator and disabled reading is `button`'s, which is the reason the two
18
+ * halves are `button`'s elements rather than this family's own.
19
+ *
20
+ * ── The join is `button-group`'s, for two children on one axis ─────────
21
+ *
22
+ * `button-group` keys on its own identifier and the root here carries this
23
+ * family's, so the rules that make one shape of two controls are restated,
24
+ * each citing the measurement the group made:
25
+ *
26
+ * - the two overlap by one border width, so two joined edges occupy one
27
+ * column rather than two;
28
+ * - exactly one border is painted in that column — the leading edge of the
29
+ * trigger — because two of them are one line on paper and two different
30
+ * lines at 110%, 150% and 175% zoom, where the browser snaps each border
31
+ * box to device pixels on its own (five uneven boundaries out of fifteen,
32
+ * measured by `button-group` before it wrote the rule);
33
+ * - the corners on the two ends of the bar survive and the two facing the
34
+ * seam go;
35
+ * - the seam between two solid halves is the tone's darkest step, read from
36
+ * `data-tone`, which `button` declares in public;
37
+ * - the frame is stated by the family under `pressed` and `disabled` for the
38
+ * two variants that have one, so a half does not redraw the bar around
39
+ * itself;
40
+ * - only focus is raised.
41
+ */
42
+ [data-ui='split-button'][data-part='root'] {
43
+ --_radius: var(--slotted-split-button-radius, var(--slotted-control-radius, 4px));
44
+ --_overlap: calc(-1 * var(--slotted-control-border-width, 1px));
45
+
46
+ align-items: stretch;
47
+ display: inline-flex;
48
+ flex-direction: row;
49
+ flex-wrap: nowrap;
50
+ gap: 0;
51
+ /* A half raised by focus is raised inside the group, never over the page. */
52
+ isolation: isolate;
53
+ /**
54
+ * A flex item is at least as wide as its content unless it is told
55
+ * otherwise, so a split button nested in one would refuse to narrow below
56
+ * its label and push the layout past the edge instead of letting the
57
+ * label wrap inside its own control, which is `button`'s rule.
58
+ */
59
+ min-inline-size: 0;
60
+ /* A control written inside a sentence sits on the text's centre. */
61
+ vertical-align: middle;
62
+ }
63
+
64
+ /* ── Width ────────────────────────────────────────────────────────────── */
65
+
66
+ /**
67
+ * Asking for the whole line means the action takes it: the trigger is
68
+ * square from the control height and stays so, because a wide chevron is a
69
+ * second button nobody asked for.
70
+ */
71
+ [data-ui='split-button'][data-part='root'][data-full-width] {
72
+ display: flex;
73
+ inline-size: 100%;
74
+ }
75
+
76
+ [data-ui='split-button'][data-part='root'][data-full-width] > [data-part='primary'] {
77
+ flex: 1 1 0;
78
+ min-inline-size: 0;
79
+ }
80
+
81
+ /* ── The shape ────────────────────────────────────────────────────────── */
82
+
83
+ [data-ui='split-button'][data-part='root'] > [data-part='primary'] {
84
+ border-end-end-radius: 0;
85
+ border-end-start-radius: var(--_radius);
86
+ border-start-end-radius: 0;
87
+ border-start-start-radius: var(--_radius);
88
+ /* A label that outgrows the bar wraps inside its own control rather than
89
+ pushing the bar past the container. */
90
+ min-inline-size: 0;
91
+ }
92
+
93
+ [data-ui='split-button'][data-part='root'] > [data-part='menu-trigger'] {
94
+ border-end-end-radius: var(--_radius);
95
+ border-end-start-radius: 0;
96
+ border-start-end-radius: var(--_radius);
97
+ border-start-start-radius: 0;
98
+ /* Square from the control height; never shrinks below its own target. */
99
+ flex: none;
100
+ margin-inline-start: var(--_overlap);
101
+ }
102
+
103
+ /**
104
+ * ── The frame belongs to the group ────────────────────────────────────
105
+ *
106
+ * `button` repaints its whole border when it is pressed or unavailable —
107
+ * correctly, for a control standing on its own — and inside a bar that
108
+ * punches a hole in the frame exactly where that control sits. The family
109
+ * states the edge for the two variants that have one to state, and it
110
+ * states it for the states that would otherwise overwrite it.
111
+ *
112
+ * The root is selected by its part as well as its family, for the reason
113
+ * `button-group` records: the rule has to outrank every border rule `button`
114
+ * writes for the same control whatever order the two sheets load in.
115
+ */
116
+ [data-ui='split-button'][data-part='root']
117
+ > [data-ui='button'][data-variant='outline'][data-pressed],
118
+ [data-ui='split-button'][data-part='root']
119
+ > [data-ui='button'][data-variant='outline'][data-disabled],
120
+ [data-ui='split-button'][data-part='root'] > [data-ui='button'][data-variant='outline']:disabled {
121
+ border-color: var(--slotted-control-border-color, ButtonBorder);
122
+ }
123
+
124
+ [data-ui='split-button'][data-part='root']
125
+ > [data-ui='button'][data-variant='ghost'][data-pressed],
126
+ [data-ui='split-button'][data-part='root']
127
+ > [data-ui='button'][data-variant='ghost'][data-disabled],
128
+ [data-ui='split-button'][data-part='root'] > [data-ui='button'][data-variant='ghost']:disabled {
129
+ border-color: transparent;
130
+ }
131
+
132
+ /**
133
+ * ── One border in the shared column ───────────────────────────────────
134
+ *
135
+ * The primary's trailing edge is taken out of the paint entirely — not
136
+ * narrowed, because a narrower border box would move its label by half a
137
+ * pixel — and the trigger's leading edge is the one seen.
138
+ */
139
+ [data-ui='split-button'][data-part='root']
140
+ > [data-ui='button'][data-part='primary'][data-variant] {
141
+ border-inline-end-color: transparent;
142
+ }
143
+
144
+ /**
145
+ * ── The seam between two filled halves ────────────────────────────────
146
+ *
147
+ * `button` paints a filled control's edge with its own fill, so two joined
148
+ * solid halves are one uninterrupted rectangle with no way to tell where
149
+ * the action ends and the menu begins. The seam is the darkest step of the
150
+ * control's own tone — Primer, Material UI, Ant Design and Bootstrap all
151
+ * reach for it, and `button-group` measured the alternative: a white line
152
+ * through a filled bar cuts it into separate buttons instead of dividing
153
+ * one. It is painted on the trigger's leading edge, the one edge the rule
154
+ * above leaves in the column.
155
+ */
156
+ [data-ui='split-button'][data-part='root'] > [data-tone='accent'] {
157
+ --_seam: var(--slotted-tone-accent-solid-active);
158
+ }
159
+
160
+ [data-ui='split-button'][data-part='root'] > [data-tone='secondary'] {
161
+ --_seam: var(--slotted-tone-secondary-solid-active);
162
+ }
163
+
164
+ [data-ui='split-button'][data-part='root'] > [data-tone='success'] {
165
+ --_seam: var(--slotted-tone-success-solid-active);
166
+ }
167
+
168
+ [data-ui='split-button'][data-part='root'] > [data-tone='warning'] {
169
+ --_seam: var(--slotted-tone-warning-solid-active);
170
+ }
171
+
172
+ [data-ui='split-button'][data-part='root'] > [data-tone='danger'] {
173
+ --_seam: var(--slotted-tone-danger-solid-active);
174
+ }
175
+
176
+ [data-ui='split-button'][data-part='root']
177
+ > [data-ui='button'][data-variant='solid'][data-part='menu-trigger'] {
178
+ border-inline-start-color: var(--_seam, ButtonBorder);
179
+ }
180
+
181
+ /**
182
+ * A filled half that is unavailable has no darkest step to draw the seam
183
+ * in: `button` paints it the disabled ground inside the disabled border,
184
+ * and the seam takes that border so the bar reads as one unavailable
185
+ * control rather than as a grey one with a coloured line through it.
186
+ */
187
+ [data-ui='split-button'][data-part='root']
188
+ > [data-ui='button'][data-variant='solid'][data-part='menu-trigger']:disabled,
189
+ [data-ui='split-button'][data-part='root']
190
+ > [data-ui='button'][data-variant='solid'][data-part='menu-trigger'][data-disabled] {
191
+ border-inline-start-color: var(--slotted-disabled-border, ButtonBorder);
192
+ }
193
+
194
+ /**
195
+ * Only focus is raised. The ring is an outline drawn outside the border
196
+ * box, so without this the primary's ring is cut by the trigger that
197
+ * follows it in the DOM. Hover and press repaint a fill and never an edge,
198
+ * so there is nothing to lift for them — `button-group`'s reading.
199
+ */
200
+ [data-ui='split-button'][data-part='root'] > *:focus-visible {
201
+ z-index: 1;
202
+ }
203
+
204
+ /**
205
+ * ── The chevron ───────────────────────────────────────────────────────
206
+ *
207
+ * The mark `select` draws for "a list opens here", which is the promise a
208
+ * menu button keeps; not the ellipsis, which says "more" without saying
209
+ * what opens. It is the `chevron-down` Glyph element the binding renders in
210
+ * the icon part, stroked in the control's own ink, so it follows the label
211
+ * into hover, press, disabled, every tone's `on-solid` and forced colours.
212
+ * `button` sizes it through the icon part; this stylesheet paints nothing.
213
+ *
214
+ * It does not turn while the menu is open. None of the six surveyed split
215
+ * buttons turns it; a mark that flips says the surface will close, while
216
+ * the surface below is what the reader is looking at. `select` turns its
217
+ * own because a select's arrow discloses the value's list; a menu button's
218
+ * chevron is a promise, not a state.
219
+ */
220
+
221
+ /**
222
+ * ── Loading ───────────────────────────────────────────────────────────
223
+ *
224
+ * The primary shows `button`'s own indicator and holds its width and name.
225
+ * The trigger is `aria-disabled` for the duration — choosing "Save as
226
+ * draft" while "Save" is in flight is a double submission — and it keeps
227
+ * its place in the tab sequence and its name, so a reader who reaches it
228
+ * hears why nothing opens.
229
+ *
230
+ * It keeps its paint. `data-disabled` would have `button` paint it the
231
+ * unavailable grey beside an action still in its tone, and the capture
232
+ * read that as two controls — one working, one broken. The bar is one
233
+ * control that is busy, so the trigger stays in the tone and simply stops
234
+ * answering the pointer; the binding refuses the activation a key would
235
+ * send. `pointer-events: none` is also what takes hover away without a
236
+ * state the stylesheet would have to hold back.
237
+ */
238
+ [data-ui='split-button'][data-part='root'][data-loading] > [data-part='menu-trigger'] {
239
+ pointer-events: none;
240
+ }
241
+
242
+ /**
243
+ * Under forced colours the theme flattens every fill, so a half that is
244
+ * pressed has only its edge left to say so. `button` makes that edge
245
+ * `Highlight`; the frame rule above would have taken it away, and this
246
+ * puts it back — `button-group`'s rule for the same case.
247
+ */
248
+ @media (forced-colors: active) {
249
+ [data-ui='split-button'][data-part='root'] > [data-ui='button'][data-pressed] {
250
+ border-color: Highlight;
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,12 @@
1
+ [
2
+ "--slotted-control-border-color",
3
+ "--slotted-control-border-width",
4
+ "--slotted-control-radius",
5
+ "--slotted-disabled-border",
6
+ "--slotted-split-button-radius",
7
+ "--slotted-tone-accent-solid-active",
8
+ "--slotted-tone-danger-solid-active",
9
+ "--slotted-tone-secondary-solid-active",
10
+ "--slotted-tone-success-solid-active",
11
+ "--slotted-tone-warning-solid-active"
12
+ ]
@@ -0,0 +1,336 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A bar between two regions the consumer owns, and nothing else. The family
4
+ * holds no panels — `docs/history/designs/2026-09-11-splitter-design.md`
5
+ * section 1 — so this stylesheet paints one element and its two children and
6
+ * writes no layout at all. A separator that sized the boxes beside it would
7
+ * be deciding a layout it was only handed a position in.
8
+ *
9
+ * The picture is SAP's, read in `SAP/fundamental-styles`
10
+ * `packages/styles/src/splitter.scss` rather than in the rendered page: two
11
+ * hairlines that fade out away from a grip in the middle, and that run the
12
+ * whole track once a hand is on them. Six of the seven surveyed
13
+ * implementations draw a plain gutter; this is the one that reads as
14
+ * something to hold.
15
+ *
16
+ * `touch-action: none` so a drag is not read as a scroll on the way past.
17
+ */
18
+ [data-ui='splitter'] {
19
+ /**
20
+ * The track is what the pointer sees and `--slotted-target-min` is what it
21
+ * gets: `::before` below is a transparent box centred on the track, which
22
+ * is the construction `drawer`'s resizer already uses to hold the WCAG 2.2
23
+ * floor without widening the picture. One rem rather than the four pixels
24
+ * `drawer` uses, because this bar carries a grip and four pixels cannot.
25
+ */
26
+ --_track: var(--slotted-space-6, 1rem);
27
+ --_reach: var(--slotted-target-min, 24px);
28
+
29
+ /**
30
+ * A hairline, not a rule. SAP draws `0.0625rem` and the thin border step is
31
+ * the same quantity under a name the theme can retune.
32
+ *
33
+ * Its resting length is a segment and not the track: a line that already
34
+ * runs end to end has nowhere to go when a hand arrives, and the growth is
35
+ * the whole of the feedback. SAP's `4rem` is `--slotted-space-12`.
36
+ */
37
+ --_line: var(--slotted-border-width-thin, 1px);
38
+ --_line-length: var(--slotted-space-12, 4rem);
39
+
40
+ /**
41
+ * SAP's own `2rem` between the grip and each line — but capped against the
42
+ * track, and that cap is a correction from the first capture.
43
+ *
44
+ * A flex gap does not shrink. Two of them at `2rem`, two lines at `4rem`
45
+ * and the grip ask for two hundred and four pixels of track, so in a region
46
+ * eighty pixels tall the gaps took everything and both lines were crushed
47
+ * to nothing: the bar showed a grip floating in a gap and no line at all.
48
+ * SAP never meets this because its splitter divides application panes,
49
+ * which are tall by construction; a bar in this library divides whatever
50
+ * the consumer put beside it.
51
+ *
52
+ * A percentage gap resolves against the container's own extent on the axis
53
+ * it runs along, so `min()` takes the rem step where there is room for it
54
+ * and a proportion of the track where there is not.
55
+ */
56
+ --_gap: min(var(--slotted-space-9, 2rem), 12%);
57
+
58
+ /**
59
+ * The grip is a two-by-three dot matrix on one pitch, which is what SAP's
60
+ * `vertical-grip` glyph draws. It is built from a gradient rather than from
61
+ * an icon token because it is not an icon: it carries no meaning a reader
62
+ * could look up, and a family that put it in the icon scale would let a
63
+ * theme swap it for something that does.
64
+ */
65
+ --_dot: var(--slotted-space-2, 0.25rem);
66
+
67
+ /**
68
+ * Two steps, and the whole of the colour decision.
69
+ *
70
+ * `--_rest` is the **strong** border step and not the subtle one, which is
71
+ * a correction from the first capture: drawn in `--slotted-border-subtle`
72
+ * the bar was invisible at rest in `light`, and a resizable edge nobody can
73
+ * see is a capability nobody finds — the argument the grip exists for. The
74
+ * subtle step is for a rule inside one surface; this is the boundary
75
+ * between two, which is the step the foundation holds to 3:1.
76
+ *
77
+ * `--_active` is a step darker again. The colour is the smaller half of the
78
+ * feedback: the larger half is the lines running the whole track, which is
79
+ * what SAP changes and what a hand actually reads.
80
+ *
81
+ * The accent is deliberately absent from both. It is the one colour in the
82
+ * library that means "the keyboard is here", and the rule below spends it
83
+ * on `:focus-visible` alone. `drawer`'s resizer spends it on hover as well,
84
+ * which is the defect this family was built to correct; SAP is the only one
85
+ * of the seven surveyed implementations that keeps the two apart.
86
+ */
87
+ --_rest: var(--slotted-splitter-line-color, var(--slotted-border-strong, ButtonBorder));
88
+ --_active: var(--slotted-splitter-line-active-color, var(--slotted-text-muted, ButtonText));
89
+
90
+ /* The repository has no global reset, and this box states its own measure. */
91
+ box-sizing: border-box;
92
+ position: relative;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ gap: var(--_gap);
97
+ flex-shrink: 0;
98
+ background: transparent;
99
+ touch-action: none;
100
+ transition: gap var(--slotted-control-transition-duration, var(--slotted-duration-short))
101
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard));
102
+ }
103
+
104
+ /**
105
+ * Under the hand the gap closes to one step, and the three marks read as one.
106
+ *
107
+ * It is not decoration: it is where half the growth comes from. On an
108
+ * eighty-pixel track the two resting gaps and the grip take thirty-one pixels
109
+ * of the eighty, so lines that could only grow from sixteen to twenty-four
110
+ * pixels were a state change nobody could see — the third capture showed
111
+ * `data-dragging` and rest as the same picture. Closing the gap hands those
112
+ * pixels to the lines, and the pair roughly doubles instead.
113
+ */
114
+ [data-ui='splitter']:not([data-disabled]):hover,
115
+ [data-ui='splitter']:not([data-disabled]):focus-visible,
116
+ [data-ui='splitter'][data-dragging] {
117
+ --_gap: var(--slotted-space-4, 0.5rem);
118
+ }
119
+
120
+ /**
121
+ * `align-self` and `justify-self` for the same reason `divider` claims both:
122
+ * the bar's length comes from the container, and a flex parent hands the
123
+ * cross size over through one while a grid parent hands it over through the
124
+ * other. A container that packs its children instead of stretching them
125
+ * would otherwise size the bar from its content.
126
+ */
127
+ [data-ui='splitter'][data-orientation='vertical'] {
128
+ flex-direction: column;
129
+ inline-size: var(--_track);
130
+ align-self: stretch;
131
+ cursor: col-resize;
132
+ }
133
+
134
+ [data-ui='splitter'][data-orientation='horizontal'] {
135
+ flex-direction: row;
136
+ block-size: var(--_track);
137
+ justify-self: stretch;
138
+ cursor: row-resize;
139
+ }
140
+
141
+ /**
142
+ * The reach. Transparent, centred on the track, and the reason a sixteen-pixel
143
+ * picture is a twenty-four-pixel target.
144
+ */
145
+ [data-ui='splitter']::before {
146
+ content: '';
147
+ position: absolute;
148
+ --_overhang: calc((var(--_reach) - var(--_track)) / -2);
149
+ }
150
+
151
+ [data-ui='splitter'][data-orientation='vertical']::before {
152
+ inset-block: 0;
153
+ inset-inline: var(--_overhang);
154
+ }
155
+
156
+ [data-ui='splitter'][data-orientation='horizontal']::before {
157
+ inset-inline: 0;
158
+ inset-block: var(--_overhang);
159
+ }
160
+
161
+ /**
162
+ * The lines.
163
+ *
164
+ * Each fades to transparent *away* from the grip, so the pair reads as one
165
+ * mark with a middle rather than as two dashes. The gradient is the reason
166
+ * these are elements and not pseudo-elements: `packages/styles/src/fallbacks.test.mjs`
167
+ * forbids a system colour inside `::before` or `::after`, and the focus
168
+ * colour's own last resort is `Highlight`.
169
+ *
170
+ * `flex-basis` carries both the resting length and the growth, because a
171
+ * grow factor does not animate and the extension *is* the feedback.
172
+ *
173
+ * At rest the basis is `min(4rem, 20%)`, and both halves of that are
174
+ * corrections from the first two captures. The rem step alone — SAP's `4rem`
175
+ * — asked for more track than an eighty-pixel region has, and the gaps, which
176
+ * do not shrink, crushed both lines to nothing. Filling the track instead
177
+ * fixed that and broke the other end: a line already occupying everything the
178
+ * gaps leave has nowhere to grow to, and hover and drag became
179
+ * indistinguishable from rest.
180
+ *
181
+ * A proportion leaves slack at every track length. That slack is what the
182
+ * hand claims.
183
+ */
184
+ [data-ui='splitter'] > [data-part='line'] {
185
+ flex: 0 1 min(var(--_line-length), 20%);
186
+ min-block-size: 0;
187
+ min-inline-size: 0;
188
+ border-radius: var(--slotted-radius-sm, 2px);
189
+ transition:
190
+ flex-basis var(--slotted-control-transition-duration, var(--slotted-duration-short))
191
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard)),
192
+ background-color var(--slotted-control-transition-duration, var(--slotted-duration-short))
193
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard));
194
+ }
195
+
196
+ [data-ui='splitter'][data-orientation='vertical'] > [data-part='line'] {
197
+ inline-size: var(--_line);
198
+ }
199
+
200
+ [data-ui='splitter'][data-orientation='horizontal'] > [data-part='line'] {
201
+ block-size: var(--_line);
202
+ }
203
+
204
+ /* Solid at the grip, gone at the far end. Four rules, one per direction. */
205
+ [data-ui='splitter'][data-orientation='vertical'] > [data-part='line']:first-child {
206
+ background: linear-gradient(to bottom, transparent, var(--_rest));
207
+ }
208
+
209
+ [data-ui='splitter'][data-orientation='vertical'] > [data-part='line']:last-child {
210
+ background: linear-gradient(to top, transparent, var(--_rest));
211
+ }
212
+
213
+ [data-ui='splitter'][data-orientation='horizontal'] > [data-part='line']:first-child {
214
+ background: linear-gradient(to right, transparent, var(--_rest));
215
+ }
216
+
217
+ [data-ui='splitter'][data-orientation='horizontal'] > [data-part='line']:last-child {
218
+ background: linear-gradient(to left, transparent, var(--_rest));
219
+ }
220
+
221
+ /**
222
+ * Under the hand, and under the drag: the pair runs the whole track and drops
223
+ * the gradient for the strong step. `flex-basis: 100%` against
224
+ * `flex-shrink: 1` is what lets the two of them and the gap share the track
225
+ * rather than overflow it.
226
+ *
227
+ * Hover is held back for `data-disabled`, which is the shared rule from
228
+ * `packages/styles/src/states.test.mjs`.
229
+ */
230
+ [data-ui='splitter']:not([data-disabled]):hover > [data-part='line'],
231
+ [data-ui='splitter']:not([data-disabled]):focus-visible > [data-part='line'],
232
+ [data-ui='splitter'][data-dragging] > [data-part='line'] {
233
+ flex-basis: 100%;
234
+ background: var(--_active);
235
+ }
236
+
237
+ /**
238
+ * The keyboard's own signal is the ring, and it is the only place the accent
239
+ * appears. The lines take the same step they take under the hand.
240
+ *
241
+ * They were drawn in the focus colour too, until the capture showed what that
242
+ * is: a two-pixel accent ring around a sixteen-pixel bar whose interior is
243
+ * also accent, which reads as a solid blue block rather than as a ring around
244
+ * a separator. The accent said the same thing twice in the same twelve
245
+ * pixels, and the second saying cost the first its shape.
246
+ */
247
+
248
+ [data-ui='splitter']:focus-visible {
249
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
250
+ outline-offset: calc(-1 * var(--slotted-focus-ring-offset, 2px));
251
+ }
252
+
253
+ /**
254
+ * The grip. Two columns by three rows on one pitch, drawn with one gradient
255
+ * and sized from the same step, so a density that retunes the space scale
256
+ * retunes the mark with it.
257
+ *
258
+ * `color` rather than a background colour on each dot: the dots and the lines
259
+ * are one mark and have to move together, and `currentColor` is the one place
260
+ * that says so.
261
+ */
262
+ [data-ui='splitter'] > [data-part='grip'] {
263
+ flex: none;
264
+ color: var(--_rest);
265
+ background-image: radial-gradient(circle at center, currentColor 40%, transparent 42%);
266
+ background-size: var(--_dot) var(--_dot);
267
+ background-repeat: repeat;
268
+ transition: color var(--slotted-control-transition-duration, var(--slotted-duration-short))
269
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard));
270
+ }
271
+
272
+ [data-ui='splitter'][data-orientation='vertical'] > [data-part='grip'] {
273
+ inline-size: calc(2 * var(--_dot));
274
+ block-size: calc(3 * var(--_dot));
275
+ }
276
+
277
+ [data-ui='splitter'][data-orientation='horizontal'] > [data-part='grip'] {
278
+ inline-size: calc(3 * var(--_dot));
279
+ block-size: calc(2 * var(--_dot));
280
+ }
281
+
282
+ [data-ui='splitter']:not([data-disabled]):hover > [data-part='grip'],
283
+ [data-ui='splitter']:not([data-disabled]):focus-visible > [data-part='grip'],
284
+ [data-ui='splitter'][data-dragging] > [data-part='grip'] {
285
+ color: var(--_active);
286
+ }
287
+
288
+ /**
289
+ * Disabled. The bar keeps its tab stop — a reader who cannot move the
290
+ * separator can still read its value — so this changes the picture and the
291
+ * cursor and nothing about the focus.
292
+ */
293
+ [data-ui='splitter'][data-disabled] {
294
+ cursor: default;
295
+ --_rest: var(--slotted-splitter-disabled-line-color, var(--slotted-disabled-border, GrayText));
296
+ }
297
+
298
+ /**
299
+ * Forced colours. A gradient resolves to nothing there, so both lines take a
300
+ * flat system colour and the growth is what is left of the feedback. The
301
+ * grip's gradient is replaced for the same reason.
302
+ */
303
+ @media (forced-colors: active) {
304
+ [data-ui='splitter'] > [data-part='line'] {
305
+ background: ButtonBorder;
306
+ forced-color-adjust: none;
307
+ }
308
+
309
+ [data-ui='splitter']:not([data-disabled]):hover > [data-part='line'],
310
+ [data-ui='splitter'][data-dragging] > [data-part='line'] {
311
+ background: ButtonText;
312
+ }
313
+
314
+ [data-ui='splitter'] > [data-part='grip'] {
315
+ background-image: none;
316
+ background-color: ButtonBorder;
317
+ }
318
+
319
+ [data-ui='splitter'][data-disabled] > [data-part='line'],
320
+ [data-ui='splitter'][data-disabled] > [data-part='grip'] {
321
+ background: GrayText;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * The preference is about movement, not about feedback. The lines still reach
327
+ * the ends of the track and still change step; they simply arrive there
328
+ * without travelling.
329
+ */
330
+ @media (prefers-reduced-motion: reduce) {
331
+ [data-ui='splitter'] > [data-part='line'],
332
+ [data-ui='splitter'] > [data-part='grip'] {
333
+ transition-duration: var(--slotted-duration-instant);
334
+ }
335
+ }
336
+ }
@@ -0,0 +1,24 @@
1
+ [
2
+ "--slotted-border-strong",
3
+ "--slotted-border-width-thin",
4
+ "--slotted-control-transition-duration",
5
+ "--slotted-control-transition-easing",
6
+ "--slotted-disabled-border",
7
+ "--slotted-duration-instant",
8
+ "--slotted-duration-short",
9
+ "--slotted-easing-standard",
10
+ "--slotted-focus-ring-color",
11
+ "--slotted-focus-ring-offset",
12
+ "--slotted-focus-ring-width",
13
+ "--slotted-radius-sm",
14
+ "--slotted-space-12",
15
+ "--slotted-space-2",
16
+ "--slotted-space-4",
17
+ "--slotted-space-6",
18
+ "--slotted-space-9",
19
+ "--slotted-splitter-disabled-line-color",
20
+ "--slotted-splitter-line-active-color",
21
+ "--slotted-splitter-line-color",
22
+ "--slotted-target-min",
23
+ "--slotted-text-muted"
24
+ ]