@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,1048 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── A text input with a list of options filtered by what was typed ──────
4
+ *
5
+ * The family is two subtrees, as `select`'s is: the **control**, where the
6
+ * consumer wrote it, and the **popup**, portalled to the end of the document
7
+ * so it escapes every clipping ancestor between them. Nothing selects one
8
+ * from the other, so `data-ui='combobox'` is written on both roots — the
9
+ * contract records it under `portalledIdentity`.
10
+ *
11
+ * ┌─ root ─ the control ───────────────────────────────────────┐
12
+ * │ input ─ role="combobox" clear trigger ▸ arrow │
13
+ * └────────────────────────────────────────────────────────────┘
14
+ *
15
+ * ┌─ positioner ─ the layer ───────────────────────────────────┐
16
+ * │ ┌─ content ─ role="listbox" ─ scrolls ───────────────┐ │
17
+ * │ │ group ▸ group-label │ │
18
+ * │ │ item ▸ icon │ label ▸ match │ indicator │ │
19
+ * │ │ description │ │
20
+ * │ │ separator · empty · status │ │
21
+ * │ └────────────────────────────────────────────────────┘ │
22
+ * └────────────────────────────────────────────────────────────┘
23
+ *
24
+ * ── What this sheet does not decide ──────────────────────────────────
25
+ *
26
+ * The frame, the three columns, the subgrid, the check in the last column,
27
+ * the highlight on `data-current`, the two-line label clamp and the bounded
28
+ * scrolling surface are all `select`'s decisions, taken in the same session
29
+ * and written out in `packages/styles/src/select/select.css`. They are
30
+ * repeated here rather than shared because the two families have two public
31
+ * identifiers, and a stylesheet that painted `[data-ui='select']` from a
32
+ * combobox would make one family's selector match the other's DOM. The
33
+ * duplication is the price of that boundary and it is paid on purpose.
34
+ *
35
+ * ── What this sheet decides on its own ───────────────────────────────
36
+ *
37
+ * **The control holds a real `input`**, so every text-editing key, the caret,
38
+ * the selection, IME and autofill stay the browser's — the pattern's own
39
+ * IMPORTANT note asks for exactly that. The frame draws the ring from the
40
+ * input's `:focus-visible`, which a browser matches for a pointer press as
41
+ * well as for a Tab, and that is right for a control whose caret is the thing
42
+ * that moved.
43
+ *
44
+ * **The trigger is a button and not a tab stop.** The pattern's Keyboard
45
+ * Interaction note excludes the popup indicator from the page tab sequence by
46
+ * name, so it carries `tabindex="-1"` and the input keeps the stop.
47
+ *
48
+ * **`match` marks what the query found.** It is a `mark` element filled from
49
+ * `matchRanges`, so the span the filter matched is the span the reader sees
50
+ * tinted. No surveyed implementation draws it, and it is the only visual
51
+ * answer to "why is this option in my results" when the filter is fuzzy or
52
+ * remote.
53
+ */
54
+
55
+ /* ── The control ──────────────────────────────────────────────────────── */
56
+
57
+ [data-ui='combobox'][data-part='root'] {
58
+ --_font-size: var(--slotted-control-font-size-md, 13px);
59
+ --_height: var(--slotted-control-height-md, 34px);
60
+ --_padding-inline: var(--slotted-control-padding-inline-md, 13px);
61
+ --_gap: var(--slotted-control-gap-md, 8px);
62
+ /** The floor under the glyph scale at the dense step (SL3); a family token so a theme can retune it. */
63
+ --_icon-floor: var(--slotted-combobox-icon-size-min, 14px);
64
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
65
+ --_inner-size: var(--slotted-control-inner-size-md, 24px);
66
+
67
+ /* `text-field`'s arithmetic, because these are the same buttons in the same
68
+ frame and a second answer would drift from the first. */
69
+ --_action-size: var(
70
+ --slotted-combobox-action-size,
71
+ min(calc(var(--_icon-size) + 2 * var(--slotted-space-2, 0.25rem)), var(--_inner-size))
72
+ );
73
+
74
+ align-items: center;
75
+ background-color: var(--slotted-combobox-background, var(--slotted-control-surface, Field));
76
+ border: var(--slotted-control-border-width, 1px) solid
77
+ var(--slotted-combobox-border-color, var(--slotted-control-border-color, ButtonBorder));
78
+ border-radius: var(--slotted-combobox-radius, var(--slotted-control-radius, 4px));
79
+ box-shadow: var(--slotted-combobox-shadow, var(--slotted-control-shadow, none));
80
+ box-sizing: border-box;
81
+ color: var(--slotted-combobox-color, var(--slotted-text, CanvasText));
82
+ display: flex;
83
+ font-family: var(--slotted-control-font-family, system-ui, sans-serif);
84
+ font-size: var(--_font-size);
85
+ gap: var(--slotted-space-1, 0.125rem);
86
+ inline-size: 100%;
87
+ min-inline-size: 0;
88
+ min-block-size: var(--_height);
89
+ padding-inline-start: var(--_padding-inline);
90
+ /* The end padding is the buttons' gutter rather than the frame's, so the
91
+ glyph — not the button's box — ends where the text begins on the other
92
+ side. `text-field` states the same arithmetic. */
93
+ padding-inline-end: max(
94
+ 0px,
95
+ calc(var(--_padding-inline) - (var(--_action-size) - var(--_icon-size)) / 2)
96
+ );
97
+ transition:
98
+ background-color var(--slotted-control-transition-duration)
99
+ var(--slotted-control-transition-easing),
100
+ border-color var(--slotted-control-transition-duration)
101
+ var(--slotted-control-transition-easing),
102
+ box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
103
+ }
104
+
105
+ [data-ui='combobox'][data-part='root']:not([data-full-width]) {
106
+ inline-size: auto;
107
+ }
108
+
109
+ /**
110
+ * The control. It gives up its own frame entirely and keeps everything that
111
+ * makes it a text control: the value, the caret, the selection and every
112
+ * native attribute the consumer wrote — including the completion string
113
+ * `autocomplete="both"` writes as a selected range rather than as text, so
114
+ * the next character typed replaces it.
115
+ */
116
+ [data-ui='combobox'] > [data-part='input'] {
117
+ background-color: transparent;
118
+ /* The frame's border box, not its content box: on a coarse pointer the frame
119
+ is the touch target's height and the control it holds is too. Stretched
120
+ rather than a percentage of the frame, which is indefinite wherever the
121
+ frame is not stretched itself — a search field in a bar measured 17
122
+ pixels. The control paints no ground, so the frame's edge still shows. */
123
+ align-self: stretch;
124
+ margin-block: calc(var(--slotted-control-border-width, 1px) * -1);
125
+ border: 0;
126
+ color: inherit;
127
+ flex: 1 1 auto;
128
+ font: inherit;
129
+ /* The frame draws the ring for it, so two rings never nest. */
130
+ outline: none;
131
+ padding: 0;
132
+ /* The intrinsic width of an `input` is about twenty characters, and a flex
133
+ item refuses to shrink below it without this. */
134
+ min-inline-size: 0;
135
+ text-overflow: ellipsis;
136
+ }
137
+
138
+ [data-ui='combobox'] > [data-part='input']::placeholder {
139
+ color: var(--slotted-combobox-placeholder-color, var(--slotted-text-muted, GrayText));
140
+ text-overflow: ellipsis;
141
+ }
142
+
143
+ /**
144
+ * The completion string. `autocomplete="both"` writes it into the input and
145
+ * selects it, so the platform's own selection paint is what shows it — and
146
+ * the theme's accent tone is what that paint should be, rather than the
147
+ * browser's blue in a form that is not blue.
148
+ */
149
+ [data-ui='combobox'] > [data-part='input']::selection {
150
+ background-color: var(
151
+ --slotted-combobox-completion-background,
152
+ var(--slotted-tone-accent-surface)
153
+ );
154
+ color: var(--slotted-combobox-completion-color, var(--slotted-tone-accent-text));
155
+ }
156
+
157
+ /**
158
+ * Autofill. Chrome paints its own ground over the control's, and a
159
+ * hundred-pixel inset shadow is the only way to reach it. The signal is kept
160
+ * rather than erased, in the accent surface the theme owns — the repair
161
+ * `text-field` makes, for the same reason.
162
+ */
163
+ [data-ui='combobox'] > [data-part='input']:autofill {
164
+ box-shadow: inset 0 0 0 100px
165
+ var(--slotted-combobox-autofill-background, var(--slotted-tone-accent-surface));
166
+ -webkit-text-fill-color: var(--slotted-combobox-color, var(--slotted-text, CanvasText));
167
+ }
168
+
169
+ /* ── The two buttons inside the frame ─────────────────────────────────── */
170
+
171
+ [data-ui='combobox'] > :is([data-part='clear'], [data-part='trigger']) {
172
+ align-items: center;
173
+ appearance: none;
174
+ background-color: transparent;
175
+ block-size: var(--_action-size);
176
+ border: 0;
177
+ border-radius: var(--slotted-combobox-radius, var(--slotted-control-radius, 4px));
178
+ color: var(--slotted-combobox-action-color, var(--slotted-text-muted, GrayText));
179
+ cursor: pointer;
180
+ display: inline-flex;
181
+ flex: 0 0 auto;
182
+ inline-size: var(--_action-size);
183
+ justify-content: center;
184
+ /* The user agent pads a button, and inside a box sized to its glyph that
185
+ padding squeezed the glyph to twelve pixels. */
186
+ padding: 0;
187
+ /* The containing block of the hit area below. */
188
+ position: relative;
189
+ touch-action: manipulation;
190
+ transition:
191
+ background-color var(--slotted-control-transition-duration)
192
+ var(--slotted-control-transition-easing),
193
+ color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
194
+ -webkit-tap-highlight-color: transparent;
195
+ }
196
+
197
+ /**
198
+ * The hit area, recovered outside the paint. At `sm` with `dense` the whole
199
+ * control is 24 pixels tall — the WCAG 2.2 minimum — so nothing drawn inside
200
+ * it can also be 24.
201
+ */
202
+ [data-ui='combobox'] > :is([data-part='clear'], [data-part='trigger'])::after {
203
+ content: '';
204
+ inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
205
+ position: absolute;
206
+ }
207
+
208
+ /* On a finger the target is the comfortable size in both axes and the box
209
+ is the one a mouse sees: the ground and the focus ring stay square, and
210
+ the hit area above grows to 44 pixels around them. Side by side, neither
211
+ button reaches across the edge the two share. */
212
+ @media (pointer: coarse) {
213
+ [data-ui='combobox'] > :is([data-part='clear'], [data-part='trigger'])::after {
214
+ inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
215
+ }
216
+
217
+ [data-ui='combobox'] > [data-part='clear']:has(+ [data-part='trigger']:not([hidden]))::after {
218
+ inset-inline-end: 0;
219
+ }
220
+
221
+ [data-ui='combobox'] > [data-part='clear']:not([hidden]) + [data-part='trigger']::after {
222
+ inset-inline-start: 0;
223
+ }
224
+ }
225
+
226
+ /* The cross and the chevron are `Glyph` elements the binding draws, sized
227
+ here to the control's icon step. */
228
+ [data-ui='combobox'] > :is([data-part='clear'], [data-part='trigger']) {
229
+ --slotted-icon-size: var(--_icon-size);
230
+ }
231
+
232
+ /**
233
+ * The chevron, drawn by its own part rather than by the button, because the
234
+ * button is what a reader presses and the arrow is what turns over. The
235
+ * library's own `Glyph` in an `icon` box; a character is never an icon.
236
+ */
237
+ [data-ui='combobox'] [data-part='arrow'] {
238
+ align-items: center;
239
+ block-size: var(--_icon-size);
240
+ display: flex;
241
+ inline-size: var(--_icon-size);
242
+ justify-content: center;
243
+ }
244
+
245
+ [data-ui='combobox'] [data-part='arrow'] > [data-ui='icon'] {
246
+ transition: rotate var(--slotted-control-transition-duration)
247
+ var(--slotted-control-transition-easing);
248
+ }
249
+
250
+ [data-ui='combobox'][data-open] [data-part='arrow'] > [data-ui='icon'] {
251
+ rotate: 180deg;
252
+ }
253
+
254
+ /**
255
+ * The ring, where the chevron stands. `select`'s decision and `select`'s
256
+ * reasons (SL6/CB5): while an answer is travelling the control shows the
257
+ * ring rather than the chevron, and the arc is the one `spinner/spinner.css`
258
+ * draws for every family that waits, worn as `data-ui='spinner'` beside this
259
+ * family's own part.
260
+ */
261
+ [data-ui='combobox'] [data-part='busy'] {
262
+ --slotted-spinner-size: var(--_icon-size);
263
+
264
+ color: var(--slotted-combobox-arrow-color, var(--slotted-text-muted, GrayText));
265
+ flex: none;
266
+ }
267
+
268
+ /* ── Size ─────────────────────────────────────────────────────────────── */
269
+
270
+ [data-ui='combobox'][data-part='root'][data-size='sm'] {
271
+ --_font-size: var(--slotted-control-font-size-sm, 12px);
272
+ --_height: var(--slotted-control-height-sm, 30px);
273
+ --_padding-inline: var(--slotted-control-padding-inline-sm, 10px);
274
+ --_gap: var(--slotted-control-gap-sm, 6px);
275
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-sm, 14px));
276
+ --_inner-size: var(--slotted-control-inner-size-sm, 22px);
277
+ }
278
+
279
+ [data-ui='combobox'][data-part='root'][data-size='md'] {
280
+ --_font-size: var(--slotted-control-font-size-md, 13px);
281
+ --_height: var(--slotted-control-height-md, 34px);
282
+ --_padding-inline: var(--slotted-control-padding-inline-md, 13px);
283
+ --_gap: var(--slotted-control-gap-md, 8px);
284
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
285
+ --_inner-size: var(--slotted-control-inner-size-md, 24px);
286
+ }
287
+
288
+ [data-ui='combobox'][data-part='root'][data-size='lg'] {
289
+ --_font-size: var(--slotted-control-font-size-lg, 14px);
290
+ --_height: var(--slotted-control-height-lg, 38px);
291
+ --_padding-inline: var(--slotted-control-padding-inline-lg, 16px);
292
+ --_gap: var(--slotted-control-gap-lg, 8px);
293
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-lg, 18px));
294
+ --_inner-size: var(--slotted-control-inner-size-lg, 28px);
295
+ }
296
+
297
+ /* ── Variant ──────────────────────────────────────────────────────────── */
298
+
299
+ [data-ui='combobox'][data-part='root'][data-variant='subtle'] {
300
+ background-color: var(
301
+ --slotted-combobox-subtle-background,
302
+ var(--slotted-surface-sunken, Canvas)
303
+ );
304
+ border-color: var(
305
+ --slotted-combobox-subtle-border-color,
306
+ var(--slotted-border-subtle, ButtonBorder)
307
+ );
308
+ }
309
+
310
+ /* No edge, no lift, no ground and no corner of its own: the cell it sits in
311
+ is the box, the row is the ground, a cell has no corner, and the resting
312
+ shadow would draw the cell where there is none. */
313
+ [data-ui='combobox'][data-part='root'][data-variant='plain'] {
314
+ background-color: transparent;
315
+ border-color: transparent;
316
+ border-radius: var(--slotted-radius-none, 0px);
317
+ box-shadow: none;
318
+ }
319
+
320
+ @media (forced-colors: active) {
321
+ [data-ui='combobox'][data-part='root'][data-variant='plain'] {
322
+ border-color: ButtonBorder;
323
+ }
324
+ }
325
+
326
+ /* ── States on the control ────────────────────────────────────────────── */
327
+
328
+ [data-ui='combobox'][data-part='root']:hover:not([data-disabled]):not([data-readonly]):not(
329
+ [data-invalid]
330
+ ) {
331
+ border-color: var(--slotted-combobox-hover-border-color, var(--slotted-tone-accent-border));
332
+ }
333
+
334
+ [data-ui='combobox'][data-part='root'][data-variant='subtle']:hover:not([data-disabled]):not(
335
+ [data-readonly]
336
+ ):not([data-invalid]) {
337
+ background-color: var(
338
+ --slotted-combobox-subtle-hover-background,
339
+ var(--slotted-tone-secondary-subtle-hover)
340
+ );
341
+ }
342
+
343
+ [data-ui='combobox'][data-part='root'][data-variant='plain']:hover:not([data-disabled]):not(
344
+ [data-readonly]
345
+ ):not([data-invalid]) {
346
+ background-color: var(
347
+ --slotted-combobox-plain-hover-background,
348
+ var(--slotted-tone-secondary-subtle-hover)
349
+ );
350
+ /* The accent edge the other variants take would draw the cell's outline. */
351
+ border-color: transparent;
352
+ }
353
+
354
+ [data-ui='combobox'][data-part='root']:has(> [data-part='input']:focus-visible) {
355
+ box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
356
+ var(--slotted-focus-ring-color, Highlight);
357
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
358
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
359
+ }
360
+
361
+ /**
362
+ * The ring of a control whose box is not its own is drawn inside that box,
363
+ * the way the inner controls draw theirs inside the frame: two pixels past
364
+ * the control is two pixels over the next row's rule. No lift either — a
365
+ * shadow that appears only under focus is a cell that changes size when it
366
+ * is used.
367
+ */
368
+ [data-ui='combobox'][data-part='root'][data-variant='plain']:has(
369
+ > [data-part='input']:focus-visible
370
+ ) {
371
+ box-shadow: none;
372
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
373
+ }
374
+
375
+ [data-ui='combobox'][data-part='root'][data-open]:not([data-disabled]) {
376
+ border-color: var(--slotted-combobox-open-border-color, var(--slotted-tone-accent-border));
377
+ }
378
+
379
+ [data-ui='combobox'][data-part='root'][data-invalid] {
380
+ --slotted-focus-ring-color: var(
381
+ --slotted-combobox-invalid-focus-ring-color,
382
+ var(--slotted-focus-ring-color-invalid, Highlight)
383
+ );
384
+ --_invalid-edge-width: var(
385
+ --slotted-combobox-invalid-edge-width,
386
+ var(--slotted-control-invalid-edge-width, 1px)
387
+ );
388
+ --_invalid-edge-color: var(
389
+ --slotted-combobox-invalid-border-color,
390
+ var(--slotted-tone-danger-border)
391
+ );
392
+ --_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
393
+
394
+ border-color: var(--slotted-combobox-invalid-border-color, var(--slotted-tone-danger-border));
395
+ /*
396
+ * Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
397
+ * `high-contrast` the danger border is the resting edge's black. The line
398
+ * is a shadow inside the border, so neither box nor text moves, and it
399
+ * takes the resting lift's place.
400
+ */
401
+ box-shadow: var(--_invalid-edge);
402
+ }
403
+
404
+ [data-ui='combobox'][data-part='root'][data-invalid]:has(> [data-part='input']:focus-visible) {
405
+ box-shadow:
406
+ var(--_invalid-edge),
407
+ var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
408
+ var(--slotted-focus-ring-color, Highlight);
409
+ }
410
+
411
+ [data-ui='combobox'][data-part='root'][data-variant='plain'][data-invalid]:has(
412
+ > [data-part='input']:focus-visible
413
+ ) {
414
+ /* The ring lies inside the box here, over the border and an edge as wide as
415
+ it; the edge reaches past the ring. */
416
+ box-shadow: inset 0 0 0
417
+ calc(
418
+ var(--slotted-focus-ring-offset, 2px) - var(--slotted-control-border-width, 1px) +
419
+ var(--_invalid-edge-width)
420
+ )
421
+ var(--_invalid-edge-color);
422
+ }
423
+
424
+ /*
425
+ * Forced colours remove every box shadow, and the edge with them. The
426
+ * width comes back as an outline inside the border, and a focused invalid
427
+ * control draws its ring double, which a valid ring is not.
428
+ */
429
+ @media (forced-colors: active) {
430
+ [data-ui='combobox'][data-part='root'][data-invalid] {
431
+ outline: var(--_invalid-edge-width) solid CanvasText;
432
+ outline-offset: calc(
433
+ -1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
434
+ );
435
+ }
436
+
437
+ [data-ui='combobox'][data-part='root'][data-invalid]:has(> [data-part='input']:focus-visible) {
438
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
439
+ var(--slotted-focus-ring-color, Highlight);
440
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
441
+ }
442
+
443
+ [data-ui='combobox'][data-part='root'][data-variant='plain'][data-invalid]:has(
444
+ > [data-part='input']:focus-visible
445
+ ) {
446
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
447
+ var(--slotted-focus-ring-color, Highlight);
448
+ outline-offset: calc(
449
+ -1 * (var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width))
450
+ );
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Read-only keeps the caret, the selection and the copy, and withholds the
456
+ * popup. The native `readonly` gives the first three; the second is the
457
+ * family's, because the pattern has no readonly for a combobox.
458
+ */
459
+ [data-ui='combobox'][data-part='root'][data-readonly] {
460
+ background-color: var(
461
+ --slotted-combobox-readonly-background,
462
+ var(--slotted-tone-secondary-subtle-hover)
463
+ );
464
+ }
465
+
466
+ [data-ui='combobox'][data-part='root'][data-readonly] [data-part='arrow'] {
467
+ opacity: 0.5;
468
+ }
469
+
470
+ /**
471
+ * `:disabled` on the input is matched beside `data-disabled` on the frame,
472
+ * because a disabled `fieldset` disables every control inside it and the
473
+ * control has no way to say so.
474
+ */
475
+ [data-ui='combobox'][data-part='root'][data-disabled],
476
+ [data-ui='combobox'][data-part='root']:has(> [data-part='input']:disabled) {
477
+ background-color: var(
478
+ --slotted-combobox-disabled-background,
479
+ var(--slotted-disabled-background)
480
+ );
481
+ border-color: var(--slotted-combobox-disabled-border-color, var(--slotted-disabled-border));
482
+ box-shadow: none;
483
+ color: var(--slotted-combobox-disabled-color, var(--slotted-disabled-foreground));
484
+ cursor: not-allowed;
485
+ }
486
+
487
+ [data-ui='combobox']
488
+ > :is([data-part='clear'], [data-part='trigger']):hover:not(:disabled):not([data-disabled]) {
489
+ background-color: var(
490
+ --slotted-combobox-action-hover-background,
491
+ var(--slotted-tone-secondary-subtle-hover)
492
+ );
493
+ }
494
+
495
+ [data-ui='combobox']
496
+ > :is([data-part='clear'], [data-part='trigger']):active:not(:disabled):not([data-disabled]) {
497
+ background-color: var(
498
+ --slotted-combobox-action-active-background,
499
+ var(--slotted-tone-secondary-subtle-active)
500
+ );
501
+ }
502
+
503
+ /**
504
+ * Each button carries its own ring rather than borrowing the frame's.
505
+ * Tabbing from the input to the clear button has to move something visible,
506
+ * and a ring around the whole frame would sit still while focus moved inside
507
+ * it. The trigger is not in the tab sequence and takes the same rule anyway,
508
+ * because a script may focus it.
509
+ */
510
+ [data-ui='combobox'] > :is([data-part='clear'], [data-part='trigger']):focus-visible {
511
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
512
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
513
+ }
514
+
515
+ /* ── The layer ────────────────────────────────────────────────────────── */
516
+
517
+ [data-ui='combobox'][data-part='positioner'] {
518
+ --_surface: var(--slotted-combobox-surface, var(--slotted-surface-raised, #ffffff));
519
+ --_text: var(--slotted-combobox-content-text, var(--slotted-text, #0f172a));
520
+ --_muted: var(--slotted-combobox-content-text-muted, var(--slotted-text-muted, #5b6b7f));
521
+ --_border-color: var(
522
+ --slotted-combobox-content-border-color,
523
+ var(--slotted-border-subtle, #e2e8f0)
524
+ );
525
+ --_border-width: var(--slotted-border-width-thin, 1px);
526
+ --_radius: var(--slotted-combobox-content-radius, var(--slotted-radius-lg, 8px));
527
+ --_shadow: var(--slotted-combobox-content-shadow, var(--slotted-shadow-md));
528
+
529
+ /**
530
+ * The stronger step of the neutral tone, for the reason
531
+ * `packages/styles/src/select/select.css` writes out in full: in the dark
532
+ * scheme `--slotted-tone-secondary-subtle-hover` is `#18181b` where the
533
+ * raised popup is `#1c1c20`, so the usual highlight resolves *darker* than
534
+ * its own ground and reads as a hole. The `-active` step is the only member
535
+ * of the pair that lifts in all three schemes.
536
+ */
537
+ --_highlight: var(--slotted-combobox-highlight, var(--slotted-tone-secondary-subtle-active));
538
+ --_highlight-active: var(
539
+ --slotted-combobox-highlight-active,
540
+ var(--slotted-tone-accent-surface)
541
+ );
542
+ --_selected-color: var(--slotted-combobox-selected-color, var(--slotted-tone-accent-text));
543
+
544
+ --_inset-block: var(--slotted-combobox-content-padding-block, var(--slotted-space-2, 0.25rem));
545
+ --_inset-inline: var(
546
+ --slotted-combobox-content-padding-inline,
547
+ var(--slotted-space-2, 0.25rem)
548
+ );
549
+ --_item-inset: var(--slotted-control-padding-inline-md, 0.8125rem);
550
+ --_item-height: var(--slotted-control-height-md, 34px);
551
+ --_item-radius: var(--slotted-combobox-item-radius, var(--slotted-control-radius));
552
+ --_content-font-size: var(--slotted-control-font-size-md, 13px);
553
+ --_secondary-font-size: var(--slotted-control-font-size-sm, 12px);
554
+ --_content-icon-size: var(--slotted-control-icon-size-md, 16px);
555
+ --_column-gap: var(--slotted-control-gap-md, 0.5rem);
556
+
557
+ --_measure: var(--slotted-combobox-measure, 24rem);
558
+
559
+ /* Only the block inset: the positioner writes `left` and never `right`, and
560
+ `inset-inline-start` would resolve to `right` in a right-to-left document
561
+ and win over the measured value. */
562
+ position: absolute;
563
+ inset-block-start: 0;
564
+
565
+ z-index: calc(var(--slotted-z-index-popover, 1100) + var(--slotted-layer-depth, 0));
566
+ isolation: isolate;
567
+
568
+ box-sizing: border-box;
569
+ pointer-events: none;
570
+
571
+ opacity: 0;
572
+ /**
573
+ * R7: the layer arrives along the axis it opens on — from one space step
574
+ * above its place when it opens downward, from one step below when it
575
+ * opens upward — and never along the inline axis. A `scale` from the
576
+ * corner the positioner named as origin moved the far edge by four per
577
+ * cent of the width, which read as an entrance from the left.
578
+ */
579
+ --_enter-distance: var(--slotted-space-2, 0.25rem);
580
+ translate: 0 calc(-1 * var(--_enter-distance));
581
+ transition:
582
+ opacity var(--slotted-duration-short) var(--slotted-easing-exit),
583
+ translate var(--slotted-duration-short) var(--slotted-easing-exit);
584
+ }
585
+
586
+ [data-ui='combobox'][data-part='positioner'][data-side='top'] {
587
+ translate: 0 var(--_enter-distance);
588
+ }
589
+
590
+ /* A lateral side has no block direction to arrive from, so it only fades. */
591
+ [data-ui='combobox'][data-part='positioner']:is([data-side='left'], [data-side='right']) {
592
+ translate: 0 0;
593
+ }
594
+
595
+ /**
596
+ * The entrance is a keyframe animation and not a transition. A transition
597
+ * starts from the style the browser computed last, and the positioner
598
+ * writes `data-side` in the same style update as the family opens the
599
+ * layer, so a transition set out from the closed style of the side the
600
+ * layer had before it had one — the bottom's — whatever side it opens on.
601
+ * A keyframe states where it starts. The exit stays a transition: it
602
+ * leaves from wherever the layer is, on the closed rule's durations.
603
+ */
604
+ [data-ui='combobox'][data-part='positioner'][data-open] {
605
+ opacity: 1;
606
+ translate: 0 0;
607
+ transition: none;
608
+ }
609
+
610
+ /**
611
+ * Only a layer the positioner placed arrives: it writes `data-positioned`
612
+ * with the side, and a layer written open in static markup — the harness,
613
+ * a docs snippet — is simply there, and is photographed where it stands.
614
+ */
615
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned] {
616
+ animation: slotted-combobox-enter-down var(--slotted-duration-medium)
617
+ var(--slotted-easing-enter);
618
+ }
619
+
620
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned][data-side='top'] {
621
+ animation-name: slotted-combobox-enter-up;
622
+ }
623
+
624
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned]:is(
625
+ [data-side='left'],
626
+ [data-side='right']
627
+ ) {
628
+ animation-name: slotted-combobox-fade;
629
+ }
630
+
631
+ @keyframes slotted-combobox-enter-down {
632
+ from {
633
+ opacity: 0;
634
+ translate: 0 calc(-1 * var(--_enter-distance));
635
+ }
636
+ }
637
+
638
+ @keyframes slotted-combobox-enter-up {
639
+ from {
640
+ opacity: 0;
641
+ translate: 0 var(--_enter-distance);
642
+ }
643
+ }
644
+
645
+ @keyframes slotted-combobox-fade {
646
+ from {
647
+ opacity: 0;
648
+ }
649
+ }
650
+
651
+ /* Stillness keeps the fade and drops the movement (R7). */
652
+ @media (prefers-reduced-motion: reduce) {
653
+ [data-ui='combobox'][data-part='positioner'],
654
+ [data-ui='combobox'][data-part='positioner'][data-side='top'],
655
+ [data-ui='combobox'][data-part='positioner'][data-side='left'],
656
+ [data-ui='combobox'][data-part='positioner'][data-side='right'] {
657
+ translate: 0 0;
658
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
659
+ }
660
+
661
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned],
662
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned][data-side='top'],
663
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned][data-side='left'],
664
+ [data-ui='combobox'][data-part='positioner'][data-open][data-positioned][data-side='right'] {
665
+ animation-name: slotted-combobox-fade;
666
+ }
667
+ }
668
+
669
+ [data-ui='combobox'][data-part='positioner'][data-detached] {
670
+ visibility: hidden;
671
+ }
672
+
673
+ [data-ui='combobox'][data-part='positioner'][data-width='anchor'] {
674
+ inline-size: var(--slotted-positioner-anchor-width, auto);
675
+ }
676
+
677
+ [data-ui='combobox'][data-part='positioner'][data-width='content'] {
678
+ inline-size: max-content;
679
+ max-inline-size: min(var(--_measure), var(--slotted-positioner-available-width, 100vw));
680
+ min-inline-size: var(--slotted-positioner-anchor-width, auto);
681
+ }
682
+
683
+ [data-ui='combobox'][data-part='positioner'][data-size='sm'] {
684
+ --_item-inset: var(--slotted-control-padding-inline-sm, 0.625rem);
685
+ --_item-height: var(--slotted-control-height-sm, 30px);
686
+ --_content-font-size: var(--slotted-control-font-size-sm, 12px);
687
+ --_secondary-font-size: var(--slotted-font-size-xs, 0.75rem);
688
+ --_content-icon-size: var(--slotted-control-icon-size-sm, 14px);
689
+ --_column-gap: var(--slotted-control-gap-sm, 0.375rem);
690
+ }
691
+
692
+ [data-ui='combobox'][data-part='positioner'][data-size='lg'] {
693
+ --_item-inset: var(--slotted-control-padding-inline-lg, 1rem);
694
+ --_item-height: var(--slotted-control-height-lg, 38px);
695
+ --_content-font-size: var(--slotted-control-font-size-lg, 14px);
696
+ --_secondary-font-size: var(--slotted-control-font-size-sm, 12px);
697
+ --_content-icon-size: var(--slotted-control-icon-size-lg, 18px);
698
+ --_column-gap: var(--slotted-control-gap-lg, 0.5rem);
699
+ }
700
+
701
+ /* ── The surface ──────────────────────────────────────────────────────── */
702
+
703
+ [data-ui='combobox'] > [data-part='content'] {
704
+ background: var(--_surface);
705
+ border: var(--_border-width) solid var(--_border-color);
706
+ border-radius: var(--_radius);
707
+ box-shadow: var(--_shadow);
708
+ box-sizing: border-box;
709
+ color: var(--_text);
710
+ display: grid;
711
+ font-family: var(--slotted-font-family-sans);
712
+ font-size: var(--_content-font-size);
713
+ grid-template-columns: auto minmax(0, 1fr) auto;
714
+ max-block-size: calc(
715
+ var(--slotted-positioner-available-height, 100vh) - var(--slotted-space-5, 0.75rem)
716
+ );
717
+ outline: none;
718
+ overflow-y: auto;
719
+ overscroll-behavior: contain;
720
+ padding-block: var(--_inset-block);
721
+ padding-inline: var(--_inset-inline);
722
+ pointer-events: auto;
723
+ }
724
+
725
+ [data-ui='combobox'] > [data-part='content']:focus-visible {
726
+ outline: none;
727
+ }
728
+
729
+ /* ── Groups ───────────────────────────────────────────────────────────── */
730
+
731
+ [data-ui='combobox'] [data-part='group'] {
732
+ display: grid;
733
+ grid-column: 1 / -1;
734
+ grid-template-columns: subgrid;
735
+ }
736
+
737
+ [data-ui='combobox'] [data-part='group-label'] {
738
+ color: var(--_muted);
739
+ font-size: var(--_secondary-font-size);
740
+ font-weight: var(--slotted-font-weight-semibold, 600);
741
+ grid-column: 1 / -1;
742
+ line-height: var(--slotted-line-height-sm, 1.45);
743
+ padding-block: var(--slotted-space-2, 0.25rem);
744
+ padding-inline: var(--_item-inset);
745
+ }
746
+
747
+ /* ── Options ──────────────────────────────────────────────────────────── */
748
+
749
+ [data-ui='combobox'] [data-part='item'] {
750
+ align-content: center;
751
+ border-radius: var(--_item-radius);
752
+ box-sizing: border-box;
753
+ /**
754
+ * The hand (CB12). A row that commits a value and closes the surface is a
755
+ * press, and this popup is not a platform menu: `select` draws the same
756
+ * cursor on the same row, and the pair says one thing about one gesture.
757
+ */
758
+ cursor: pointer;
759
+ display: grid;
760
+ grid-column: 1 / -1;
761
+ grid-template-columns: subgrid;
762
+ line-height: var(--slotted-line-height-md, 1.5);
763
+ min-block-size: var(--_item-height);
764
+ padding-block: var(--slotted-space-2, 0.25rem);
765
+ padding-inline: var(--_item-inset);
766
+ row-gap: var(--slotted-space-1, 0.125rem);
767
+ user-select: none;
768
+ transition:
769
+ background-color var(--slotted-control-transition-duration, var(--slotted-duration-short))
770
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard)),
771
+ color var(--slotted-control-transition-duration, var(--slotted-duration-short))
772
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard));
773
+ }
774
+
775
+ [data-ui='combobox'] [data-part='item'][data-current] {
776
+ background-color: var(--_highlight);
777
+ /**
778
+ * A ring as well as a ground. Nothing on this surface holds focus — DOM
779
+ * focus is on the input for the whole interaction — so there is no focus
780
+ * ring anywhere to say where the reader is, and the APG's own select-only
781
+ * example marks its current option with an outline for that reason. An
782
+ * inset shadow rather than an outline, so it follows the option's own
783
+ * radius and never overflows the surface's rounded edge.
784
+ */
785
+ box-shadow: inset 0 0 0 var(--slotted-border-width-thin, 1px)
786
+ var(--slotted-combobox-highlight-ring, var(--slotted-tone-accent-border));
787
+ }
788
+
789
+ [data-ui='combobox'] [data-part='item']:not([data-disabled]):active {
790
+ background-color: var(--_highlight-active);
791
+ }
792
+
793
+ [data-ui='combobox'] [data-part='item'][data-selected] {
794
+ color: var(--_selected-color);
795
+ }
796
+
797
+ /**
798
+ * Two chosen neighbours are one block (LB2). Every item keeps its own
799
+ * radius, so two selected siblings painted with the same ground met at a
800
+ * waist — four rounded corners inside the block — and a multiple selection
801
+ * read as a column of separate chips. The second gives up its top corners
802
+ * and the first its bottom ones, so the block is rounded only where the
803
+ * selection begins and ends. `select` and `combobox` draw the same ground
804
+ * and answer the same way; `selection-block.test.mjs` holds the three to it.
805
+ */
806
+ [data-ui='combobox'] [data-part='item'][data-selected] + [data-part='item'][data-selected] {
807
+ border-start-end-radius: 0;
808
+ border-start-start-radius: 0;
809
+ }
810
+
811
+ [data-ui='combobox'] [data-part='item'][data-selected]:has(+ [data-part='item'][data-selected]) {
812
+ border-end-end-radius: 0;
813
+ border-end-start-radius: 0;
814
+ }
815
+
816
+ [data-ui='combobox'] [data-part='item'][data-selected] > [data-part='label'] {
817
+ font-weight: var(--slotted-font-weight-medium, 500);
818
+ }
819
+
820
+ [data-ui='combobox'] [data-part='item'][data-disabled] {
821
+ color: var(--slotted-disabled-foreground, #64748b);
822
+ cursor: not-allowed;
823
+ }
824
+
825
+ /**
826
+ * The highlight can land on an option nobody can choose, because a disabled
827
+ * option keeps its place in the collection and the pattern requires it to be
828
+ * announced. It must not wear the accent cursor while it is there: an accent
829
+ * ring on a row that answers nothing says "act here" to the one reader who
830
+ * cannot. The ground and the ring both step down to the theme's disabled
831
+ * pair, and the row stays legible rather than being faded toward it.
832
+ */
833
+ /**
834
+ * The highlight can land on an option nobody can choose, because a disabled
835
+ * option keeps its place in the collection and the pattern requires it to be
836
+ * announced.
837
+ *
838
+ * **The ground stays the highlight's.** Painting the theme's disabled surface
839
+ * here was the first answer and it was wrong in the dark scheme, for the same
840
+ * reason the highlight itself was: `--slotted-disabled-background` is
841
+ * `#18181b` where the raised popup is `#1c1c20`, so the cursor *sank* on a
842
+ * disabled row and rose on every other one — a reader arrowing down the list
843
+ * would watch it disappear. A cursor lifts, always. What the row cannot do is
844
+ * said by the ink and by the ring, which both step down to the disabled pair.
845
+ */
846
+ [data-ui='combobox'] [data-part='item'][data-disabled][data-current] {
847
+ box-shadow: inset 0 0 0 var(--slotted-border-width-thin, 1px)
848
+ var(--slotted-disabled-border, #cbd5e1);
849
+ color: var(--slotted-disabled-foreground, #64748b);
850
+ }
851
+
852
+ /* ── The cells ────────────────────────────────────────────────────────── */
853
+
854
+ [data-ui='combobox'] :is([data-part='indicator'], [data-part='icon']) {
855
+ block-size: 1lh;
856
+ }
857
+
858
+ /* Column one holds content and paints none: a `Glyph` in an `Icon` box, or
859
+ artwork of the consumer's own, centred and sized from the column. */
860
+ [data-ui='combobox'] [data-part='icon'] {
861
+ --slotted-icon-size: var(--_content-icon-size);
862
+
863
+ align-items: center;
864
+ display: flex;
865
+ grid-column: 1;
866
+ grid-row: 1;
867
+ inline-size: var(--_content-icon-size);
868
+ justify-content: center;
869
+ justify-self: center;
870
+ margin-inline-end: var(--_column-gap);
871
+ }
872
+
873
+ [data-ui='combobox'] [data-part='label'] {
874
+ grid-column: 2;
875
+ grid-row: 1;
876
+ min-inline-size: 0;
877
+ display: -webkit-box;
878
+ -webkit-box-orient: vertical;
879
+ -webkit-line-clamp: 2;
880
+ overflow: hidden;
881
+ overflow-wrap: anywhere;
882
+ }
883
+
884
+ /**
885
+ * The span the query matched is not painted here. It is the shared mark of
886
+ * R6 — `highlight/highlight.css`, `data-ui='highlight'` worn beside this
887
+ * family's `match` part — because every list a reader types into says the
888
+ * same thing about the same span, and `select` with a filter, `listbox` with
889
+ * a filter and `menu` with a search would otherwise each have answered it
890
+ * again. The contract names the sheet under `mark`.
891
+ *
892
+ * What lived here also changed the metric of the word: 600 weight and 0.1em
893
+ * of inline padding made `Blueberry` 6.86px wider marked than plain, and the
894
+ * run moved on every keystroke as the filter re-marked the label (CB6).
895
+ */
896
+
897
+ [data-ui='combobox'] [data-part='description'] {
898
+ color: var(--_muted);
899
+ font-size: var(--_secondary-font-size);
900
+ grid-column: 2;
901
+ grid-row: 2;
902
+ line-height: var(--slotted-line-height-sm, 1.45);
903
+ min-inline-size: 0;
904
+ overflow-wrap: anywhere;
905
+ }
906
+
907
+ [data-ui='combobox'] [data-part='item'][data-disabled] > [data-part='description'] {
908
+ color: inherit;
909
+ }
910
+
911
+ /* The tick is a `Glyph` the binding draws, at the content's icon step. */
912
+ [data-ui='combobox'] [data-part='indicator'] {
913
+ --slotted-icon-size: var(--_content-icon-size);
914
+
915
+ align-items: center;
916
+ display: flex;
917
+ grid-column: 3;
918
+ grid-row: 1;
919
+ inline-size: var(--_content-icon-size);
920
+ justify-content: center;
921
+ justify-self: end;
922
+ margin-inline-start: var(--_column-gap);
923
+ opacity: 0;
924
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-standard);
925
+ }
926
+
927
+ [data-ui='combobox'] [data-part='item'][data-selected] > [data-part='indicator'] {
928
+ opacity: 1;
929
+ }
930
+
931
+ /* ── Separator, empty, status ─────────────────────────────────────────── */
932
+
933
+ [data-ui='combobox'] [data-part='separator'] {
934
+ background-color: var(--slotted-combobox-separator-color, var(--_border-color));
935
+ block-size: var(--_border-width);
936
+ border: 0;
937
+ grid-column: 1 / -1;
938
+ margin-block: var(--slotted-space-2, 0.25rem);
939
+ margin-inline: 0;
940
+ }
941
+
942
+ /**
943
+ * A query with no answers, and a query whose answer is still travelling.
944
+ * Both keep the surface's measure rather than collapsing, so nothing jumps
945
+ * when the results land — which is the gap none of the surveyed
946
+ * implementations closes.
947
+ */
948
+ [data-ui='combobox'] :is([data-part='empty'], [data-part='status']) {
949
+ color: var(--_muted);
950
+ /**
951
+ * Its own bidi run, ordered by its own content. "3 results" is a European
952
+ * number, a neutral space and a Latin word, so in a right-to-left document
953
+ * the space resolves to the paragraph and the two runs swap: the first
954
+ * right-to-left capture of this family printed "results 3". `plaintext`
955
+ * takes the direction from the sentence's own first strong character, so an
956
+ * English count reads as itself and a translated one reads as itself — the
957
+ * same repair `text-field` makes for an adornment, and for the same reason.
958
+ * These two parts are centred, so taking their own direction costs no
959
+ * alignment; an option's label is the consumer's content and keeps the
960
+ * document's direction unless the consumer says otherwise.
961
+ */
962
+ unicode-bidi: plaintext;
963
+ grid-column: 1 / -1;
964
+ /**
965
+ * Wide enough that a one-word message does not draw a popup narrower than a
966
+ * word, and never wider than the popup it is in: at `width="anchor"` the
967
+ * surface is exactly the control's width, and a bare `12rem` on a child
968
+ * overflows the grid the moment the control is narrower than that.
969
+ */
970
+ min-inline-size: min(12rem, 100%);
971
+ padding-block: var(--slotted-space-5, 0.75rem);
972
+ padding-inline: var(--_item-inset);
973
+ text-align: center;
974
+ }
975
+
976
+ /**
977
+ * The status row is also the live region that announces the count, so it is
978
+ * often present with nothing to draw. A row with no text takes no space, and
979
+ * a row with text takes the same space as the empty message beside it.
980
+ */
981
+ [data-ui='combobox'] [data-part='status']:empty {
982
+ display: none;
983
+ }
984
+
985
+ /**
986
+ * The wait, as the shape of the list that is coming. `select`'s decision and
987
+ * `select`'s reasons (SL6/CB5): three rows the height of an option rather
988
+ * than one sentence, so the surface a reader is looking at while the answer
989
+ * travels is the surface they will go on looking at. The bars are
990
+ * `skeleton/skeleton.css`'s — the family places them and states nothing
991
+ * about how a placeholder is painted — and they are hidden from assistive
992
+ * technology, because the `status` row beside them is the live region.
993
+ */
994
+ [data-ui='combobox'] [data-part='placeholder'] {
995
+ display: grid;
996
+ /* The surface is a grid of option columns; the rows span all of them, as
997
+ the status and empty rows do. */
998
+ grid-column: 1 / -1;
999
+ padding-block: var(--slotted-space-2, 0.25rem);
1000
+ padding-inline: var(--_item-inset);
1001
+ }
1002
+
1003
+ [data-ui='combobox'] [data-part='placeholder'] > * {
1004
+ align-items: center;
1005
+ display: flex;
1006
+ min-block-size: var(--_item-height);
1007
+ }
1008
+
1009
+ [data-ui='combobox'] [data-part='placeholder'] > [data-ui='skeleton'] {
1010
+ align-self: center;
1011
+ }
1012
+
1013
+ /* Three ragged widths: three identical bars read as a table, and a list of
1014
+ labels is ragged. */
1015
+ [data-ui='combobox'] [data-part='placeholder'] > :nth-child(2) [data-ui='skeleton'] {
1016
+ inline-size: 78%;
1017
+ }
1018
+
1019
+ [data-ui='combobox'] [data-part='placeholder'] > :nth-child(3) [data-ui='skeleton'] {
1020
+ inline-size: 56%;
1021
+ }
1022
+
1023
+ /**
1024
+ * The status row while an answer is travelling. It no longer draws a pulsing
1025
+ * dot of its own: the control carries the ring and the popup carries the
1026
+ * three bars, and a third repeating motion beside them was one answer too
1027
+ * many for one wait (SL6/CB5).
1028
+ */
1029
+
1030
+ [data-ui='combobox'] > [data-part='content'][data-loading] {
1031
+ cursor: progress;
1032
+ }
1033
+
1034
+ /* ── Forced colours ───────────────────────────────────────────────────── */
1035
+
1036
+ @media (forced-colors: active) {
1037
+ [data-ui='combobox'] [data-part='item'][data-current] {
1038
+ background-color: Highlight;
1039
+ color: HighlightText;
1040
+ forced-color-adjust: none;
1041
+ }
1042
+
1043
+ [data-ui='combobox'] [data-part='item'][data-disabled][data-current] {
1044
+ background-color: Highlight;
1045
+ color: GrayText;
1046
+ }
1047
+ }
1048
+ }