@slotted/styles 0.0.0-stage → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/package.json +276 -3
- package/src/accordion/accordion.css +96 -0
- package/src/accordion/accordion.tokens.json +14 -0
- package/src/action-bar/action-bar.css +455 -0
- package/src/action-bar/action-bar.tokens.json +50 -0
- package/src/action-sheet/action-sheet.css +462 -0
- package/src/action-sheet/action-sheet.tokens.json +59 -0
- package/src/alert/alert.css +764 -0
- package/src/alert/alert.tokens.json +77 -0
- package/src/app-shell/app-shell.css +1899 -0
- package/src/app-shell/app-shell.tokens.json +88 -0
- package/src/aspect-ratio/aspect-ratio.css +131 -0
- package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
- package/src/attachment/attachment.css +906 -0
- package/src/attachment/attachment.tokens.json +85 -0
- package/src/avatar/avatar.css +598 -0
- package/src/avatar/avatar.tokens.json +64 -0
- package/src/avatar-group/avatar-group.css +268 -0
- package/src/avatar-group/avatar-group.tokens.json +16 -0
- package/src/badge/badge.css +462 -0
- package/src/badge/badge.tokens.json +58 -0
- package/src/breadcrumb/breadcrumb.css +441 -0
- package/src/breadcrumb/breadcrumb.tokens.json +50 -0
- package/src/button/button.css +754 -0
- package/src/button/button.tokens.json +72 -0
- package/src/button-group/button-group.css +455 -0
- package/src/button-group/button-group.tokens.json +11 -0
- package/src/calendar/calendar.css +846 -0
- package/src/calendar/calendar.tokens.json +67 -0
- package/src/card/card.css +602 -0
- package/src/card/card.tokens.json +68 -0
- package/src/carousel/carousel.css +860 -0
- package/src/carousel/carousel.tokens.json +62 -0
- package/src/cascader/cascader.css +1320 -0
- package/src/cascader/cascader.tokens.json +103 -0
- package/src/chart/chart.css +1473 -0
- package/src/chart/chart.tokens.json +107 -0
- package/src/checkbox/checkbox.css +1011 -0
- package/src/checkbox/checkbox.tokens.json +102 -0
- package/src/checkbox-group/checkbox-group.css +70 -0
- package/src/checkbox-group/checkbox-group.tokens.json +6 -0
- package/src/code-block/code-block.css +555 -0
- package/src/code-block/code-block.tokens.json +69 -0
- package/src/collapsible/collapsible.css +546 -0
- package/src/collapsible/collapsible.tokens.json +55 -0
- package/src/color-picker/color-picker.css +607 -0
- package/src/color-picker/color-picker.tokens.json +58 -0
- package/src/column-manager/column-manager.css +452 -0
- package/src/column-manager/column-manager.tokens.json +43 -0
- package/src/combobox/combobox.css +1048 -0
- package/src/combobox/combobox.tokens.json +112 -0
- package/src/command-palette/command-palette.css +1000 -0
- package/src/command-palette/command-palette.tokens.json +75 -0
- package/src/container/container.css +198 -0
- package/src/container/container.tokens.json +18 -0
- package/src/context-menu/context-menu.css +104 -0
- package/src/context-menu/context-menu.tokens.json +11 -0
- package/src/currency-field/currency-field.css +787 -0
- package/src/currency-field/currency-field.tokens.json +80 -0
- package/src/dashboard-grid/dashboard-grid.css +748 -0
- package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
- package/src/data-grid/data-grid.css +2484 -0
- package/src/data-grid/data-grid.tokens.json +100 -0
- package/src/data-list/data-list.css +455 -0
- package/src/data-list/data-list.tokens.json +35 -0
- package/src/date-field/date-field.css +560 -0
- package/src/date-field/date-field.tokens.json +80 -0
- package/src/date-picker/date-picker.css +420 -0
- package/src/date-picker/date-picker.tokens.json +49 -0
- package/src/date-range-picker/date-range-picker.css +983 -0
- package/src/date-range-picker/date-range-picker.tokens.json +109 -0
- package/src/datetime-field/datetime-field.css +850 -0
- package/src/datetime-field/datetime-field.tokens.json +91 -0
- package/src/datetime-picker/datetime-picker.css +843 -0
- package/src/datetime-picker/datetime-picker.tokens.json +78 -0
- package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
- package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
- package/src/description-list/description-list.css +675 -0
- package/src/description-list/description-list.tokens.json +66 -0
- package/src/detail-panel/detail-panel.css +329 -0
- package/src/detail-panel/detail-panel.tokens.json +33 -0
- package/src/dialog/dialog.css +968 -0
- package/src/dialog/dialog.tokens.json +66 -0
- package/src/divider/divider.css +131 -0
- package/src/divider/divider.tokens.json +18 -0
- package/src/drawer/drawer.css +1303 -0
- package/src/drawer/drawer.tokens.json +75 -0
- package/src/entity-picker/entity-picker.css +1367 -0
- package/src/entity-picker/entity-picker.tokens.json +118 -0
- package/src/event-calendar/event-calendar.css +2148 -0
- package/src/event-calendar/event-calendar.tokens.json +87 -0
- package/src/field/field.css +707 -0
- package/src/field/field.tokens.json +58 -0
- package/src/field-group/field-group.css +226 -0
- package/src/field-group/field-group.tokens.json +33 -0
- package/src/filter-bar/filter-bar.css +1306 -0
- package/src/filter-bar/filter-bar.tokens.json +78 -0
- package/src/form/form.css +218 -0
- package/src/form/form.tokens.json +32 -0
- package/src/fullscreen/fullscreen.css +376 -0
- package/src/fullscreen/fullscreen.tokens.json +50 -0
- package/src/grid/grid.css +173 -0
- package/src/grid/grid.tokens.json +20 -0
- package/src/highlight/highlight.css +135 -0
- package/src/highlight/highlight.tokens.json +16 -0
- package/src/hover-card/hover-card.css +93 -0
- package/src/hover-card/hover-card.tokens.json +17 -0
- package/src/icon/icon.css +136 -0
- package/src/icon/icon.tokens.json +14 -0
- package/src/image/image.css +449 -0
- package/src/image/image.tokens.json +27 -0
- package/src/import-export-center/import-export-center.css +1175 -0
- package/src/import-export-center/import-export-center.tokens.json +84 -0
- package/src/inline/inline.css +149 -0
- package/src/inline/inline.tokens.json +14 -0
- package/src/inline-message/inline-message.css +396 -0
- package/src/inline-message/inline-message.tokens.json +19 -0
- package/src/kbd/kbd.css +490 -0
- package/src/kbd/kbd.tokens.json +40 -0
- package/src/link/link.css +237 -0
- package/src/link/link.tokens.json +25 -0
- package/src/list/list.css +240 -0
- package/src/list/list.tokens.json +42 -0
- package/src/list-item/list-item.css +937 -0
- package/src/list-item/list-item.tokens.json +114 -0
- package/src/listbox/listbox.css +674 -0
- package/src/listbox/listbox.tokens.json +72 -0
- package/src/loading-overlay/loading-overlay.css +730 -0
- package/src/loading-overlay/loading-overlay.tokens.json +48 -0
- package/src/markdown/markdown.css +168 -0
- package/src/markdown/markdown.tokens.json +18 -0
- package/src/menu/menu.css +817 -0
- package/src/menu/menu.tokens.json +78 -0
- package/src/menubar/menubar.css +170 -0
- package/src/menubar/menubar.tokens.json +32 -0
- package/src/message/message.css +1453 -0
- package/src/message/message.tokens.json +85 -0
- package/src/message-scroller/message-scroller.css +371 -0
- package/src/message-scroller/message-scroller.tokens.json +47 -0
- package/src/multi-select/multi-select.css +1427 -0
- package/src/multi-select/multi-select.tokens.json +114 -0
- package/src/navigation-menu/navigation-menu.css +1029 -0
- package/src/navigation-menu/navigation-menu.tokens.json +70 -0
- package/src/notification-center/notification-center.css +856 -0
- package/src/notification-center/notification-center.tokens.json +84 -0
- package/src/number-field/number-field.css +794 -0
- package/src/number-field/number-field.tokens.json +80 -0
- package/src/otp-input/otp-input.css +525 -0
- package/src/otp-input/otp-input.tokens.json +69 -0
- package/src/page/page.css +179 -0
- package/src/page/page.tokens.json +20 -0
- package/src/page-header/page-header.css +1181 -0
- package/src/page-header/page-header.tokens.json +71 -0
- package/src/pagination/pagination.css +500 -0
- package/src/pagination/pagination.tokens.json +56 -0
- package/src/popover/popover.css +526 -0
- package/src/popover/popover.tokens.json +60 -0
- package/src/progress-indicator/progress-indicator.css +1584 -0
- package/src/progress-indicator/progress-indicator.tokens.json +50 -0
- package/src/prompt-composer/prompt-composer.css +680 -0
- package/src/prompt-composer/prompt-composer.tokens.json +99 -0
- package/src/prose/prose.css +733 -0
- package/src/prose/prose.tokens.json +79 -0
- package/src/radio-group/radio-group.css +778 -0
- package/src/radio-group/radio-group.tokens.json +103 -0
- package/src/rating/rating.css +1071 -0
- package/src/rating/rating.tokens.json +88 -0
- package/src/result-state/result-state.css +980 -0
- package/src/result-state/result-state.tokens.json +63 -0
- package/src/runtime-properties.mjs +321 -0
- package/src/saved-views/saved-views.css +394 -0
- package/src/saved-views/saved-views.tokens.json +41 -0
- package/src/scroll-area/scroll-area.css +481 -0
- package/src/scroll-area/scroll-area.tokens.json +15 -0
- package/src/section/section.css +177 -0
- package/src/section/section.tokens.json +39 -0
- package/src/segmented-control/segmented-control.css +564 -0
- package/src/segmented-control/segmented-control.tokens.json +67 -0
- package/src/select/select.css +1593 -0
- package/src/select/select.tokens.json +110 -0
- package/src/side-navigation/side-navigation.css +1096 -0
- package/src/side-navigation/side-navigation.tokens.json +87 -0
- package/src/skeleton/skeleton.css +431 -0
- package/src/skeleton/skeleton.tokens.json +29 -0
- package/src/slider/slider.css +792 -0
- package/src/slider/slider.tokens.json +75 -0
- package/src/sparkline/sparkline.css +282 -0
- package/src/sparkline/sparkline.tokens.json +45 -0
- package/src/spinner/spinner.css +224 -0
- package/src/spinner/spinner.tokens.json +8 -0
- package/src/split-button/split-button.css +253 -0
- package/src/split-button/split-button.tokens.json +12 -0
- package/src/splitter/splitter.css +336 -0
- package/src/splitter/splitter.tokens.json +24 -0
- package/src/stack/stack.css +128 -0
- package/src/stack/stack.tokens.json +14 -0
- package/src/stat-card/stat-card.css +641 -0
- package/src/stat-card/stat-card.tokens.json +95 -0
- package/src/status-indicator/status-indicator.css +481 -0
- package/src/status-indicator/status-indicator.tokens.json +38 -0
- package/src/stepper/stepper.css +629 -0
- package/src/stepper/stepper.tokens.json +72 -0
- package/src/switch/switch.css +703 -0
- package/src/switch/switch.tokens.json +75 -0
- package/src/table/table.css +1482 -0
- package/src/table/table.tokens.json +69 -0
- package/src/table-of-contents/table-of-contents.css +533 -0
- package/src/table-of-contents/table-of-contents.tokens.json +53 -0
- package/src/tabs/tabs.css +998 -0
- package/src/tabs/tabs.tokens.json +82 -0
- package/src/tag/tag.css +768 -0
- package/src/tag/tag.tokens.json +85 -0
- package/src/tags-input/tags-input.css +757 -0
- package/src/tags-input/tags-input.tokens.json +84 -0
- package/src/text-field/text-field.css +900 -0
- package/src/text-field/text-field.tokens.json +87 -0
- package/src/textarea/textarea.css +365 -0
- package/src/textarea/textarea.tokens.json +59 -0
- package/src/time-field/time-field.css +719 -0
- package/src/time-field/time-field.tokens.json +86 -0
- package/src/time-picker/time-picker.css +665 -0
- package/src/time-picker/time-picker.tokens.json +66 -0
- package/src/time-range-picker/time-range-picker.css +1287 -0
- package/src/time-range-picker/time-range-picker.tokens.json +134 -0
- package/src/timeline/timeline.css +1596 -0
- package/src/timeline/timeline.tokens.json +96 -0
- package/src/toast/toast.css +943 -0
- package/src/toast/toast.tokens.json +90 -0
- package/src/toggle/toggle.css +290 -0
- package/src/toggle/toggle.tokens.json +54 -0
- package/src/toggle-group/toggle-group.css +463 -0
- package/src/toggle-group/toggle-group.tokens.json +54 -0
- package/src/toolbar/toolbar.css +434 -0
- package/src/toolbar/toolbar.tokens.json +49 -0
- package/src/tooltip/tooltip.css +368 -0
- package/src/tooltip/tooltip.tokens.json +39 -0
- package/src/top-navigation/top-navigation.css +1287 -0
- package/src/top-navigation/top-navigation.tokens.json +91 -0
- package/src/tour/tour.css +680 -0
- package/src/tour/tour.tokens.json +67 -0
- package/src/transfer/transfer.css +571 -0
- package/src/transfer/transfer.tokens.json +49 -0
- package/src/tree/tree.css +1000 -0
- package/src/tree/tree.tokens.json +80 -0
- package/src/typography/typography.css +211 -0
- package/src/typography/typography.tokens.json +31 -0
- package/src/upload/upload.css +1706 -0
- package/src/upload/upload.tokens.json +123 -0
- package/src/visually-hidden/visually-hidden.css +52 -0
- package/src/visually-hidden/visually-hidden.tokens.json +1 -0
- package/src/wizard/wizard.css +740 -0
- package/src/wizard/wizard.tokens.json +63 -0
|
@@ -0,0 +1,1427 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── A frame of chosen values, with a field that adds more ───────────────
|
|
4
|
+
*
|
|
5
|
+
* The family is two subtrees, as `select`'s and `combobox`'s are: the
|
|
6
|
+
* **control**, where the consumer wrote it, and the **popup**, portalled to
|
|
7
|
+
* the end of the document so it escapes every clipping ancestor between them.
|
|
8
|
+
* Nothing selects one from the other, so `data-ui='multi-select'` is written
|
|
9
|
+
* on both roots — the contract records it under `portalledIdentity`.
|
|
10
|
+
*
|
|
11
|
+
* ┌─ root ─ the frame ─────────────────────────────────────────┐
|
|
12
|
+
* │ ┌─ control ─ role="grid" ───────────────┐ │
|
|
13
|
+
* │ │ ┌─ chips ─ role="row" ─ wraps ──────┐ │ counter │
|
|
14
|
+
* │ │ │ tag ▸ tag ▸ tag │ │ clear trigger │
|
|
15
|
+
* │ │ │ field ▸ input ─ role="combobox" │ │ ▸ arrow
|
|
16
|
+
* │ │ └───────────────────────────────────┘ │ │
|
|
17
|
+
* │ └───────────────────────────────────────┘ │
|
|
18
|
+
* └────────────────────────────────────────────────────────────┘
|
|
19
|
+
*
|
|
20
|
+
* ┌─ positioner ─ the layer ───────────────────────────────────┐
|
|
21
|
+
* │ ┌─ content ─ role="listbox" ─ scrolls ───────────────┐ │
|
|
22
|
+
* │ │ chosen ─ every value, however the row overflowed │ │
|
|
23
|
+
* │ │ group ▸ group-label │ │
|
|
24
|
+
* │ │ item ▸ icon │ label ▸ match │ indicator │ │
|
|
25
|
+
* │ │ description │ │
|
|
26
|
+
* │ │ separator · empty · status · placeholder │ │
|
|
27
|
+
* │ └────────────────────────────────────────────────────┘ │
|
|
28
|
+
* └────────────────────────────────────────────────────────────┘
|
|
29
|
+
*
|
|
30
|
+
* ── What this sheet does not decide ──────────────────────────────────
|
|
31
|
+
*
|
|
32
|
+
* **Nothing about a chip.** The chips are `tag`'s, worn as `data-ui` beside
|
|
33
|
+
* this family's own parts, so the pill, the small step, and the remove
|
|
34
|
+
* control floored at `--slotted-target-min` are all stated once in
|
|
35
|
+
* `tag/tag.css`. This sheet decides only where the row is and how it wraps.
|
|
36
|
+
*
|
|
37
|
+
* **Nothing about the mark on a matched span.** That is
|
|
38
|
+
* `highlight/highlight.css`, worn as `data-ui='highlight'` beside the `match`
|
|
39
|
+
* part, because every list a reader types into says the same thing about the
|
|
40
|
+
* same span. `combobox` recorded the metric behind the decision.
|
|
41
|
+
*
|
|
42
|
+
* **The popup.** The bounded scrolling surface, the three columns, the
|
|
43
|
+
* subgrid, the check in the last column, the highlight on `data-current` and
|
|
44
|
+
* the two-line label clamp are `select`'s decisions, taken in an earlier
|
|
45
|
+
* session. They are repeated here rather than shared because the three
|
|
46
|
+
* families have three public identifiers, and a stylesheet that painted
|
|
47
|
+
* `[data-ui='select']` from a multi-select would make one family's selector
|
|
48
|
+
* match another's DOM. The duplication is the price of that boundary.
|
|
49
|
+
*
|
|
50
|
+
* ── What this sheet decides on its own ───────────────────────────────
|
|
51
|
+
*
|
|
52
|
+
* **The control grows by rows, and by nothing else.** It is the first family
|
|
53
|
+
* here whose height is not a token: an empty control is exactly the height of
|
|
54
|
+
* a text field beside it on the same form, and every wrapped row of chips
|
|
55
|
+
* adds one chip's height and one gap. Three rows is where it stops growing
|
|
56
|
+
* and starts scrolling — past that it has stopped being a field.
|
|
57
|
+
*
|
|
58
|
+
* **The chips wrap inside the frame, beside the caret.** `select` puts its
|
|
59
|
+
* row *above* its control because its control is a `button` and a button
|
|
60
|
+
* cannot contain the remove button a chip needs. This control is a `div`
|
|
61
|
+
* holding a real `input`, so the chips sit where every surveyed
|
|
62
|
+
* implementation draws them, and `Backspace` can reach them.
|
|
63
|
+
*
|
|
64
|
+
* **Under 120 pixels there is no chip.** A chip narrower than a word is a
|
|
65
|
+
* shape, so the counter answers instead. The width is watched by
|
|
66
|
+
* `createWidthGate` and reported as `data-narrow`, not by a container query:
|
|
67
|
+
* `container-type: inline-size` would stop the frame's own width depending on
|
|
68
|
+
* its contents, which is exactly how a control that is not `fullWidth` is
|
|
69
|
+
* measured. `select` recorded the same finding.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/* ── The frame ────────────────────────────────────────────────────────── */
|
|
73
|
+
|
|
74
|
+
[data-ui='multi-select'][data-part='root'] {
|
|
75
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
76
|
+
--_height: var(--slotted-control-height-md, 34px);
|
|
77
|
+
--_padding-inline: var(--slotted-control-padding-inline-md, 13px);
|
|
78
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
79
|
+
/** The floor under the glyph scale at the dense step; a family token so a theme can retune it. */
|
|
80
|
+
--_icon-floor: var(--slotted-multi-select-icon-size-min, 14px);
|
|
81
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
|
|
82
|
+
|
|
83
|
+
/* `text-field`'s arithmetic, because these are the same buttons in the same
|
|
84
|
+
frame and a second answer would drift from the first. */
|
|
85
|
+
--_action-size: var(
|
|
86
|
+
--slotted-multi-select-action-size,
|
|
87
|
+
min(calc(var(--_icon-size) + 2 * var(--slotted-space-2, 0.25rem)), var(--_inner-size))
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The air above and below a row of chips: a space step, kept at every
|
|
92
|
+
* density. It is the chip that yields, below — not the air — so a control
|
|
93
|
+
* holding one row of chips is the height of an empty one beside it and the
|
|
94
|
+
* chip never touches the frame.
|
|
95
|
+
*
|
|
96
|
+
* The first answer sized the row at the target minimum and let the air
|
|
97
|
+
* give way, which stood the chip edge to edge inside a `dense` frame and
|
|
98
|
+
* two pixels taller than an `sm` one; it was recorded as the honest price
|
|
99
|
+
* of a chip at the tightest step, and the reviewer refused it at
|
|
100
|
+
* `tags-input`'s release gate. The geometry below is `tags-input.css`'s.
|
|
101
|
+
*/
|
|
102
|
+
--_row-air: var(--slotted-space-1, 0.125rem);
|
|
103
|
+
/**
|
|
104
|
+
* One row's height is the height the foundation gives the inside of a
|
|
105
|
+
* control at this size step, `--slotted-control-inner-size-*`: the chip
|
|
106
|
+
* takes it through the property the row writes below, so a `dense` frame
|
|
107
|
+
* of 26 pixels holds an 18-pixel chip with four pixels of air a side, and
|
|
108
|
+
* three rows plus their gaps divide the cap below with no sliver of a
|
|
109
|
+
* fourth. The type stays 12px; the chip's line and inset are derived from
|
|
110
|
+
* the height in `tag.css`, and the remove control keeps its 24-pixel target
|
|
111
|
+
* through `tag`'s own halo.
|
|
112
|
+
*/
|
|
113
|
+
--_inner-size: var(--slotted-control-inner-size-md, 24px);
|
|
114
|
+
--_row-size: var(--_inner-size);
|
|
115
|
+
--_row-gap: var(--slotted-space-2, 0.25rem);
|
|
116
|
+
|
|
117
|
+
align-items: center;
|
|
118
|
+
background-color: var(--slotted-multi-select-background, var(--slotted-control-surface, Field));
|
|
119
|
+
border: var(--slotted-control-border-width, 1px) solid
|
|
120
|
+
var(--slotted-multi-select-border-color, var(--slotted-control-border-color, ButtonBorder));
|
|
121
|
+
border-radius: var(--slotted-multi-select-radius, var(--slotted-control-radius, 4px));
|
|
122
|
+
box-shadow: var(--slotted-multi-select-shadow, var(--slotted-control-shadow, none));
|
|
123
|
+
box-sizing: border-box;
|
|
124
|
+
color: var(--slotted-multi-select-color, var(--slotted-text, CanvasText));
|
|
125
|
+
display: flex;
|
|
126
|
+
font-family: var(--slotted-control-font-family, system-ui, sans-serif);
|
|
127
|
+
font-size: var(--_font-size);
|
|
128
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
129
|
+
inline-size: 100%;
|
|
130
|
+
min-block-size: var(--_height);
|
|
131
|
+
min-inline-size: 0;
|
|
132
|
+
padding-inline-start: var(--_padding-inline);
|
|
133
|
+
/* The end padding is the buttons' gutter rather than the frame's, so the
|
|
134
|
+
glyph — not the button's box — ends where the text begins on the other
|
|
135
|
+
side. `text-field` states the same arithmetic. */
|
|
136
|
+
padding-inline-end: max(
|
|
137
|
+
0px,
|
|
138
|
+
calc(var(--_padding-inline) - (var(--_action-size) - var(--_icon-size)) / 2)
|
|
139
|
+
);
|
|
140
|
+
transition:
|
|
141
|
+
background-color var(--slotted-control-transition-duration)
|
|
142
|
+
var(--slotted-control-transition-easing),
|
|
143
|
+
border-color var(--slotted-control-transition-duration)
|
|
144
|
+
var(--slotted-control-transition-easing),
|
|
145
|
+
box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
[data-ui='multi-select'][data-part='root']:not([data-full-width]) {
|
|
149
|
+
inline-size: auto;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The grid. It holds one row and gives up every visual decision to it: the
|
|
154
|
+
* role is what the layout-grid pattern asks for, and the geometry belongs to
|
|
155
|
+
* the row that wraps.
|
|
156
|
+
*/
|
|
157
|
+
[data-ui='multi-select'] > [data-part='control'] {
|
|
158
|
+
display: flex;
|
|
159
|
+
flex: 1 1 auto;
|
|
160
|
+
min-inline-size: 0;
|
|
161
|
+
/* The frame stretches to the row; the row is what has a height. */
|
|
162
|
+
align-self: stretch;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The distance between the values and the controls that act on them. The
|
|
167
|
+
* frame's own gap is the tight one the action buttons want between each
|
|
168
|
+
* other, and a chip has a hard edge where an input's text has none: the last
|
|
169
|
+
* chip stood two pixels from the clear button while the chips stood four
|
|
170
|
+
* pixels from each other, so the boundary between what is chosen and what
|
|
171
|
+
* acts on it was narrower than the rhythm inside it. Whichever part comes
|
|
172
|
+
* first after the row takes the row's own gap.
|
|
173
|
+
*/
|
|
174
|
+
[data-ui='multi-select'] > [data-part='control'] + * {
|
|
175
|
+
margin-inline-start: var(--_row-gap);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The row. One DOM row that wraps onto as many visual lines as it needs,
|
|
180
|
+
* which is what keeps `ArrowDown` and `ArrowUp` free for the popup: there is
|
|
181
|
+
* no cell above or below any cell, so the grid answers neither key.
|
|
182
|
+
*/
|
|
183
|
+
[data-ui='multi-select'] [data-part='chips'] {
|
|
184
|
+
/**
|
|
185
|
+
* `safe`, because this box scrolls. Centring a scroll container's content
|
|
186
|
+
* pushes the first line above the scrollport when there is more than fits,
|
|
187
|
+
* and the browser gives no way to scroll back to it: the ninth chip was on
|
|
188
|
+
* screen and the first three were unreachable at 240 pixels. `safe` falls
|
|
189
|
+
* back to `start` exactly when the content overflows, which is the one case
|
|
190
|
+
* where centring is wrong.
|
|
191
|
+
*/
|
|
192
|
+
align-content: safe center;
|
|
193
|
+
align-items: center;
|
|
194
|
+
/* The cap below counts rows, gaps and the row's own air, so it has to be a
|
|
195
|
+
border box: against a content box the same number left four pixels of a
|
|
196
|
+
fourth row showing under the third. */
|
|
197
|
+
box-sizing: border-box;
|
|
198
|
+
column-gap: var(--_row-gap);
|
|
199
|
+
display: flex;
|
|
200
|
+
flex: 1 1 auto;
|
|
201
|
+
flex-wrap: wrap;
|
|
202
|
+
min-inline-size: 0;
|
|
203
|
+
padding-block: var(--_row-air);
|
|
204
|
+
/**
|
|
205
|
+
* The row scrolls, and a box that scrolls on one axis clips on both. A
|
|
206
|
+
* chip's remove control takes the rest of its own target from a transparent
|
|
207
|
+
* halo that stands outside the chip (TG2), so the row keeps two pixels of
|
|
208
|
+
* inline padding for it and takes them back with a negative margin: the
|
|
209
|
+
* chips stand exactly where the frame's gutter puts them, and the halo of
|
|
210
|
+
* the first and last is inside the scrollport rather than against its edge.
|
|
211
|
+
*/
|
|
212
|
+
padding-inline: var(--_row-air);
|
|
213
|
+
margin-inline: calc(-1 * var(--_row-air));
|
|
214
|
+
row-gap: var(--_row-gap);
|
|
215
|
+
/**
|
|
216
|
+
* Three rows, then it scrolls. Three *whole* rows: a chip drawn to half its
|
|
217
|
+
* height is not an affordance, it is a chip that looks broken. Every chip
|
|
218
|
+
* stays in the DOM either way, which is what the feature guide asks of an
|
|
219
|
+
* overflow — the set is summarised, never hidden.
|
|
220
|
+
*/
|
|
221
|
+
max-block-size: calc(3 * var(--_row-size) + 2 * var(--_row-gap) + 2 * var(--_row-air));
|
|
222
|
+
overflow-y: auto;
|
|
223
|
+
overscroll-behavior: contain;
|
|
224
|
+
/**
|
|
225
|
+
* The row scrolls and does not draw a scrollbar. A form control has one
|
|
226
|
+
* frame and a bar inside that frame reads as a panel that got into a field:
|
|
227
|
+
* it takes width from the chips, moves them when it appears, and paints a
|
|
228
|
+
* second edge inside the control's own. The bar is refused, not the
|
|
229
|
+
* scrolling — the wheel, the trackpad and every arrow key still reach the
|
|
230
|
+
* rows above and below, because a chip is focusable and the browser scrolls
|
|
231
|
+
* focus into view.
|
|
232
|
+
*
|
|
233
|
+
* What says there is more is not the bar. It is the counter beside the row,
|
|
234
|
+
* the row's own `aria-label` carrying the count, and the band at the top of
|
|
235
|
+
* the popup, which holds every chosen value whatever this row did with them.
|
|
236
|
+
* A reader who cannot see all the chips has three places that say how many
|
|
237
|
+
* there are; a reader who could see a scrollbar would have four, and one of
|
|
238
|
+
* them would be inside a field.
|
|
239
|
+
*/
|
|
240
|
+
scrollbar-width: none;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
[data-ui='multi-select'] [data-part='chips']::-webkit-scrollbar {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* One line high, whatever is in it. For a control in a table row or a filter
|
|
249
|
+
* bar, where the frame cannot be allowed to grow: what does not fit is
|
|
250
|
+
* collapsed by `createCollapse` into the counting chip below, and the whole
|
|
251
|
+
* set is still reachable in the popup's `chosen` band.
|
|
252
|
+
*/
|
|
253
|
+
[data-ui='multi-select'][data-overflow='collapse'] [data-part='chips'] {
|
|
254
|
+
flex-wrap: nowrap;
|
|
255
|
+
max-block-size: none;
|
|
256
|
+
overflow: hidden;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* A chip being edited. The grid pattern's own answer to a cell holding
|
|
261
|
+
* editable content is a text field inside the cell, so that is what it is —
|
|
262
|
+
* and it gives up every mark of a field, because the chip around it is
|
|
263
|
+
* already the box. It takes the chip's own type and ink so the word does not
|
|
264
|
+
* move when editing begins.
|
|
265
|
+
*
|
|
266
|
+
* Selected by element rather than by part: the field belongs to the chip and
|
|
267
|
+
* not to this family, so it has no `data-part` of its own to be selected by,
|
|
268
|
+
* and the query's own input is told apart by the part it does have.
|
|
269
|
+
*/
|
|
270
|
+
[data-ui='multi-select'] [data-part='chips'] input:not([data-part='input']) {
|
|
271
|
+
background-color: transparent;
|
|
272
|
+
border: 0;
|
|
273
|
+
color: inherit;
|
|
274
|
+
font: inherit;
|
|
275
|
+
min-inline-size: 0;
|
|
276
|
+
outline: none;
|
|
277
|
+
padding: 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* A chip never shrinks. Flexbox shrinks before it wraps when it is allowed
|
|
282
|
+
* to, and at 240 pixels that turned a run of chips into `B.. ×` beside a
|
|
283
|
+
* remove button with nothing left to remove. A chip either fits on the line
|
|
284
|
+
* or goes to the next one; the only chip that gives way is one wider than the
|
|
285
|
+
* whole row, which is capped here and truncated by `tag.css`.
|
|
286
|
+
*/
|
|
287
|
+
[data-ui='multi-select'] [data-part='chips'] > :not([data-part='field']) {
|
|
288
|
+
flex: 0 0 auto;
|
|
289
|
+
max-inline-size: 100%;
|
|
290
|
+
/* The row's own height, so three chips plus their gaps divide the cap the
|
|
291
|
+
row above states and no fourth row shows as a sliver under the third. */
|
|
292
|
+
min-block-size: var(--_row-size);
|
|
293
|
+
min-inline-size: 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The chip takes the height the inside of this control has, through the
|
|
298
|
+
* property `tag` reads for it. Set on the row, inherited by every chip, and
|
|
299
|
+
* no rule of this sheet names `[data-ui='tag']`: the host stands beside the
|
|
300
|
+
* tag sheet and never reaches into it.
|
|
301
|
+
*/
|
|
302
|
+
[data-ui='multi-select'] [data-part='chips'] {
|
|
303
|
+
--slotted-control-inner-size: var(--_inner-size);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The cell that holds the caret. It is the last cell of the row, so it wraps
|
|
308
|
+
* with the chips rather than being pushed to a line of its own, and it asks
|
|
309
|
+
* for a word's worth of width so the reader always has somewhere to type.
|
|
310
|
+
*
|
|
311
|
+
* It asks rather than demands: the basis is a word and the floor is a couple
|
|
312
|
+
* of characters, because a cell that insisted on a word sent itself to a line
|
|
313
|
+
* of its own the moment two chips were in front of it, and a control 240
|
|
314
|
+
* pixels wide holding two values then stood two rows tall with one row empty.
|
|
315
|
+
*/
|
|
316
|
+
[data-ui='multi-select'] [data-part='field'] {
|
|
317
|
+
align-items: center;
|
|
318
|
+
display: flex;
|
|
319
|
+
flex: 1 1 var(--slotted-multi-select-field-basis, 4rem);
|
|
320
|
+
min-block-size: var(--_row-size);
|
|
321
|
+
min-inline-size: var(--slotted-multi-select-field-min, 2.5rem);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The control's text. It gives up its own frame entirely and keeps
|
|
326
|
+
* everything that makes it a text control: the caret, the selection, IME
|
|
327
|
+
* composition, autofill and every native attribute the consumer wrote.
|
|
328
|
+
*/
|
|
329
|
+
[data-ui='multi-select'] [data-part='input'] {
|
|
330
|
+
background-color: transparent;
|
|
331
|
+
border: 0;
|
|
332
|
+
color: inherit;
|
|
333
|
+
flex: 1 1 auto;
|
|
334
|
+
font: inherit;
|
|
335
|
+
/* The frame draws the ring for it, so two rings never nest. */
|
|
336
|
+
outline: none;
|
|
337
|
+
padding: 0;
|
|
338
|
+
/* The intrinsic width of an `input` is about twenty characters, and a flex
|
|
339
|
+
item refuses to shrink below it without this. */
|
|
340
|
+
min-inline-size: 0;
|
|
341
|
+
text-overflow: ellipsis;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
[data-ui='multi-select'] [data-part='input']::placeholder {
|
|
345
|
+
color: var(--slotted-multi-select-placeholder-color, var(--slotted-text-muted, GrayText));
|
|
346
|
+
text-overflow: ellipsis;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Autofill. Chrome paints its own ground over the control's, and a
|
|
351
|
+
* hundred-pixel inset shadow is the only way to reach it. The signal is kept
|
|
352
|
+
* rather than erased, in the accent surface the theme owns — the repair
|
|
353
|
+
* `text-field` makes, for the same reason.
|
|
354
|
+
*/
|
|
355
|
+
[data-ui='multi-select'] [data-part='input']:autofill {
|
|
356
|
+
box-shadow: inset 0 0 0 100px
|
|
357
|
+
var(--slotted-multi-select-autofill-background, var(--slotted-tone-accent-surface));
|
|
358
|
+
-webkit-text-fill-color: var(--slotted-multi-select-color, var(--slotted-text, CanvasText));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* A chip that holds focus. The cell takes the ring rather than the remove
|
|
363
|
+
* button inside it — the departure the contract records against the grid
|
|
364
|
+
* pattern — so the ring is drawn around the value, which is the thing
|
|
365
|
+
* `Delete` acts on. It follows the chip's own radius by being an outline on
|
|
366
|
+
* the pill itself.
|
|
367
|
+
*
|
|
368
|
+
* The ring is drawn one width inside the chip, not at the theme's offset
|
|
369
|
+
* outside it. The row around the chip scrolls, or clips under
|
|
370
|
+
* `overflow='collapse'`, and either way it cuts at its padding edge, two
|
|
371
|
+
* pixels past the chip; a ring two pixels further out lost its outer half on
|
|
372
|
+
* every side, at every density. One width inside is `listbox`'s answer for an
|
|
373
|
+
* option in a scrolling list: the ring covers the chip's own edge and reads
|
|
374
|
+
* as the chip's edge turned to the ring colour, whole, with the text clear
|
|
375
|
+
* of it even at `dense`, where the chip has no inset to spare. `tag` draws
|
|
376
|
+
* its own inner ring two widths in to keep a sliver of ground outside it;
|
|
377
|
+
* here that sliver would cost the text its leading.
|
|
378
|
+
*/
|
|
379
|
+
[data-ui='multi-select'] [data-part='chips'] > :not([data-part='field']):focus-visible {
|
|
380
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
381
|
+
outline-offset: calc(-1 * var(--slotted-focus-ring-width, 2px));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The chip that counts what did not fit, under `overflow='collapse'`. It is a
|
|
386
|
+
* chip and not a number beside them, so the row reads as one run of chips
|
|
387
|
+
* whose last one happens to be a count, and `createCollapse` measures it the
|
|
388
|
+
* way it measures every other unit.
|
|
389
|
+
*/
|
|
390
|
+
[data-ui='multi-select'] [data-part='chips'] > [data-count] {
|
|
391
|
+
flex: none;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* The count of what is held, against the limit that holds it. It is drawn
|
|
396
|
+
* only where it answers something — while `max` is set, or while the frame is
|
|
397
|
+
* too narrow for a chip — because a counter with nothing to count against is
|
|
398
|
+
* furniture.
|
|
399
|
+
*/
|
|
400
|
+
[data-ui='multi-select'] > [data-part='counter'] {
|
|
401
|
+
text-box: auto; /* refine the alignment */
|
|
402
|
+
align-self: center;
|
|
403
|
+
color: var(--slotted-multi-select-counter-color, var(--slotted-text-muted, GrayText));
|
|
404
|
+
/* It gives way before the buttons do: under 120 pixels the counter, the
|
|
405
|
+
clear button and the trigger needed 127 pixels of a 112-pixel frame, and
|
|
406
|
+
the trigger ran past the border. The grid's aria-label carries the count
|
|
407
|
+
whole. */
|
|
408
|
+
flex: 0 1 auto;
|
|
409
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
410
|
+
min-inline-size: 0;
|
|
411
|
+
overflow: hidden;
|
|
412
|
+
text-overflow: ellipsis;
|
|
413
|
+
font-variant-numeric: tabular-nums;
|
|
414
|
+
line-height: var(--slotted-line-height-sm, 1.45);
|
|
415
|
+
padding-inline: var(--slotted-space-1, 0.125rem);
|
|
416
|
+
/* Its own bidi run, ordered by its own content: "3 of 5" is a European
|
|
417
|
+
number, a Latin word and another number, and in a right-to-left document
|
|
418
|
+
the neutral spaces resolve to the paragraph and the runs swap. The repair
|
|
419
|
+
`status`, `empty` and `select`'s summary all make. */
|
|
420
|
+
unicode-bidi: plaintext;
|
|
421
|
+
white-space: nowrap;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* A full control says so where the count is, in the accent the family already
|
|
426
|
+
* uses for a chosen value. Not a warning tone: reaching the limit is the
|
|
427
|
+
* reader arriving somewhere, not going wrong.
|
|
428
|
+
*/
|
|
429
|
+
[data-ui='multi-select'][data-complete] > [data-part='counter'] {
|
|
430
|
+
color: var(--slotted-multi-select-counter-complete-color, var(--slotted-tone-accent-text));
|
|
431
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Too narrow for a chip: the count is the whole answer. */
|
|
435
|
+
[data-ui='multi-select'][data-narrow] [data-part='chips'] > :not([data-part='field']) {
|
|
436
|
+
display: none;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* The row takes only the room the count and the buttons leave, so the count
|
|
440
|
+
is what shortens when even that room runs out. */
|
|
441
|
+
[data-ui='multi-select'][data-narrow] > [data-part='control'] {
|
|
442
|
+
flex-basis: 0;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* ── The two buttons inside the frame ─────────────────────────────────── */
|
|
446
|
+
|
|
447
|
+
[data-ui='multi-select'] > :is([data-part='clear'], [data-part='trigger']) {
|
|
448
|
+
align-items: center;
|
|
449
|
+
align-self: center;
|
|
450
|
+
appearance: none;
|
|
451
|
+
background-color: transparent;
|
|
452
|
+
block-size: var(--_action-size);
|
|
453
|
+
border: 0;
|
|
454
|
+
border-radius: var(--slotted-multi-select-radius, var(--slotted-control-radius, 4px));
|
|
455
|
+
color: var(--slotted-multi-select-action-color, var(--slotted-text-muted, GrayText));
|
|
456
|
+
cursor: pointer;
|
|
457
|
+
display: inline-flex;
|
|
458
|
+
flex: 0 0 auto;
|
|
459
|
+
inline-size: var(--_action-size);
|
|
460
|
+
justify-content: center;
|
|
461
|
+
/* The user agent pads a button, and inside a box sized to its glyph that
|
|
462
|
+
padding squeezed the glyph to twelve pixels. */
|
|
463
|
+
padding: 0;
|
|
464
|
+
/* The containing block of the hit area below. */
|
|
465
|
+
position: relative;
|
|
466
|
+
touch-action: manipulation;
|
|
467
|
+
transition:
|
|
468
|
+
background-color var(--slotted-control-transition-duration)
|
|
469
|
+
var(--slotted-control-transition-easing),
|
|
470
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
471
|
+
-webkit-tap-highlight-color: transparent;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* The hit area, recovered outside the paint. At `sm` with `dense` the whole
|
|
476
|
+
* control is 24 pixels tall — the WCAG 2.2 minimum — so nothing drawn inside
|
|
477
|
+
* it can also be 24.
|
|
478
|
+
*/
|
|
479
|
+
[data-ui='multi-select'] > :is([data-part='clear'], [data-part='trigger'])::after {
|
|
480
|
+
content: '';
|
|
481
|
+
inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
|
|
482
|
+
position: absolute;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* On a finger the target is the comfortable size in both axes and the box
|
|
486
|
+
is the one a mouse sees: the ground and the focus ring stay square, and
|
|
487
|
+
the hit area above grows to 44 pixels around them. Side by side, neither
|
|
488
|
+
button reaches across the edge the two share. */
|
|
489
|
+
@media (pointer: coarse) {
|
|
490
|
+
[data-ui='multi-select'] > :is([data-part='clear'], [data-part='trigger'])::after {
|
|
491
|
+
inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
[data-ui='multi-select']
|
|
495
|
+
> [data-part='clear']:has(+ [data-part='trigger']:not([hidden]))::after {
|
|
496
|
+
inset-inline-end: 0;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
[data-ui='multi-select'] > [data-part='clear']:not([hidden]) + [data-part='trigger']::after {
|
|
500
|
+
inset-inline-start: 0;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* The cross and the chevron are `Glyph` elements the binding draws, sized
|
|
505
|
+
here to the control's icon step. */
|
|
506
|
+
[data-ui='multi-select'] > :is([data-part='clear'], [data-part='trigger']) {
|
|
507
|
+
--slotted-icon-size: var(--_icon-size);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* The chevron, drawn by its own part rather than by the button, because the
|
|
512
|
+
* button is what a reader presses and the arrow is what turns over. The
|
|
513
|
+
* library's own `Glyph` in an `icon` box; a character is never an icon.
|
|
514
|
+
*/
|
|
515
|
+
[data-ui='multi-select'] [data-part='arrow'] {
|
|
516
|
+
align-items: center;
|
|
517
|
+
block-size: var(--_icon-size);
|
|
518
|
+
display: flex;
|
|
519
|
+
inline-size: var(--_icon-size);
|
|
520
|
+
justify-content: center;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
[data-ui='multi-select'] [data-part='arrow'] > [data-ui='icon'] {
|
|
524
|
+
transition: rotate var(--slotted-control-transition-duration)
|
|
525
|
+
var(--slotted-control-transition-easing);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
[data-ui='multi-select'][data-open] [data-part='arrow'] > [data-ui='icon'] {
|
|
529
|
+
rotate: 180deg;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* The ring, where the chevron stands. `select`'s decision and `select`'s
|
|
534
|
+
* reasons: while an answer is travelling the control shows the ring rather
|
|
535
|
+
* than the chevron — the chevron says the popup opens, and it does not open
|
|
536
|
+
* onto anything yet — and the arc is the one `spinner/spinner.css` draws for
|
|
537
|
+
* every family that waits.
|
|
538
|
+
*/
|
|
539
|
+
[data-ui='multi-select'] [data-part='busy'] {
|
|
540
|
+
--slotted-spinner-size: var(--_icon-size);
|
|
541
|
+
|
|
542
|
+
align-self: center;
|
|
543
|
+
color: var(--slotted-multi-select-arrow-color, var(--slotted-text-muted, GrayText));
|
|
544
|
+
flex: none;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* ── Size ─────────────────────────────────────────────────────────────── */
|
|
548
|
+
|
|
549
|
+
[data-ui='multi-select'][data-part='root'][data-size='sm'] {
|
|
550
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
551
|
+
--_height: var(--slotted-control-height-sm, 30px);
|
|
552
|
+
--_inner-size: var(--slotted-control-inner-size-sm, 22px);
|
|
553
|
+
--_padding-inline: var(--slotted-control-padding-inline-sm, 10px);
|
|
554
|
+
--_gap: var(--slotted-control-gap-sm, 6px);
|
|
555
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-sm, 14px));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
[data-ui='multi-select'][data-part='root'][data-size='md'] {
|
|
559
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
560
|
+
--_height: var(--slotted-control-height-md, 34px);
|
|
561
|
+
--_padding-inline: var(--slotted-control-padding-inline-md, 13px);
|
|
562
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
563
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
|
|
564
|
+
--_inner-size: var(--slotted-control-inner-size-md, 24px);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
[data-ui='multi-select'][data-part='root'][data-size='lg'] {
|
|
568
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
569
|
+
--_height: var(--slotted-control-height-lg, 38px);
|
|
570
|
+
--_inner-size: var(--slotted-control-inner-size-lg, 28px);
|
|
571
|
+
--_padding-inline: var(--slotted-control-padding-inline-lg, 16px);
|
|
572
|
+
--_gap: var(--slotted-control-gap-lg, 8px);
|
|
573
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-lg, 18px));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* ── Variant ──────────────────────────────────────────────────────────── */
|
|
577
|
+
|
|
578
|
+
[data-ui='multi-select'][data-part='root'][data-variant='subtle'] {
|
|
579
|
+
background-color: var(
|
|
580
|
+
--slotted-multi-select-subtle-background,
|
|
581
|
+
var(--slotted-surface-sunken, Canvas)
|
|
582
|
+
);
|
|
583
|
+
border-color: var(
|
|
584
|
+
--slotted-multi-select-subtle-border-color,
|
|
585
|
+
var(--slotted-border-subtle, ButtonBorder)
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/* No edge, no lift, no ground and no corner of its own: the cell it sits in
|
|
590
|
+
is the box, the row is the ground, a cell has no corner, and the resting
|
|
591
|
+
shadow would draw the cell where there is none. */
|
|
592
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'] {
|
|
593
|
+
background-color: transparent;
|
|
594
|
+
border-color: transparent;
|
|
595
|
+
border-radius: var(--slotted-radius-none, 0px);
|
|
596
|
+
box-shadow: none;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@media (forced-colors: active) {
|
|
600
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'] {
|
|
601
|
+
border-color: ButtonBorder;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/* ── States on the control ────────────────────────────────────────────── */
|
|
606
|
+
|
|
607
|
+
[data-ui='multi-select'][data-part='root']:hover:not([data-disabled]):not([data-readonly]):not(
|
|
608
|
+
[data-invalid]
|
|
609
|
+
) {
|
|
610
|
+
border-color: var(--slotted-multi-select-hover-border-color, var(--slotted-tone-accent-border));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
[data-ui='multi-select'][data-part='root'][data-variant='subtle']:hover:not([data-disabled]):not(
|
|
614
|
+
[data-readonly]
|
|
615
|
+
):not([data-invalid]) {
|
|
616
|
+
background-color: var(
|
|
617
|
+
--slotted-multi-select-subtle-hover-background,
|
|
618
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain']:hover:not([data-disabled]):not(
|
|
623
|
+
[data-readonly]
|
|
624
|
+
):not([data-invalid]) {
|
|
625
|
+
background-color: var(
|
|
626
|
+
--slotted-multi-select-plain-hover-background,
|
|
627
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
628
|
+
);
|
|
629
|
+
/* The accent edge the other variants take would draw the cell's outline. */
|
|
630
|
+
border-color: transparent;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* The frame carries the ring for whatever inside it holds focus — the input
|
|
635
|
+
* or a chip — because the frame is the control a reader is operating.
|
|
636
|
+
* `:focus-within` rather than `:has(:focus-visible)`: a chip is focused by
|
|
637
|
+
* script, after a key, and a browser does not always match `:focus-visible`
|
|
638
|
+
* on an element script moved focus to.
|
|
639
|
+
*/
|
|
640
|
+
[data-ui='multi-select'][data-part='root']:has([data-part='input']:focus-visible),
|
|
641
|
+
[data-ui='multi-select'][data-part='root']:has(
|
|
642
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
643
|
+
) {
|
|
644
|
+
box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
|
|
645
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
646
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
647
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* The ring of a control whose box is not its own is drawn inside that box,
|
|
652
|
+
* the way the inner controls draw theirs inside the frame: two pixels past
|
|
653
|
+
* the control is two pixels over the next row's rule. No lift either — a
|
|
654
|
+
* shadow that appears only under focus is a cell that changes size when it
|
|
655
|
+
* is used.
|
|
656
|
+
*/
|
|
657
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain']:has(
|
|
658
|
+
[data-part='input']:focus-visible
|
|
659
|
+
),
|
|
660
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain']:has(
|
|
661
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
662
|
+
) {
|
|
663
|
+
box-shadow: none;
|
|
664
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* A chip already wears the ring, and the frame around it wearing a second one
|
|
669
|
+
* is two rings for one focus. The frame keeps its lift and gives up the
|
|
670
|
+
* outline while the ring is inside it.
|
|
671
|
+
*/
|
|
672
|
+
[data-ui='multi-select'][data-part='root']:has(
|
|
673
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
674
|
+
) {
|
|
675
|
+
outline: none;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
[data-ui='multi-select'][data-part='root'][data-open]:not([data-disabled]) {
|
|
679
|
+
border-color: var(--slotted-multi-select-open-border-color, var(--slotted-tone-accent-border));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
[data-ui='multi-select'][data-part='root'][data-invalid] {
|
|
683
|
+
--slotted-focus-ring-color: var(
|
|
684
|
+
--slotted-multi-select-invalid-focus-ring-color,
|
|
685
|
+
var(--slotted-focus-ring-color-invalid, Highlight)
|
|
686
|
+
);
|
|
687
|
+
--_invalid-edge-width: var(
|
|
688
|
+
--slotted-multi-select-invalid-edge-width,
|
|
689
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
690
|
+
);
|
|
691
|
+
--_invalid-edge-color: var(
|
|
692
|
+
--slotted-multi-select-invalid-border-color,
|
|
693
|
+
var(--slotted-tone-danger-border)
|
|
694
|
+
);
|
|
695
|
+
--_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
|
|
696
|
+
|
|
697
|
+
border-color: var(
|
|
698
|
+
--slotted-multi-select-invalid-border-color,
|
|
699
|
+
var(--slotted-tone-danger-border)
|
|
700
|
+
);
|
|
701
|
+
/*
|
|
702
|
+
* Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
|
|
703
|
+
* `high-contrast` the danger border is the resting edge's black. The line
|
|
704
|
+
* is a shadow inside the border, so neither box nor text moves, and it
|
|
705
|
+
* takes the resting lift's place.
|
|
706
|
+
*/
|
|
707
|
+
box-shadow: var(--_invalid-edge);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
[data-ui='multi-select'][data-part='root'][data-invalid]:has([data-part='input']:focus-visible),
|
|
711
|
+
[data-ui='multi-select'][data-part='root'][data-invalid]:has(
|
|
712
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
713
|
+
) {
|
|
714
|
+
box-shadow:
|
|
715
|
+
var(--_invalid-edge),
|
|
716
|
+
var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
|
|
717
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
721
|
+
[data-part='input']:focus-visible
|
|
722
|
+
),
|
|
723
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
724
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
725
|
+
) {
|
|
726
|
+
/* The ring lies inside the box here, over the border and an edge as wide as
|
|
727
|
+
it; the edge reaches past the ring. */
|
|
728
|
+
box-shadow: inset 0 0 0
|
|
729
|
+
calc(
|
|
730
|
+
var(--slotted-focus-ring-offset, 2px) - var(--slotted-control-border-width, 1px) +
|
|
731
|
+
var(--_invalid-edge-width)
|
|
732
|
+
)
|
|
733
|
+
var(--_invalid-edge-color);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/*
|
|
737
|
+
* Forced colours remove every box shadow, and the edge with them. The
|
|
738
|
+
* width comes back as an outline inside the border, and a focused invalid
|
|
739
|
+
* control draws its ring double, which a valid ring is not.
|
|
740
|
+
*/
|
|
741
|
+
@media (forced-colors: active) {
|
|
742
|
+
[data-ui='multi-select'][data-part='root'][data-invalid] {
|
|
743
|
+
outline: var(--_invalid-edge-width) solid CanvasText;
|
|
744
|
+
outline-offset: calc(
|
|
745
|
+
-1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
[data-ui='multi-select'][data-part='root'][data-invalid]:has(
|
|
750
|
+
[data-part='input']:focus-visible
|
|
751
|
+
) {
|
|
752
|
+
outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
|
|
753
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
754
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
758
|
+
[data-part='input']:focus-visible
|
|
759
|
+
) {
|
|
760
|
+
outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
|
|
761
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
762
|
+
outline-offset: calc(
|
|
763
|
+
-1 * (var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width))
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
[data-ui='multi-select'][data-part='root'][data-invalid]:has(
|
|
768
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
769
|
+
),
|
|
770
|
+
[data-ui='multi-select'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
771
|
+
[data-part='chips'] > :not([data-part='field']):focus-visible
|
|
772
|
+
) {
|
|
773
|
+
outline: var(--_invalid-edge-width) solid CanvasText;
|
|
774
|
+
outline-offset: calc(
|
|
775
|
+
-1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Read-only keeps the chips, the caret, the selection and the copy, and
|
|
782
|
+
* withholds the popup and every remove control. The native `readonly` gives
|
|
783
|
+
* the text half; the rest is the family's, because neither pattern has a
|
|
784
|
+
* readonly for a combobox.
|
|
785
|
+
*/
|
|
786
|
+
[data-ui='multi-select'][data-part='root'][data-readonly] {
|
|
787
|
+
background-color: var(
|
|
788
|
+
--slotted-multi-select-readonly-background,
|
|
789
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
[data-ui='multi-select'][data-part='root'][data-readonly] [data-part='arrow'] {
|
|
794
|
+
opacity: 0.5;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* `:disabled` on the input is matched beside `data-disabled` on the frame,
|
|
799
|
+
* because a disabled `fieldset` disables every control inside it and the
|
|
800
|
+
* control has no way to say so.
|
|
801
|
+
*/
|
|
802
|
+
[data-ui='multi-select'][data-part='root'][data-disabled],
|
|
803
|
+
[data-ui='multi-select'][data-part='root']:has([data-part='input']:disabled) {
|
|
804
|
+
background-color: var(
|
|
805
|
+
--slotted-multi-select-disabled-background,
|
|
806
|
+
var(--slotted-disabled-background)
|
|
807
|
+
);
|
|
808
|
+
border-color: var(--slotted-multi-select-disabled-border-color, var(--slotted-disabled-border));
|
|
809
|
+
box-shadow: none;
|
|
810
|
+
color: var(--slotted-multi-select-disabled-color, var(--slotted-disabled-foreground));
|
|
811
|
+
cursor: not-allowed;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
[data-ui='multi-select']
|
|
815
|
+
> :is([data-part='clear'], [data-part='trigger']):hover:not(:disabled):not([data-disabled]) {
|
|
816
|
+
background-color: var(
|
|
817
|
+
--slotted-multi-select-action-hover-background,
|
|
818
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
[data-ui='multi-select']
|
|
823
|
+
> :is([data-part='clear'], [data-part='trigger']):active:not(:disabled):not([data-disabled]) {
|
|
824
|
+
background-color: var(
|
|
825
|
+
--slotted-multi-select-action-active-background,
|
|
826
|
+
var(--slotted-tone-secondary-subtle-active)
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Each button carries its own ring rather than borrowing the frame's.
|
|
832
|
+
* Tabbing from the input to the clear button has to move something visible,
|
|
833
|
+
* and a ring around the whole frame would sit still while focus moved inside
|
|
834
|
+
* it.
|
|
835
|
+
*/
|
|
836
|
+
[data-ui='multi-select'] > :is([data-part='clear'], [data-part='trigger']):focus-visible {
|
|
837
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
838
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/* ── The layer ────────────────────────────────────────────────────────── */
|
|
842
|
+
|
|
843
|
+
[data-ui='multi-select'][data-part='positioner'] {
|
|
844
|
+
--_surface: var(--slotted-multi-select-surface, var(--slotted-surface-raised, #ffffff));
|
|
845
|
+
--_text: var(--slotted-multi-select-content-text, var(--slotted-text, #0f172a));
|
|
846
|
+
--_muted: var(--slotted-multi-select-content-text-muted, var(--slotted-text-muted, #5b6b7f));
|
|
847
|
+
--_border-color: var(
|
|
848
|
+
--slotted-multi-select-content-border-color,
|
|
849
|
+
var(--slotted-border-subtle, #e2e8f0)
|
|
850
|
+
);
|
|
851
|
+
--_border-width: var(--slotted-border-width-thin, 1px);
|
|
852
|
+
--_radius: var(--slotted-multi-select-content-radius, var(--slotted-radius-lg, 8px));
|
|
853
|
+
--_shadow: var(--slotted-multi-select-content-shadow, var(--slotted-shadow-md));
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* The stronger step of the neutral tone, for the reason `select.css` writes
|
|
857
|
+
* out in full: in the dark scheme `--slotted-tone-secondary-subtle-hover`
|
|
858
|
+
* is `#18181b` where the raised popup is `#1c1c20`, so the usual highlight
|
|
859
|
+
* resolves *darker* than its own ground and reads as a hole. The `-active`
|
|
860
|
+
* step is the only member of the pair that lifts in all three schemes.
|
|
861
|
+
*/
|
|
862
|
+
--_highlight: var(
|
|
863
|
+
--slotted-multi-select-highlight,
|
|
864
|
+
var(--slotted-tone-secondary-subtle-active)
|
|
865
|
+
);
|
|
866
|
+
--_highlight-active: var(
|
|
867
|
+
--slotted-multi-select-highlight-active,
|
|
868
|
+
var(--slotted-tone-accent-surface)
|
|
869
|
+
);
|
|
870
|
+
--_selected-color: var(--slotted-multi-select-selected-color, var(--slotted-tone-accent-text));
|
|
871
|
+
|
|
872
|
+
--_inset-block: var(
|
|
873
|
+
--slotted-multi-select-content-padding-block,
|
|
874
|
+
var(--slotted-space-2, 0.25rem)
|
|
875
|
+
);
|
|
876
|
+
--_inset-inline: var(
|
|
877
|
+
--slotted-multi-select-content-padding-inline,
|
|
878
|
+
var(--slotted-space-2, 0.25rem)
|
|
879
|
+
);
|
|
880
|
+
--_item-inset: var(--slotted-control-padding-inline-md, 0.8125rem);
|
|
881
|
+
--_item-height: var(--slotted-control-height-md, 34px);
|
|
882
|
+
--_item-radius: var(--slotted-multi-select-item-radius, var(--slotted-control-radius));
|
|
883
|
+
--_content-font-size: var(--slotted-control-font-size-md, 13px);
|
|
884
|
+
--_secondary-font-size: var(--slotted-control-font-size-sm, 12px);
|
|
885
|
+
--_content-icon-size: var(--slotted-control-icon-size-md, 16px);
|
|
886
|
+
--_column-gap: var(--slotted-control-gap-md, 0.5rem);
|
|
887
|
+
|
|
888
|
+
--_measure: var(--slotted-multi-select-measure, 24rem);
|
|
889
|
+
|
|
890
|
+
/* Only the block inset: the positioner writes `left` and never `right`, and
|
|
891
|
+
`inset-inline-start` would resolve to `right` in a right-to-left document
|
|
892
|
+
and win over the measured value. */
|
|
893
|
+
position: absolute;
|
|
894
|
+
inset-block-start: 0;
|
|
895
|
+
|
|
896
|
+
z-index: calc(var(--slotted-z-index-popover, 1100) + var(--slotted-layer-depth, 0));
|
|
897
|
+
isolation: isolate;
|
|
898
|
+
|
|
899
|
+
box-sizing: border-box;
|
|
900
|
+
pointer-events: none;
|
|
901
|
+
|
|
902
|
+
opacity: 0;
|
|
903
|
+
/**
|
|
904
|
+
* The layer arrives along the axis it opens on — from one space step above
|
|
905
|
+
* its place when it opens downward, from one step below when it opens
|
|
906
|
+
* upward — and never along the inline axis. A `scale` from the corner the
|
|
907
|
+
* positioner named as origin moved the far edge by four per cent of the
|
|
908
|
+
* width, which read as an entrance from the left.
|
|
909
|
+
*/
|
|
910
|
+
--_enter-distance: var(--slotted-space-2, 0.25rem);
|
|
911
|
+
translate: 0 calc(-1 * var(--_enter-distance));
|
|
912
|
+
transition:
|
|
913
|
+
opacity var(--slotted-duration-short) var(--slotted-easing-exit),
|
|
914
|
+
translate var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
[data-ui='multi-select'][data-part='positioner'][data-side='top'] {
|
|
918
|
+
translate: 0 var(--_enter-distance);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/* A lateral side has no block direction to arrive from, so it only fades. */
|
|
922
|
+
[data-ui='multi-select'][data-part='positioner']:is([data-side='left'], [data-side='right']) {
|
|
923
|
+
translate: 0 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* The entrance is a keyframe animation and not a transition. A transition
|
|
928
|
+
* starts from the style the browser computed last, and the positioner writes
|
|
929
|
+
* `data-side` in the same style update as the family opens the layer, so a
|
|
930
|
+
* transition set out from the closed style of the side the layer had before
|
|
931
|
+
* it had one. A keyframe states where it starts. The exit stays a transition:
|
|
932
|
+
* it leaves from wherever the layer is, on the closed rule's durations.
|
|
933
|
+
*/
|
|
934
|
+
[data-ui='multi-select'][data-part='positioner'][data-open] {
|
|
935
|
+
opacity: 1;
|
|
936
|
+
translate: 0 0;
|
|
937
|
+
transition: none;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Only a layer the positioner placed arrives: it writes `data-positioned`
|
|
942
|
+
* with the side, and a layer written open in static markup — the harness, a
|
|
943
|
+
* docs snippet — is simply there, and is photographed where it stands.
|
|
944
|
+
*/
|
|
945
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned] {
|
|
946
|
+
animation: slotted-multi-select-enter-down var(--slotted-duration-medium)
|
|
947
|
+
var(--slotted-easing-enter);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned][data-side='top'] {
|
|
951
|
+
animation-name: slotted-multi-select-enter-up;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned]:is(
|
|
955
|
+
[data-side='left'],
|
|
956
|
+
[data-side='right']
|
|
957
|
+
) {
|
|
958
|
+
animation-name: slotted-multi-select-fade;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
@keyframes slotted-multi-select-enter-down {
|
|
962
|
+
from {
|
|
963
|
+
opacity: 0;
|
|
964
|
+
translate: 0 calc(-1 * var(--_enter-distance));
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
@keyframes slotted-multi-select-enter-up {
|
|
969
|
+
from {
|
|
970
|
+
opacity: 0;
|
|
971
|
+
translate: 0 var(--_enter-distance);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
@keyframes slotted-multi-select-fade {
|
|
976
|
+
from {
|
|
977
|
+
opacity: 0;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/* Stillness keeps the fade and drops the movement. */
|
|
982
|
+
@media (prefers-reduced-motion: reduce) {
|
|
983
|
+
[data-ui='multi-select'][data-part='positioner'],
|
|
984
|
+
[data-ui='multi-select'][data-part='positioner'][data-side='top'],
|
|
985
|
+
[data-ui='multi-select'][data-part='positioner'][data-side='left'],
|
|
986
|
+
[data-ui='multi-select'][data-part='positioner'][data-side='right'] {
|
|
987
|
+
translate: 0 0;
|
|
988
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned],
|
|
992
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned][data-side='top'],
|
|
993
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned][data-side='left'],
|
|
994
|
+
[data-ui='multi-select'][data-part='positioner'][data-open][data-positioned][data-side='right'] {
|
|
995
|
+
animation-name: slotted-multi-select-fade;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
[data-ui='multi-select'][data-part='positioner'][data-detached] {
|
|
1000
|
+
visibility: hidden;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
[data-ui='multi-select'][data-part='positioner'][data-width='anchor'] {
|
|
1004
|
+
inline-size: var(--slotted-positioner-anchor-width, auto);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
[data-ui='multi-select'][data-part='positioner'][data-width='content'] {
|
|
1008
|
+
inline-size: max-content;
|
|
1009
|
+
max-inline-size: min(var(--_measure), var(--slotted-positioner-available-width, 100vw));
|
|
1010
|
+
min-inline-size: var(--slotted-positioner-anchor-width, auto);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
[data-ui='multi-select'][data-part='positioner'][data-size='sm'] {
|
|
1014
|
+
--_item-inset: var(--slotted-control-padding-inline-sm, 0.625rem);
|
|
1015
|
+
--_item-height: var(--slotted-control-height-sm, 30px);
|
|
1016
|
+
--_content-font-size: var(--slotted-control-font-size-sm, 12px);
|
|
1017
|
+
--_secondary-font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
1018
|
+
--_content-icon-size: var(--slotted-control-icon-size-sm, 14px);
|
|
1019
|
+
--_column-gap: var(--slotted-control-gap-sm, 0.375rem);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
[data-ui='multi-select'][data-part='positioner'][data-size='lg'] {
|
|
1023
|
+
--_item-inset: var(--slotted-control-padding-inline-lg, 1rem);
|
|
1024
|
+
--_item-height: var(--slotted-control-height-lg, 38px);
|
|
1025
|
+
--_content-font-size: var(--slotted-control-font-size-lg, 14px);
|
|
1026
|
+
--_secondary-font-size: var(--slotted-control-font-size-sm, 12px);
|
|
1027
|
+
--_content-icon-size: var(--slotted-control-icon-size-lg, 18px);
|
|
1028
|
+
--_column-gap: var(--slotted-control-gap-lg, 0.5rem);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* ── The surface ──────────────────────────────────────────────────────── */
|
|
1032
|
+
|
|
1033
|
+
[data-ui='multi-select'] > [data-part='content'] {
|
|
1034
|
+
background: var(--_surface);
|
|
1035
|
+
border: var(--_border-width) solid var(--_border-color);
|
|
1036
|
+
border-radius: var(--_radius);
|
|
1037
|
+
box-shadow: var(--_shadow);
|
|
1038
|
+
box-sizing: border-box;
|
|
1039
|
+
color: var(--_text);
|
|
1040
|
+
display: grid;
|
|
1041
|
+
font-family: var(--slotted-font-family-sans);
|
|
1042
|
+
font-size: var(--_content-font-size);
|
|
1043
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
1044
|
+
max-block-size: calc(
|
|
1045
|
+
var(--slotted-positioner-available-height, 100vh) - var(--slotted-space-5, 0.75rem)
|
|
1046
|
+
);
|
|
1047
|
+
outline: none;
|
|
1048
|
+
overflow-y: auto;
|
|
1049
|
+
overscroll-behavior: contain;
|
|
1050
|
+
padding-block: var(--_inset-block);
|
|
1051
|
+
padding-inline: var(--_inset-inline);
|
|
1052
|
+
pointer-events: auto;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* The surface never takes a ring. Focus stays on the control for the whole
|
|
1057
|
+
* interaction — the highlight is `aria-activedescendant`, not focus — so a
|
|
1058
|
+
* ring here would draw a box around something nobody is operating.
|
|
1059
|
+
*/
|
|
1060
|
+
[data-ui='multi-select'] > [data-part='content']:focus-visible {
|
|
1061
|
+
outline: none;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Every chosen value, whatever the row in the control did with them. It is
|
|
1066
|
+
* what makes `overflow='collapse'` legal against the feature guide: the `+N`
|
|
1067
|
+
* chip in the control summarises, and this band is where the whole set stays
|
|
1068
|
+
* reachable. It is drawn only while something is chosen.
|
|
1069
|
+
*
|
|
1070
|
+
* Its chips carry no remove control, and the reason is keyboard reach rather
|
|
1071
|
+
* than tidiness: focus never enters this layer, so a control in here would be
|
|
1072
|
+
* reachable by pointer and by nothing else. Taking a value back is the
|
|
1073
|
+
* option's own row, which the list has open beside it, and the chips in the
|
|
1074
|
+
* control, which are focusable. `select` recorded the same decision.
|
|
1075
|
+
*/
|
|
1076
|
+
[data-ui='multi-select'] [data-part='chosen'] {
|
|
1077
|
+
background: var(--_surface);
|
|
1078
|
+
border-block-end: var(--_border-width) solid var(--_border-color);
|
|
1079
|
+
box-sizing: border-box;
|
|
1080
|
+
display: flex;
|
|
1081
|
+
flex-wrap: wrap;
|
|
1082
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
1083
|
+
grid-column: 1 / -1;
|
|
1084
|
+
/* Its own padding stands where the surface's would, so nothing shows above
|
|
1085
|
+
it as the list scrolls under. */
|
|
1086
|
+
inset-block-start: calc(-1 * var(--_inset-block));
|
|
1087
|
+
margin-block: calc(-1 * var(--_inset-block)) var(--_inset-block);
|
|
1088
|
+
margin-inline: calc(-1 * var(--_inset-inline));
|
|
1089
|
+
/* Two whole rows of chips, the gap between them, and the air above the
|
|
1090
|
+
first. What says there is more is the scrollbar and the rule under it. */
|
|
1091
|
+
max-block-size: calc(
|
|
1092
|
+
2 * var(--slotted-target-min, 24px) + var(--slotted-space-2, 0.25rem) + var(--_inset-block)
|
|
1093
|
+
);
|
|
1094
|
+
min-inline-size: 0;
|
|
1095
|
+
overflow-y: auto;
|
|
1096
|
+
overscroll-behavior: contain;
|
|
1097
|
+
padding-block: var(--_inset-block);
|
|
1098
|
+
padding-inline: var(--_inset-inline);
|
|
1099
|
+
position: sticky;
|
|
1100
|
+
z-index: 1;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/* ── Groups ───────────────────────────────────────────────────────────── */
|
|
1104
|
+
|
|
1105
|
+
[data-ui='multi-select'] [data-part='group'] {
|
|
1106
|
+
display: grid;
|
|
1107
|
+
grid-column: 1 / -1;
|
|
1108
|
+
grid-template-columns: subgrid;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* An option the query rejected, and a group whose options all went. Both
|
|
1113
|
+
* carry `hidden` — Angular hides each rejected option rather than leaving the
|
|
1114
|
+
* list, and both bindings hide an emptied group — and `display: grid` on
|
|
1115
|
+
* either beats the user agent's own `[hidden] { display: none }`, because an
|
|
1116
|
+
* author rule wins over the user agent sheet. Without this the search
|
|
1117
|
+
* filtered nothing the reader could see.
|
|
1118
|
+
*/
|
|
1119
|
+
[data-ui='multi-select'] [data-part='item'][hidden],
|
|
1120
|
+
[data-ui='multi-select'] [data-part='group'][hidden] {
|
|
1121
|
+
display: none;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
[data-ui='multi-select'] [data-part='group-label'] {
|
|
1125
|
+
color: var(--_muted);
|
|
1126
|
+
font-size: var(--_secondary-font-size);
|
|
1127
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
1128
|
+
grid-column: 1 / -1;
|
|
1129
|
+
line-height: var(--slotted-line-height-sm, 1.45);
|
|
1130
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
1131
|
+
padding-inline: var(--_item-inset);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/* ── Options ──────────────────────────────────────────────────────────── */
|
|
1135
|
+
|
|
1136
|
+
[data-ui='multi-select'] [data-part='item'] {
|
|
1137
|
+
align-content: center;
|
|
1138
|
+
border-radius: var(--_item-radius);
|
|
1139
|
+
box-sizing: border-box;
|
|
1140
|
+
/* A row that adds a value to the set is a press, and this popup is not a
|
|
1141
|
+
platform menu: `select` and `combobox` draw the same cursor on the same
|
|
1142
|
+
row, and the three say one thing about one gesture. */
|
|
1143
|
+
cursor: pointer;
|
|
1144
|
+
display: grid;
|
|
1145
|
+
grid-column: 1 / -1;
|
|
1146
|
+
grid-template-columns: subgrid;
|
|
1147
|
+
line-height: var(--slotted-line-height-md, 1.5);
|
|
1148
|
+
min-block-size: var(--_item-height);
|
|
1149
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
1150
|
+
padding-inline: var(--_item-inset);
|
|
1151
|
+
row-gap: var(--slotted-space-1, 0.125rem);
|
|
1152
|
+
user-select: none;
|
|
1153
|
+
transition:
|
|
1154
|
+
background-color var(--slotted-control-transition-duration, var(--slotted-duration-short))
|
|
1155
|
+
var(--slotted-control-transition-easing, var(--slotted-easing-standard)),
|
|
1156
|
+
color var(--slotted-control-transition-duration, var(--slotted-duration-short))
|
|
1157
|
+
var(--slotted-control-transition-easing, var(--slotted-easing-standard));
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
[data-ui='multi-select'] [data-part='item'][data-current] {
|
|
1161
|
+
background-color: var(--_highlight);
|
|
1162
|
+
/**
|
|
1163
|
+
* A ring as well as a ground. Nothing on this surface holds focus — DOM
|
|
1164
|
+
* focus is on the input for the whole interaction — so there is no focus
|
|
1165
|
+
* ring anywhere to say where the reader is, and the APG's own select-only
|
|
1166
|
+
* example marks its current option with an outline for that reason. An
|
|
1167
|
+
* inset shadow rather than an outline, so it follows the option's own
|
|
1168
|
+
* radius and never overflows the surface's rounded edge.
|
|
1169
|
+
*/
|
|
1170
|
+
box-shadow: inset 0 0 0 var(--slotted-border-width-thin, 1px)
|
|
1171
|
+
var(--slotted-multi-select-highlight-ring, var(--slotted-tone-accent-border));
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
[data-ui='multi-select'] [data-part='item']:not([data-disabled]):active {
|
|
1175
|
+
background-color: var(--_highlight-active);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
[data-ui='multi-select'] [data-part='item'][data-selected] {
|
|
1179
|
+
color: var(--_selected-color);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Two chosen neighbours are one block. Every item keeps its own radius, so
|
|
1184
|
+
* two selected siblings painted with the same ground met at a waist — four
|
|
1185
|
+
* rounded corners inside the block — and a multiple selection read as a
|
|
1186
|
+
* column of separate chips. The second gives up its top corners and the first
|
|
1187
|
+
* its bottom ones, so the block is rounded only where the selection begins
|
|
1188
|
+
* and ends. `select` and `combobox` answer the same way, and
|
|
1189
|
+
* `selection-block.test.mjs` holds all of them to it.
|
|
1190
|
+
*/
|
|
1191
|
+
[data-ui='multi-select'] [data-part='item'][data-selected] + [data-part='item'][data-selected] {
|
|
1192
|
+
border-start-end-radius: 0;
|
|
1193
|
+
border-start-start-radius: 0;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
[data-ui='multi-select']
|
|
1197
|
+
[data-part='item'][data-selected]:has(+ [data-part='item'][data-selected]) {
|
|
1198
|
+
border-end-end-radius: 0;
|
|
1199
|
+
border-end-start-radius: 0;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
[data-ui='multi-select'] [data-part='item'][data-selected] > [data-part='label'] {
|
|
1203
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
[data-ui='multi-select'] [data-part='item'][data-disabled] {
|
|
1207
|
+
color: var(--slotted-disabled-foreground, #64748b);
|
|
1208
|
+
cursor: not-allowed;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* The highlight can land on an option nobody can choose — a disabled one, or
|
|
1213
|
+
* any unchosen one while the control is full — because it keeps its place in
|
|
1214
|
+
* the collection and the pattern requires it to be announced.
|
|
1215
|
+
*
|
|
1216
|
+
* **The ground stays the highlight's.** Painting the theme's disabled surface
|
|
1217
|
+
* here was the first answer and it is wrong in the dark scheme, for the same
|
|
1218
|
+
* reason the highlight itself was: `--slotted-disabled-background` is
|
|
1219
|
+
* `#18181b` where the raised popup is `#1c1c20`, so the cursor would *sink*
|
|
1220
|
+
* on a disabled row and rise on every other one. A cursor lifts, always. What
|
|
1221
|
+
* the row cannot do is said by the ink and by the ring, which both step down
|
|
1222
|
+
* to the disabled pair.
|
|
1223
|
+
*/
|
|
1224
|
+
[data-ui='multi-select'] [data-part='item'][data-disabled][data-current] {
|
|
1225
|
+
box-shadow: inset 0 0 0 var(--slotted-border-width-thin, 1px)
|
|
1226
|
+
var(--slotted-disabled-border, #cbd5e1);
|
|
1227
|
+
color: var(--slotted-disabled-foreground, #64748b);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/* ── The cells ────────────────────────────────────────────────────────── */
|
|
1231
|
+
|
|
1232
|
+
[data-ui='multi-select'] [data-part='item'] > :is([data-part='indicator'], [data-part='icon']) {
|
|
1233
|
+
block-size: 1lh;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/* Column one holds content and paints none: a `Glyph` in an `Icon` box, or
|
|
1237
|
+
artwork of the consumer's own, centred and sized from the column. */
|
|
1238
|
+
[data-ui='multi-select'] [data-part='item'] > [data-part='icon'] {
|
|
1239
|
+
--slotted-icon-size: var(--_content-icon-size);
|
|
1240
|
+
|
|
1241
|
+
align-items: center;
|
|
1242
|
+
display: flex;
|
|
1243
|
+
grid-column: 1;
|
|
1244
|
+
grid-row: 1;
|
|
1245
|
+
inline-size: var(--_content-icon-size);
|
|
1246
|
+
justify-content: center;
|
|
1247
|
+
justify-self: center;
|
|
1248
|
+
margin-inline-end: var(--_column-gap);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
[data-ui='multi-select'] [data-part='item'] > [data-part='label'] {
|
|
1252
|
+
grid-column: 2;
|
|
1253
|
+
grid-row: 1;
|
|
1254
|
+
min-inline-size: 0;
|
|
1255
|
+
display: -webkit-box;
|
|
1256
|
+
-webkit-box-orient: vertical;
|
|
1257
|
+
-webkit-line-clamp: 2;
|
|
1258
|
+
overflow: hidden;
|
|
1259
|
+
overflow-wrap: anywhere;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
[data-ui='multi-select'] [data-part='item'] > [data-part='description'] {
|
|
1263
|
+
color: var(--_muted);
|
|
1264
|
+
font-size: var(--_secondary-font-size);
|
|
1265
|
+
grid-column: 2;
|
|
1266
|
+
grid-row: 2;
|
|
1267
|
+
line-height: var(--slotted-line-height-sm, 1.45);
|
|
1268
|
+
min-inline-size: 0;
|
|
1269
|
+
overflow-wrap: anywhere;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
[data-ui='multi-select'] [data-part='item'][data-disabled] > [data-part='description'] {
|
|
1273
|
+
color: inherit;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/* The tick is a `Glyph` the binding draws, at the content's icon step. */
|
|
1277
|
+
[data-ui='multi-select'] [data-part='item'] > [data-part='indicator'] {
|
|
1278
|
+
--slotted-icon-size: var(--_content-icon-size);
|
|
1279
|
+
|
|
1280
|
+
align-items: center;
|
|
1281
|
+
display: flex;
|
|
1282
|
+
grid-column: 3;
|
|
1283
|
+
grid-row: 1;
|
|
1284
|
+
inline-size: var(--_content-icon-size);
|
|
1285
|
+
justify-content: center;
|
|
1286
|
+
justify-self: end;
|
|
1287
|
+
margin-inline-start: var(--_column-gap);
|
|
1288
|
+
opacity: 0;
|
|
1289
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-standard);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
[data-ui='multi-select'] [data-part='item'][data-selected] > [data-part='indicator'] {
|
|
1293
|
+
opacity: 1;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* The offer to add what was typed. It is an option — the same part, the same
|
|
1298
|
+
* columns, the same keys — told apart by `data-create`, and its mark is the
|
|
1299
|
+
* plus rather than the check: the check says "this is in the set" and nothing
|
|
1300
|
+
* is in the set yet. The binding draws the plus; the rule only shows it.
|
|
1301
|
+
*/
|
|
1302
|
+
[data-ui='multi-select'] [data-part='item'][data-create] > [data-part='indicator'] {
|
|
1303
|
+
opacity: 1;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* The offer's label is the family's own sentence with the reader's query
|
|
1308
|
+
* quoted inside it, which is why it takes its own bidi run where an option's
|
|
1309
|
+
* label — the consumer's content — does not. `Add “berr”` in a right-to-left
|
|
1310
|
+
* document put the opening quote after the closing one, because the quotes
|
|
1311
|
+
* are neutral characters and resolved to the paragraph rather than to the
|
|
1312
|
+
* Latin word between them. The repair `status`, `empty` and the counter all
|
|
1313
|
+
* make, for the same reason.
|
|
1314
|
+
*/
|
|
1315
|
+
[data-ui='multi-select'] [data-part='item'][data-create] > [data-part='label'] {
|
|
1316
|
+
unicode-bidi: plaintext;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/* ── Separator, empty, status ─────────────────────────────────────────── */
|
|
1320
|
+
|
|
1321
|
+
[data-ui='multi-select'] [data-part='separator'] {
|
|
1322
|
+
background-color: var(--slotted-multi-select-separator-color, var(--_border-color));
|
|
1323
|
+
block-size: var(--_border-width);
|
|
1324
|
+
border: 0;
|
|
1325
|
+
grid-column: 1 / -1;
|
|
1326
|
+
margin-block: var(--slotted-space-2, 0.25rem);
|
|
1327
|
+
margin-inline: 0;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* A query with no answers, and a query whose answer is still travelling. Both
|
|
1332
|
+
* keep the surface's measure rather than collapsing, so nothing jumps when
|
|
1333
|
+
* the results land.
|
|
1334
|
+
*/
|
|
1335
|
+
[data-ui='multi-select'] :is([data-part='empty'], [data-part='status']) {
|
|
1336
|
+
color: var(--_muted);
|
|
1337
|
+
/* Its own bidi run, ordered by its own content: "3 results" is a European
|
|
1338
|
+
number, a neutral space and a Latin word, and in a right-to-left document
|
|
1339
|
+
the space resolves to the paragraph and the two runs swap. */
|
|
1340
|
+
unicode-bidi: plaintext;
|
|
1341
|
+
grid-column: 1 / -1;
|
|
1342
|
+
/**
|
|
1343
|
+
* Wide enough that a one-word message does not draw a popup narrower than a
|
|
1344
|
+
* word, and never wider than the popup it is in — as two declarations
|
|
1345
|
+
* rather than as one `min()`.
|
|
1346
|
+
*
|
|
1347
|
+
* `min-inline-size: min(12rem, 100%)` was the first answer and it breaks on
|
|
1348
|
+
* a popup sized to its content. A percentage resolves against an indefinite
|
|
1349
|
+
* width as nothing while the browser is computing that width, so the `min()`
|
|
1350
|
+
* collapsed to zero, the surface sized itself to the widest thing left — the
|
|
1351
|
+
* three waiting bars — and the message then took its twelve rems inside a
|
|
1352
|
+
* box narrower than that and hung out of both sides of it, centred. Stating
|
|
1353
|
+
* the floor and the ceiling separately means the floor is counted while the
|
|
1354
|
+
* surface is being measured and the ceiling is applied once it has a width.
|
|
1355
|
+
*
|
|
1356
|
+
* The box is a border box so the ceiling counts the message's own gutters;
|
|
1357
|
+
* against a content box `100%` would let the padding push it out again.
|
|
1358
|
+
*/
|
|
1359
|
+
box-sizing: border-box;
|
|
1360
|
+
min-inline-size: 12rem;
|
|
1361
|
+
max-inline-size: 100%;
|
|
1362
|
+
padding-block: var(--slotted-space-5, 0.75rem);
|
|
1363
|
+
padding-inline: var(--_item-inset);
|
|
1364
|
+
text-align: center;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* The status row is also the live region that announces the count, so it is
|
|
1369
|
+
* often present with nothing to draw. A row with no text takes no space.
|
|
1370
|
+
*/
|
|
1371
|
+
[data-ui='multi-select'] [data-part='status']:empty {
|
|
1372
|
+
display: none;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* The wait, as the shape of the list that is coming: rows the height of an
|
|
1377
|
+
* option rather than one sentence, so the surface a reader is looking at
|
|
1378
|
+
* while the answer travels is the surface they will go on looking at. The
|
|
1379
|
+
* bars are `skeleton/skeleton.css`'s, and they are hidden from assistive
|
|
1380
|
+
* technology because the `status` row beside them is the live region.
|
|
1381
|
+
*/
|
|
1382
|
+
[data-ui='multi-select'] [data-part='placeholder'] {
|
|
1383
|
+
display: grid;
|
|
1384
|
+
grid-column: 1 / -1;
|
|
1385
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
1386
|
+
padding-inline: var(--_item-inset);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
[data-ui='multi-select'] [data-part='placeholder'] > * {
|
|
1390
|
+
align-items: center;
|
|
1391
|
+
display: flex;
|
|
1392
|
+
min-block-size: var(--_item-height);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
[data-ui='multi-select'] [data-part='placeholder'] > [data-ui='skeleton'] {
|
|
1396
|
+
align-self: center;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/* Three ragged widths: three identical bars read as a table, and a list of
|
|
1400
|
+
labels is ragged. */
|
|
1401
|
+
[data-ui='multi-select'] [data-part='placeholder'] > :nth-child(2) [data-ui='skeleton'] {
|
|
1402
|
+
inline-size: 78%;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
[data-ui='multi-select'] [data-part='placeholder'] > :nth-child(3) [data-ui='skeleton'] {
|
|
1406
|
+
inline-size: 56%;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
[data-ui='multi-select'] > [data-part='content'][data-loading] {
|
|
1410
|
+
cursor: progress;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/* ── Forced colours ───────────────────────────────────────────────────── */
|
|
1414
|
+
|
|
1415
|
+
@media (forced-colors: active) {
|
|
1416
|
+
[data-ui='multi-select'] [data-part='item'][data-current] {
|
|
1417
|
+
background-color: Highlight;
|
|
1418
|
+
color: HighlightText;
|
|
1419
|
+
forced-color-adjust: none;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
[data-ui='multi-select'] [data-part='item'][data-disabled][data-current] {
|
|
1423
|
+
background-color: Highlight;
|
|
1424
|
+
color: GrayText;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|