@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,1306 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A bar above a listing that edits one value: a tree of conditions over the
4
+ * listing's fields, whose leaves are the descriptor `table` edits in a
5
+ * column header. The bar draws the tree's first level — a search field, a
6
+ * pill per pinned field still waiting for a condition, a chip per condition
7
+ * — and opens a drawer on the whole tree.
8
+ *
9
+ * Two tab stops however many fields a product declares: the search, and the
10
+ * toolbar that holds everything else. The root is a `search` landmark and
11
+ * not a toolbar, because the search field needs the arrow keys a toolbar
12
+ * takes.
13
+ *
14
+ * Every control reads the control scale — height, inline padding, glyph,
15
+ * gap — so a theme that retunes a text field retunes this bar with it, and
16
+ * the three densities move every control together.
17
+ */
18
+ [data-ui='filter-bar'] {
19
+ --_gap: var(--slotted-filter-bar-gap, var(--slotted-control-gap-md, 8px));
20
+ --_gap-sm: var(--slotted-control-gap-sm, 6px);
21
+ --_height: var(--slotted-control-height-md, 34px);
22
+ --_font-size: var(--slotted-control-font-size-md, 13px);
23
+ --_pad: var(--slotted-control-padding-inline-md, 13px);
24
+ /* A chip segment sits between two others, so it takes the smaller inset of the scale. */
25
+ --_seg-pad: var(--slotted-control-padding-inline-sm, 10px);
26
+ --_icon: var(--slotted-control-icon-size-md, 16px);
27
+ --_radius: var(--slotted-filter-bar-pill-radius, var(--slotted-radius-full, 9999px));
28
+ --_control-radius: var(--slotted-control-radius, var(--slotted-radius-sm, 4px));
29
+ --_border-width: var(--slotted-control-border-width, 1px);
30
+ --_ink: var(--slotted-filter-bar-color, var(--slotted-text, CanvasText));
31
+ --_muted: var(--slotted-filter-bar-color-muted, var(--slotted-text-muted, GrayText));
32
+ --_edge: var(
33
+ --slotted-filter-bar-pill-border,
34
+ var(--slotted-control-border-color, ButtonBorder)
35
+ );
36
+ /* The line between two segments of a chip: the chip's own edge carries the outline, the seam only divides. */
37
+ --_seam: var(--slotted-filter-bar-chip-seam, var(--slotted-border-subtle, ButtonBorder));
38
+ --_surface: var(--slotted-filter-bar-surface, var(--slotted-surface, Canvas));
39
+ --_hover: var(--slotted-tone-secondary-subtle-hover, ButtonFace);
40
+ --_active: var(--slotted-tone-secondary-subtle-active, ButtonFace);
41
+ --_invalid: var(--slotted-tone-danger-border, GrayText);
42
+ /* Width as well as colour (WCAG 1.4.1): in `high-contrast` the danger edge is the resting edge's black, so every invalid box adds the edge `text-field` draws, inside its border, where nothing moves. */
43
+ --_invalid-edge-width: var(--slotted-control-invalid-edge-width, 1px);
44
+ --_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid);
45
+ --_duration: var(--slotted-control-transition-duration, var(--slotted-duration-short));
46
+ --_easing: var(--slotted-control-transition-easing, var(--slotted-easing-standard));
47
+
48
+ box-sizing: border-box;
49
+ color: var(--_ink);
50
+ font-family: var(--slotted-filter-bar-font-family, var(--slotted-font-family-sans, inherit));
51
+ font-size: var(--_font-size);
52
+ line-height: var(--slotted-line-height-md, 1.5);
53
+ min-inline-size: 0;
54
+ }
55
+
56
+ /**
57
+ * The builder is a part that leaves the root: it renders inside a drawer,
58
+ * which `portal` moves to the end of the document, so it carries `data-ui`
59
+ * of its own and the block above gives it the same locals. The value
60
+ * popover a chip opens does the same.
61
+ *
62
+ * The root is the query container and the frame is the grid, because a
63
+ * container query cannot restyle the element it measures — the split
64
+ * `stepper` and `description-list` already carry, recorded in the briefing.
65
+ *
66
+ * A bar spans the listing it filters, so its inline size is its parent's:
67
+ * inline-size containment gives a container no intrinsic width, and a
68
+ * start-justified grid or flex parent measured the root at zero pixels and
69
+ * drew every pill and chip outside it.
70
+ */
71
+ [data-ui='filter-bar'][data-part='root'] {
72
+ container-name: slotted-filter-bar;
73
+ container-type: inline-size;
74
+ display: block;
75
+ inline-size: 100%;
76
+ }
77
+
78
+ /**
79
+ * The editor a pill or a chip's value opens: one rule alone in a popover,
80
+ * so it carries the family's `data-ui` like the builder does. A column — the
81
+ * operator when a pill opened it, the operand or the pair, then Clear and
82
+ * Done at the end — at a width of its own, because a popover shrink-wraps
83
+ * and a select shrink-wrapped to its value is a control that changes width
84
+ * when the value does.
85
+ */
86
+ [data-ui='filter-bar'][data-part='rule'] {
87
+ display: grid;
88
+ gap: var(--_gap);
89
+ inline-size: var(--slotted-filter-bar-editor-size, 18rem);
90
+ max-inline-size: 100%;
91
+ }
92
+
93
+ /*
94
+ * The operator and the operand are reached as descendants and not as
95
+ * children: a binding may render the condition inside one element of its
96
+ * own at `display: contents`, which leaves the two parts as grid items of
97
+ * the rule and out of reach of a child combinator.
98
+ */
99
+ [data-ui='filter-bar'][data-part='rule'] [data-part='operand'] {
100
+ display: grid;
101
+ gap: var(--_gap-sm);
102
+ min-inline-size: 0;
103
+ }
104
+
105
+ [data-ui='filter-bar'][data-part='rule'][data-kind='boolean'] [data-part='operand']:empty {
106
+ display: none;
107
+ }
108
+
109
+ [data-ui='filter-bar'][data-part='rule'] > [data-part='actions'] {
110
+ align-items: center;
111
+ display: flex;
112
+ gap: var(--_gap-sm);
113
+ justify-content: flex-end;
114
+ }
115
+
116
+ /* ── Composition ──────────────────────────────────────────────────────
117
+ *
118
+ * Two lines at every width from 36rem: the search, the consumer's controls
119
+ * and the count, then the toolbar, which wraps. The toolbar holds the chips,
120
+ * so it grows with the filter and has a line of its own to grow in; a
121
+ * composition that put it beside the search at a wide width had to measure
122
+ * when it wrapped, and fell back to this one whenever it did.
123
+ *
124
+ * Under 36rem: the search alone, then one control that says how many
125
+ * conditions are applied beside the consumer's controls, then the count.
126
+ * The pills and chips fold behind that control, which opens the builder. The
127
+ * line sits at 36rem and not at 40rem because a reference page's stage is
128
+ * 39rem wide: at 40rem every demonstration on the page showed the fold and
129
+ * none showed a chip.
130
+ *
131
+ * Every member of the first line starts at its top and is a control tall,
132
+ * so the count centres its text against the search whatever wraps.
133
+ */
134
+ [data-ui='filter-bar'] > [data-part='frame'] {
135
+ align-items: start;
136
+ column-gap: var(--_gap);
137
+ display: grid;
138
+ grid-template-areas:
139
+ 'search end count'
140
+ 'bar bar bar';
141
+ grid-template-columns: minmax(0, 1fr) auto auto;
142
+ row-gap: var(--_gap);
143
+ }
144
+
145
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='search'] {
146
+ grid-area: search;
147
+ max-inline-size: var(--slotted-filter-bar-search-max, 28rem);
148
+ min-inline-size: 0;
149
+ }
150
+
151
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='bar'] {
152
+ grid-area: bar;
153
+ }
154
+
155
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='end'] {
156
+ grid-area: end;
157
+ }
158
+
159
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='count'] {
160
+ grid-area: count;
161
+ justify-self: end;
162
+ }
163
+
164
+ /** A member that is absent collapses: the grid's gap is what would remain, and it is taken away. */
165
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='end']:empty {
166
+ display: none;
167
+ }
168
+
169
+ [data-ui='filter-bar'][data-size='sm'] {
170
+ --_height: var(--slotted-control-height-sm, 30px);
171
+ --_font-size: var(--slotted-control-font-size-sm, 12px);
172
+ --_pad: var(--slotted-control-padding-inline-sm, 10px);
173
+ --_seg-pad: var(--slotted-control-gap-md, 8px);
174
+ --_icon: var(--slotted-control-icon-size-sm, 14px);
175
+ --_gap: var(--slotted-filter-bar-gap, var(--slotted-control-gap-sm, 6px));
176
+ --_gap-sm: var(--slotted-control-gap-sm, 4px);
177
+ }
178
+
179
+ [data-ui='filter-bar'][data-size='lg'] {
180
+ --_height: var(--slotted-control-height-lg, 38px);
181
+ --_font-size: var(--slotted-control-font-size-lg, 14px);
182
+ --_pad: var(--slotted-control-padding-inline-lg, 16px);
183
+ --_seg-pad: var(--slotted-control-padding-inline-md, 13px);
184
+ --_icon: var(--slotted-control-icon-size-lg, 18px);
185
+ --_gap: var(--slotted-filter-bar-gap, var(--slotted-control-gap-lg, 8px));
186
+ }
187
+
188
+ /**
189
+ * The search, the preset, a rule's field and operator are other families'
190
+ * roots — `text-field` and `select` select their own root part — so each
191
+ * one sits inside a part of this family's and fills it. The magnifier before
192
+ * the query is an `icon` in the field's start adornment, which the field
193
+ * sizes as it sizes every adornment.
194
+ */
195
+ [data-ui='filter-bar'] [data-part='search'] > [data-ui='text-field'],
196
+ [data-ui='filter-bar'] [data-part='preset'] > [data-ui='select'],
197
+ [data-ui='filter-bar'] :is([data-part='field'], [data-part='operator']) > [data-ui='select'] {
198
+ inline-size: 100%;
199
+ }
200
+
201
+ /**
202
+ * One toolbar, which wraps: the arrows walk its controls in order from line
203
+ * to line, which the toolbar pattern allows for a toolbar of several rows.
204
+ * The row gap is the column gap, so two lines of chips read as one set.
205
+ */
206
+ [data-ui='filter-bar'] [data-part='bar'] {
207
+ align-items: center;
208
+ display: flex;
209
+ flex-wrap: wrap;
210
+ gap: var(--_gap);
211
+ min-inline-size: 0;
212
+ }
213
+
214
+ [data-ui='filter-bar'] [data-part='end'] {
215
+ align-items: center;
216
+ display: flex;
217
+ gap: var(--_gap);
218
+ }
219
+
220
+ /** The count is the answer to the filters, and it takes the far end of its line — PatternFly's rule for an item count. */
221
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='count'] {
222
+ color: var(--_muted);
223
+ font-variant-numeric: tabular-nums;
224
+ margin: 0;
225
+ /* A line of its own type whatever it holds: before the first answer it holds only the
226
+ spinner, shorter than the text that replaces it, and the rows under the bar moved. */
227
+ min-block-size: 1lh;
228
+ /* The line in the middle of a control's height, which is the line it shares with the
229
+ search: the frame starts every member at the top of its line. */
230
+ padding-block: max(0px, calc((var(--_height) - 1lh) / 2));
231
+ white-space: nowrap;
232
+ }
233
+
234
+ /* ── The pill ───────────────────────────────────────────────────────────
235
+ *
236
+ * A pinned field with no condition: a place in the bar waiting to be
237
+ * filled. Its edge is dashed — the edge `upload` gives a file not yet sent,
238
+ * which already means *waiting for something* in this library — and it has
239
+ * no ground and no lift, so it reads as a slot rather than as a control
240
+ * that holds something. The plus before the name says what pressing it
241
+ * does. Given a condition, the same place in the bar becomes a chip.
242
+ */
243
+ [data-ui='filter-bar'] [data-part='pill'] {
244
+ align-items: center;
245
+ appearance: none;
246
+ background-color: transparent;
247
+ block-size: var(--_height);
248
+ border: var(--_border-width) dashed var(--_edge);
249
+ border-radius: var(--_radius);
250
+ box-sizing: border-box;
251
+ color: var(--_muted);
252
+ cursor: pointer;
253
+ display: inline-flex;
254
+ font: inherit;
255
+ font-weight: var(--slotted-font-weight-medium, 500);
256
+ gap: var(--_gap-sm);
257
+ line-height: var(--slotted-control-line-height, 1);
258
+ margin: 0;
259
+ /* The plus carries its own air, so the start inset is one step short. */
260
+ padding-block: 0;
261
+ padding-inline: calc(var(--_pad) - var(--_gap-sm)) var(--_pad);
262
+ transition:
263
+ background-color var(--_duration) var(--_easing),
264
+ border-color var(--_duration) var(--_easing),
265
+ color var(--_duration) var(--_easing),
266
+ opacity var(--slotted-duration-medium) var(--slotted-easing-enter),
267
+ translate var(--slotted-duration-medium) var(--slotted-easing-enter);
268
+ white-space: nowrap;
269
+ }
270
+
271
+ [data-ui='filter-bar'] [data-part='pill'] > span:not([data-ui='icon']) {
272
+ min-inline-size: 0;
273
+ overflow: hidden;
274
+ text-overflow: ellipsis;
275
+ unicode-bidi: isolate;
276
+ }
277
+
278
+ [data-ui='filter-bar'] [data-part='pill'] > [data-ui='icon'] {
279
+ --slotted-icon-size: var(--_icon);
280
+ }
281
+
282
+ [data-ui='filter-bar'] [data-part='pill'][data-invalid] {
283
+ border-color: var(--_invalid);
284
+ box-shadow: var(--_invalid-edge);
285
+ }
286
+
287
+ [data-ui='filter-bar'] [data-part='pill']:enabled:hover {
288
+ background-color: var(--_hover);
289
+ border-color: var(--slotted-border-strong, ButtonBorder);
290
+ color: var(--_ink);
291
+ }
292
+
293
+ [data-ui='filter-bar'] [data-part='pill']:enabled:active,
294
+ [data-ui='filter-bar'] [data-part='pill']:enabled[aria-expanded='true'] {
295
+ background-color: var(--_active);
296
+ border-color: var(--slotted-border-strong, ButtonBorder);
297
+ color: var(--_ink);
298
+ }
299
+
300
+ [data-ui='filter-bar'] [data-part='pill']:focus-visible {
301
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
302
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
303
+ }
304
+
305
+ /* ── The chip ───────────────────────────────────────────────────────────
306
+ *
307
+ * One condition, written as the sentence it is: the field, the operator,
308
+ * the value, the remove control, each a segment of one capsule divided by a
309
+ * seam. Each segment answers one question — the operator opens a menu of
310
+ * operators, the value a popover with the operand — so changing how two
311
+ * values compare no longer reopens the values.
312
+ *
313
+ * The field is read and never pressed: a condition keeps its field for its
314
+ * life. It carries the weight, the operator reads quieter as the connective
315
+ * between two things, and the value is back at the weight of the field.
316
+ *
317
+ * The capsule owns the edge, the ground and the lift, and the segments own
318
+ * hover and press. The first and last segments take the capsule's corners,
319
+ * so a hover ground and a focus ring follow its curve rather than being
320
+ * clipped by it.
321
+ */
322
+ [data-ui='filter-bar'] [data-part='chip'] {
323
+ align-items: stretch;
324
+ background-color: var(--_surface);
325
+ block-size: var(--_height);
326
+ border: var(--_border-width) solid var(--_edge);
327
+ border-radius: var(--_radius);
328
+ box-shadow: var(--slotted-control-shadow, none);
329
+ box-sizing: border-box;
330
+ display: inline-flex;
331
+ max-inline-size: 100%;
332
+ min-inline-size: 0;
333
+ transition:
334
+ border-color var(--_duration) var(--_easing),
335
+ opacity var(--slotted-duration-medium) var(--slotted-easing-enter),
336
+ translate var(--slotted-duration-medium) var(--slotted-easing-enter);
337
+ }
338
+
339
+ /**
340
+ * A chip or a pill arrives: it fades in from a step above its place — the
341
+ * arrival `upload` gives a file — so a condition added from the menu is seen
342
+ * landing in the bar. It does not scale: a box of text that scales redraws
343
+ * its letters at every frame. It
344
+ * starts from the style below only on the frame it is first rendered — a
345
+ * chip whose value changes does not arrive again. A chip that leaves does
346
+ * not animate: focus moves to its neighbour in the same gesture, and a chip
347
+ * held for an exit would be a stop of the arrows that no longer exists.
348
+ */
349
+ @starting-style {
350
+ [data-ui='filter-bar'] :is([data-part='chip'], [data-part='pill']) {
351
+ opacity: 0;
352
+ translate: 0 calc(-1 * var(--slotted-space-2, 0.25rem));
353
+ }
354
+ }
355
+
356
+ [data-ui='filter-bar'] [data-part='chip'] > * {
357
+ align-items: center;
358
+ background-color: transparent;
359
+ border: 0;
360
+ box-sizing: border-box;
361
+ color: inherit;
362
+ display: inline-flex;
363
+ flex: none;
364
+ font: inherit;
365
+ gap: var(--_gap-sm);
366
+ line-height: var(--slotted-control-line-height, 1);
367
+ margin: 0;
368
+ min-inline-size: 0;
369
+ padding-block: 0;
370
+ padding-inline: var(--_seg-pad);
371
+ white-space: nowrap;
372
+ }
373
+
374
+ [data-ui='filter-bar'] [data-part='chip'] > * + * {
375
+ border-inline-start: var(--_border-width) solid var(--_seam);
376
+ }
377
+
378
+ [data-ui='filter-bar'] [data-part='chip'] > :first-child {
379
+ border-end-start-radius: var(--_radius);
380
+ border-start-start-radius: var(--_radius);
381
+ /* The capsule's curve takes air from the first letter, so the start inset is the control's, not the segment's. */
382
+ padding-inline-start: var(--_pad);
383
+ }
384
+
385
+ [data-ui='filter-bar'] [data-part='chip'] > :last-child {
386
+ border-end-end-radius: var(--_radius);
387
+ border-start-end-radius: var(--_radius);
388
+ }
389
+
390
+ [data-ui='filter-bar'] [data-part='chip'] [data-ui='icon'] {
391
+ --slotted-icon-size: var(--_icon);
392
+ }
393
+
394
+ [data-ui='filter-bar'] [data-part='chip-field'] {
395
+ font-weight: var(--slotted-font-weight-medium, 500);
396
+ }
397
+
398
+ [data-ui='filter-bar'] [data-part='chip-field'] > [data-ui='icon'] {
399
+ color: var(--_muted);
400
+ }
401
+
402
+ /** The field's name, the operator and the value are isolated runs, so a right-to-left name beside an ISO date reorders nothing. */
403
+ [data-ui='filter-bar'] [data-part='chip'] > * > span:not([data-ui]),
404
+ [data-ui='filter-bar'] [data-part='chip-operator'] {
405
+ unicode-bidi: isolate;
406
+ }
407
+
408
+ [data-ui='filter-bar'] [data-part='chip-operator'] {
409
+ color: var(--_muted);
410
+ font-weight: var(--slotted-font-weight-regular, 400);
411
+ }
412
+
413
+ [data-ui='filter-bar'] [data-part='chip-value'] {
414
+ font-weight: var(--slotted-font-weight-medium, 500);
415
+ min-inline-size: 0;
416
+ }
417
+
418
+ /* The value truncates and the whole list stays in the segment's name and title. */
419
+ [data-ui='filter-bar'] [data-part='chip-value'] > [data-value] {
420
+ max-inline-size: var(--slotted-filter-bar-value-max, 12rem);
421
+ min-inline-size: 0;
422
+ overflow: hidden;
423
+ text-overflow: ellipsis;
424
+ }
425
+
426
+ /**
427
+ * The operator's words inside the value segment are drawn only where the
428
+ * operator segment is not — under the narrow breakpoint, below.
429
+ */
430
+ [data-ui='filter-bar'] [data-part='chip-value'] > [data-operator] {
431
+ color: var(--_muted);
432
+ display: none;
433
+ font-weight: var(--slotted-font-weight-regular, 400);
434
+ }
435
+
436
+ /** The remove control is square in the capsule's height, and its glyph reads quiet until it is aimed at. */
437
+ [data-ui='filter-bar'] [data-part='chip-remove'] {
438
+ aspect-ratio: 1;
439
+ color: var(--_muted);
440
+ justify-content: center;
441
+ padding-inline: 0;
442
+ }
443
+
444
+ [data-ui='filter-bar'] [data-part='chip'] > :is(button, [role='button']) {
445
+ cursor: pointer;
446
+ transition:
447
+ background-color var(--_duration) var(--_easing),
448
+ color var(--_duration) var(--_easing);
449
+ }
450
+
451
+ /*
452
+ * Hover and press are the segment's own. Press is a step past hover, and an
453
+ * open segment holds the press ground while its surface is up, so the
454
+ * reader sees which segment the menu or the popover belongs to.
455
+ */
456
+ [data-ui='filter-bar']
457
+ [data-part='chip']
458
+ > :is(button, [role='button']):enabled:not([aria-disabled='true']):hover {
459
+ background-color: var(--_hover);
460
+ color: var(--_ink);
461
+ }
462
+
463
+ [data-ui='filter-bar']
464
+ [data-part='chip']
465
+ > :is(button, [role='button']):enabled:not([aria-disabled='true']):active,
466
+ [data-ui='filter-bar'] [data-part='chip'] > :is(button, [role='button'])[aria-expanded='true'] {
467
+ background-color: var(--_active);
468
+ color: var(--_ink);
469
+ }
470
+
471
+ /*
472
+ * A segment's ring is drawn inward: an outward ring would lie across the
473
+ * neighbouring segments, and inward it stays inside the capsule and follows
474
+ * its curve on the end segments — the rule every inner control follows.
475
+ */
476
+ [data-ui='filter-bar'] [data-part='chip'] > :focus-visible {
477
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
478
+ outline-offset: calc(var(--slotted-focus-ring-width, 2px) * -1);
479
+ }
480
+
481
+ /**
482
+ * A condition still waiting for its value: the dashed edge of a pill, which
483
+ * says the same thing, and the value segment writes the prompt in the
484
+ * muted ink. The condition is not applied until the value is there.
485
+ */
486
+ [data-ui='filter-bar'] [data-part='chip'][data-incomplete] {
487
+ border-style: dashed;
488
+ box-shadow: none;
489
+ }
490
+
491
+ [data-ui='filter-bar'] [data-part='chip'][data-incomplete] > [data-part='chip-value'] {
492
+ color: var(--_muted);
493
+ font-weight: var(--slotted-font-weight-regular, 400);
494
+ }
495
+
496
+ [data-ui='filter-bar'] [data-part='chip'][data-invalid] {
497
+ border-color: var(--_invalid);
498
+ box-shadow: var(--_invalid-edge);
499
+ }
500
+
501
+ /** The field's glyph and name turn to the danger ink with the edge, so the fault is read on the segment it is about. */
502
+ [data-ui='filter-bar'] [data-part='chip'][data-invalid] > [data-part='chip-field'],
503
+ [data-ui='filter-bar']
504
+ [data-part='chip'][data-invalid]
505
+ > [data-part='chip-field']
506
+ > [data-ui='icon'] {
507
+ color: var(--slotted-tone-danger-text, CanvasText);
508
+ }
509
+
510
+ [data-ui='filter-bar'] [data-part='chip'][data-folded] {
511
+ display: none;
512
+ visibility: hidden;
513
+ }
514
+
515
+ [data-ui='filter-bar'] [data-part='bar'][data-revealed] [data-part='chip'][data-folded] {
516
+ display: inline-flex;
517
+ visibility: visible;
518
+ }
519
+
520
+ /* ── More ───────────────────────────────────────────────────────────── */
521
+
522
+ [data-ui='filter-bar'] [data-part='more'] {
523
+ align-items: center;
524
+ appearance: none;
525
+ background: none;
526
+ block-size: var(--_height);
527
+ border: 0;
528
+ border-radius: var(--_radius);
529
+ color: var(--_muted);
530
+ cursor: pointer;
531
+ display: inline-flex;
532
+ font: inherit;
533
+ font-weight: var(--slotted-font-weight-medium, 500);
534
+ justify-content: center;
535
+ margin: 0;
536
+ padding-inline: var(--_seg-pad);
537
+ transition:
538
+ background-color var(--_duration) var(--_easing),
539
+ color var(--_duration) var(--_easing);
540
+ white-space: nowrap;
541
+ }
542
+
543
+ [data-ui='filter-bar'] [data-part='more']:enabled:hover {
544
+ background-color: var(--_hover);
545
+ color: var(--_ink);
546
+ }
547
+
548
+ [data-ui='filter-bar'] [data-part='more']:enabled:active {
549
+ background-color: var(--_active);
550
+ color: var(--_ink);
551
+ }
552
+
553
+ [data-ui='filter-bar'] [data-part='more']:focus-visible {
554
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
555
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
556
+ }
557
+
558
+ /* ── Add and Clear ──────────────────────────────────────────────────────
559
+ *
560
+ * Both are `button`s worn with this family's part name. Add draws its glyph
561
+ * alone once a chip is in the bar, as a circle in the chips' height, so the
562
+ * line of capsules ends in a control of their own shape; it keeps its label
563
+ * as its name and its title. Clear and Reset read at the chips' weight: the
564
+ * conditions are what the bar is about, and a control that empties them is
565
+ * not the loudest word on the line. Apply keeps the weight of a commit.
566
+ */
567
+ [data-ui='filter-bar'] [data-part='bar'] :is([data-part='clear'], [data-part='reset']) {
568
+ --slotted-control-font-weight: var(--slotted-font-weight-medium, 500);
569
+ }
570
+
571
+ /* ── The summary, drawn only when the bar is narrow ─────────────────── */
572
+
573
+ [data-ui='filter-bar'] [data-part='summary'] {
574
+ display: none;
575
+ visibility: hidden;
576
+ }
577
+
578
+ [data-ui='filter-bar'] [data-part='summary'] > [data-part='label'] > span {
579
+ /* The count in the summary is a figure, and figures line up. */
580
+ font-variant-numeric: tabular-nums;
581
+ }
582
+
583
+ /* ── The actions under apply=manual ─────────────────────────────────── */
584
+
585
+ /**
586
+ * Apply and Reset close the toolbar line from its end. Clear stays beside
587
+ * Add rather than at the far end: under manual the far end holds Apply,
588
+ * and Clear there would make the control that empties the filter the
589
+ * neighbour of the one that commits it.
590
+ */
591
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='bar'] > [data-part='actions'] {
592
+ align-items: center;
593
+ display: flex;
594
+ gap: var(--_gap-sm);
595
+ margin-inline-start: auto;
596
+ }
597
+
598
+ /* ── States of the whole bar ────────────────────────────────────────── */
599
+
600
+ [data-ui='filter-bar'][data-disabled] {
601
+ --_ink: var(--slotted-disabled-foreground, GrayText);
602
+ --_muted: var(--slotted-disabled-foreground, GrayText);
603
+ --_edge: var(--slotted-disabled-border, GrayText);
604
+ --_seam: var(--slotted-disabled-border, GrayText);
605
+ --_surface: var(--slotted-disabled-background, Canvas);
606
+ }
607
+
608
+ [data-ui='filter-bar'][data-disabled] :is([data-part='pill'], [data-part='chip']) {
609
+ box-shadow: none;
610
+ }
611
+
612
+ [data-ui='filter-bar'][data-disabled] :is([data-part='pill'], [data-part='chip'] > *) {
613
+ cursor: not-allowed;
614
+ }
615
+
616
+ /**
617
+ * Read-only: every condition stays reachable and readable, and nothing
618
+ * invites a press — the segments keep their stop and lose the pointer, and
619
+ * the binding draws no remove, add or clear control.
620
+ */
621
+ [data-ui='filter-bar'][data-readonly] [data-part='chip'] > * {
622
+ cursor: default;
623
+ }
624
+
625
+ /** A loading bar keeps its width and its controls, and says so in the count's place. */
626
+ [data-ui='filter-bar'][data-loading] > [data-part='frame'] > [data-part='count'] {
627
+ align-items: center;
628
+ display: inline-flex;
629
+ gap: var(--_gap-sm);
630
+ }
631
+
632
+ /* ── Sticky ─────────────────────────────────────────────────────────────
633
+ *
634
+ * The bar sticks under the page chrome, reading the property `page`
635
+ * publishes and never measuring the header itself. It then adds its own
636
+ * height to the number for what follows: the binding measures the root and
637
+ * writes the sum as `--slotted-page-sticky-top` on the element that holds
638
+ * the bar and the listing — the rule `section` established for its body —
639
+ * and writes the value it read back on the root itself, so the bar keeps
640
+ * sticking where it did while everything after it stops under the bar.
641
+ */
642
+ [data-ui='filter-bar'][data-sticky] {
643
+ background-color: var(--_surface);
644
+ inset-block-start: var(--slotted-page-sticky-top, 0px);
645
+ padding-block: var(--slotted-space-3, 0.375rem);
646
+ position: sticky;
647
+ z-index: var(--slotted-z-index-sticky, 100);
648
+ }
649
+
650
+ /* ── The builder ────────────────────────────────────────────────────────
651
+ *
652
+ * The whole tree, in a drawer, laid out the way Notion, Airtable and AG
653
+ * Grid's advanced filter lay a query out: a group opens with "Match [All |
654
+ * Any] of the following", every row under it starts with the word that
655
+ * joins it to the row above — "Where" on the first, "and" or "or" after —
656
+ * and a group inside a group is a box on a sunken ground with the same
657
+ * header and its own remove. A rule is one row: field, operator, operand,
658
+ * remove; it stacks when the drawer is narrow, and the leading word stays.
659
+ */
660
+ [data-ui='filter-bar'][data-part='builder'] {
661
+ --_join-col: var(--slotted-filter-bar-join-size, 3.5rem);
662
+ --_group-surface: var(
663
+ --slotted-filter-bar-group-surface,
664
+ var(--slotted-surface-sunken, Canvas)
665
+ );
666
+ --_group-edge: var(--slotted-filter-bar-group-border, var(--slotted-border-subtle, GrayText));
667
+
668
+ container-name: slotted-filter-bar-builder;
669
+ container-type: inline-size;
670
+ display: grid;
671
+ gap: var(--slotted-space-5, 0.75rem);
672
+ }
673
+
674
+ /**
675
+ * Every group is a container of its own, so a rule stacks against the width
676
+ * its own group has left, not the drawer's: two levels in, the gutter, the
677
+ * box padding and the border have taken nine rems, and a row measured
678
+ * against the drawer overflowed its box. The name differs from the
679
+ * builder's, because the card form below is the builder's decision and
680
+ * every group in one builder takes the same form.
681
+ */
682
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group'] {
683
+ border: 0;
684
+ container-name: slotted-filter-bar-group;
685
+ container-type: inline-size;
686
+ display: grid;
687
+ gap: var(--_gap);
688
+ margin: 0;
689
+ min-inline-size: 0;
690
+ padding: 0;
691
+ }
692
+
693
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group']:not([data-depth='0']) {
694
+ background-color: var(--_group-surface);
695
+ border: var(--slotted-border-width-thin, 1px) solid var(--_group-edge);
696
+ border-radius: var(--slotted-radius-md, 6px);
697
+ padding: var(--slotted-space-5, 0.75rem);
698
+ }
699
+
700
+ /* A box inside a box alternates its ground, so the third level reads as a level and not as a border. */
701
+ [data-ui='filter-bar'][data-part='builder']
702
+ [data-part='group']:is([data-depth='2'], [data-depth='4'], [data-depth='6']) {
703
+ background-color: var(--_surface);
704
+ }
705
+
706
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group'] > legend {
707
+ /* The legend names the group to a reader and draws nothing. */
708
+ block-size: 1px;
709
+ clip-path: inset(50%);
710
+ overflow: hidden;
711
+ position: absolute;
712
+ white-space: nowrap;
713
+ inline-size: 1px;
714
+ }
715
+
716
+ /** The header: the words, the All | Any control between them, and, in a nested group, its remove at the end. */
717
+ [data-ui='filter-bar'][data-part='builder'] [data-part='join'] {
718
+ align-items: center;
719
+ color: var(--_ink);
720
+ display: flex;
721
+ flex-wrap: wrap;
722
+ font-weight: var(--slotted-font-weight-medium, 500);
723
+ gap: var(--_gap-sm);
724
+ }
725
+
726
+ [data-ui='filter-bar'][data-part='builder'] [data-part='join'] > [data-part='remove-group'] {
727
+ margin-inline-start: auto;
728
+ }
729
+
730
+ /**
731
+ * The rows: a two-column grid, the joining word then the rule or the group,
732
+ * so every field lines up under the first whatever word stands before it.
733
+ */
734
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rules'] {
735
+ align-items: start;
736
+ column-gap: var(--_gap-sm);
737
+ display: grid;
738
+ grid-template-columns: var(--_join-col) minmax(0, 1fr);
739
+ min-inline-size: 0;
740
+ row-gap: var(--_gap);
741
+ }
742
+
743
+ [data-ui='filter-bar'][data-part='builder'] [data-part='conjunction'] {
744
+ color: var(--_muted);
745
+ /* Centred on the first control of its row, whatever the row stacks to. */
746
+ line-height: var(--_height);
747
+ overflow: hidden;
748
+ text-align: end;
749
+ text-overflow: ellipsis;
750
+ white-space: nowrap;
751
+ }
752
+
753
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'] {
754
+ align-items: center;
755
+ display: grid;
756
+ gap: var(--_gap-sm);
757
+ /* Every track may shrink, so a row never pushes its remove control past its
758
+ group's edge; the container query below stacks it before it gets narrow.
759
+ The remove track is the control's own width, so the crosses of one
760
+ group stand in a column whatever each row holds. */
761
+ grid-template-columns:
762
+ minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.5fr)
763
+ var(--slotted-target-min, 24px);
764
+ min-inline-size: 0;
765
+ }
766
+
767
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'] > *,
768
+ [data-ui='filter-bar'][data-part='builder']
769
+ [data-part='rule']
770
+ :is([data-part='operator'], [data-part='operand']) {
771
+ min-inline-size: 0;
772
+ }
773
+
774
+ [data-ui='filter-bar'][data-part='builder']
775
+ [data-part='rule'][data-operands='0']
776
+ [data-part='operand'] {
777
+ display: none;
778
+ }
779
+
780
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-operands='0'] {
781
+ /* The operator takes the operand's tracks too, so the field keeps the width it has on every other row. */
782
+ grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) var(--slotted-target-min, 24px);
783
+ }
784
+
785
+ [data-ui='filter-bar'][data-part='builder'] [data-part='operand'] {
786
+ display: grid;
787
+ gap: var(--_gap-sm);
788
+ min-inline-size: 0;
789
+ }
790
+
791
+ [data-ui='filter-bar'][data-part='builder']
792
+ [data-part='rule'][data-operands='2']
793
+ [data-part='operand'] {
794
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
795
+ }
796
+
797
+ /** The group's own controls, under its rows and aligned with them. */
798
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group'] > [data-part='actions'] {
799
+ align-items: center;
800
+ display: flex;
801
+ flex-wrap: wrap;
802
+ gap: var(--_gap-sm);
803
+ padding-inline-start: calc(var(--_join-col) + var(--_gap-sm));
804
+ }
805
+
806
+ /**
807
+ * The builder's Add rule and Add group are outlined: they are what the
808
+ * footer is for, and as ghosts under a column of outlined fields they read
809
+ * as a caption. A nested group's pair is ghost, so the builder's own pair
810
+ * stays the one that reads as the footer. Save
811
+ * as view is a different act — it leaves the builder for the application's
812
+ * dialog — so it stands apart at the far end, at the weight of the words.
813
+ */
814
+ [data-ui='filter-bar'][data-part='builder']
815
+ [data-part='group']
816
+ > [data-part='actions']
817
+ > :is(
818
+ [data-part='add-rule'],
819
+ [data-part='add-group'],
820
+ [data-part='clear'],
821
+ [data-part='save-preset']
822
+ ) {
823
+ --slotted-control-font-weight: var(--slotted-font-weight-medium, 500);
824
+ }
825
+
826
+ [data-ui='filter-bar'][data-part='builder']
827
+ [data-part='group']
828
+ > [data-part='actions']
829
+ > [data-part='save-preset'] {
830
+ margin-inline-start: auto;
831
+ }
832
+
833
+ @container slotted-filter-bar-group (max-width: 30rem) {
834
+ /* The second track is the remove control's own width, not `auto`: the
835
+ operator and the operand span both tracks, and a spanning item hands
836
+ its size to the non-flexible track first — `auto` took the operand's
837
+ whole width and left the field 34 pixels wide, a chevron with no name. */
838
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'],
839
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-operands='0'] {
840
+ grid-template-columns: minmax(0, 1fr) var(--slotted-target-min, 24px);
841
+ }
842
+
843
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'] > [data-part='field'] {
844
+ grid-column: 1;
845
+ }
846
+
847
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'] > [data-part='remove'] {
848
+ grid-column: 2;
849
+ grid-row: 1;
850
+ }
851
+
852
+ [data-ui='filter-bar'][data-part='builder']
853
+ [data-part='rule']
854
+ :is([data-part='operator'], [data-part='operand']) {
855
+ grid-column: 1 / -1;
856
+ }
857
+ }
858
+
859
+ /*
860
+ * The sentence of a rule is the card form's header, and a row that already
861
+ * shows its three controls does not draw it.
862
+ */
863
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] {
864
+ display: none;
865
+ visibility: hidden;
866
+ }
867
+
868
+ /* ── The builder as cards ───────────────────────────────────────────────
869
+ *
870
+ * Under 30rem of the builder — a phone, where the builder is a sheet — a row
871
+ * of three controls beside a gutter of joining words leaves each control a
872
+ * third of a third of the screen, and every level of nesting takes another
873
+ * eighty pixels. So each rule becomes a card: its sentence, which a reader
874
+ * scans, and its remove control; pressing the sentence expands the card and
875
+ * stacks the field, the operator and the operand under it at the card's
876
+ * full width. A nested group is a rail on its start edge, one step in, and
877
+ * the joining word stands on its own line between two cards.
878
+ */
879
+ @container slotted-filter-bar-builder (max-width: 30rem) {
880
+ [data-ui='filter-bar'][data-part='builder'] {
881
+ gap: var(--_gap);
882
+ }
883
+
884
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rules'] {
885
+ grid-template-columns: minmax(0, 1fr);
886
+ row-gap: var(--_gap-sm);
887
+ }
888
+
889
+ /* The joining word, small and on a line of its own; "Where" says nothing a list of cards does not. */
890
+ [data-ui='filter-bar'][data-part='builder'] [data-part='conjunction'] {
891
+ font-size: var(--slotted-font-size-xs, 12px);
892
+ line-height: var(--slotted-line-height-xs, 1.5);
893
+ padding-inline-start: var(--_seg-pad);
894
+ text-align: start;
895
+ }
896
+
897
+ [data-ui='filter-bar'][data-part='builder']
898
+ [data-part='rules']
899
+ > [data-part='conjunction']:first-child {
900
+ display: none;
901
+ }
902
+
903
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group']:not([data-depth='0']),
904
+ [data-ui='filter-bar'][data-part='builder']
905
+ [data-part='group']:is([data-depth='2'], [data-depth='4'], [data-depth='6']) {
906
+ background-color: transparent;
907
+ border: 0;
908
+ /* The rail carries the nesting alone here, so it takes the edge the cards have rather than the box's subtler one. */
909
+ border-inline-start: var(--slotted-border-width-medium, 2px) solid var(--_edge);
910
+ border-radius: 0;
911
+ padding-block: 0;
912
+ padding-inline: var(--slotted-space-4, 0.5rem) 0;
913
+ }
914
+
915
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'],
916
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-operands='0'] {
917
+ background-color: var(--_surface);
918
+ border: var(--_border-width) solid var(--_edge);
919
+ border-radius: var(--slotted-radius-md, 6px);
920
+ box-shadow: var(--slotted-control-shadow, none);
921
+ column-gap: 0;
922
+ grid-template-areas: 'summary remove';
923
+ grid-template-columns: minmax(0, 1fr) var(--_height);
924
+ row-gap: var(--_gap-sm);
925
+ transition: border-color var(--_duration) var(--_easing);
926
+ }
927
+
928
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-expanded] {
929
+ border-color: var(--slotted-border-strong, ButtonBorder);
930
+ padding-block-end: var(--_seg-pad);
931
+ }
932
+
933
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-invalid] {
934
+ border-color: var(--_invalid);
935
+ box-shadow: var(--_invalid-edge);
936
+ }
937
+
938
+ /* Forced colours remove the shadow; the edge comes back as an outline. */
939
+ @media (forced-colors: active) {
940
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'][data-invalid] {
941
+ outline: var(--_invalid-edge-width) solid CanvasText;
942
+ outline-offset: calc(-1 * (var(--_border-width) + var(--_invalid-edge-width)));
943
+ }
944
+ }
945
+
946
+ /* The sentence: the field at the weight of a name, the operator quiet, the value back at weight. It wraps rather than truncates — on a phone the sentence is what is read. */
947
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] {
948
+ /* The chevron stands in the middle of the card's header, whether the sentence takes one line or two. */
949
+ align-items: center;
950
+ appearance: none;
951
+ background: none;
952
+ border: 0;
953
+ border-radius: calc(var(--slotted-radius-md, 6px) - var(--_border-width));
954
+ color: var(--_ink);
955
+ cursor: pointer;
956
+ display: flex;
957
+ font: inherit;
958
+ gap: var(--_gap-sm);
959
+ grid-area: summary;
960
+ margin: 0;
961
+ min-block-size: var(--_height);
962
+ min-inline-size: 0;
963
+ padding-block: max(var(--_gap-sm), calc((var(--_height) - 1lh) / 2));
964
+ padding-inline: var(--_seg-pad) 0;
965
+ text-align: start;
966
+ transition: background-color var(--_duration) var(--_easing);
967
+ visibility: visible;
968
+ }
969
+
970
+ /* The words wrap inside their own run, so the chevron keeps the end of the first line however long the value is. */
971
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] > [data-sentence] {
972
+ flex: 1 1 auto;
973
+ min-inline-size: 0;
974
+ }
975
+
976
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] [data-field] {
977
+ font-weight: var(--slotted-font-weight-medium, 500);
978
+ }
979
+
980
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] [data-operator] {
981
+ color: var(--_muted);
982
+ }
983
+
984
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] [data-value] {
985
+ font-weight: var(--slotted-font-weight-medium, 500);
986
+ min-inline-size: 0;
987
+ overflow-wrap: anywhere;
988
+ }
989
+
990
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary'] > [data-ui='icon'] {
991
+ --slotted-icon-size: var(--_icon);
992
+
993
+ color: var(--_muted);
994
+ flex: none;
995
+ margin-inline-start: auto;
996
+ transition: rotate var(--_duration) var(--_easing);
997
+ }
998
+
999
+ [data-ui='filter-bar'][data-part='builder']
1000
+ [data-part='rule-summary'][aria-expanded='true']
1001
+ > [data-ui='icon'] {
1002
+ rotate: 180deg;
1003
+ }
1004
+
1005
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary']:enabled:hover {
1006
+ background-color: var(--_hover);
1007
+ }
1008
+
1009
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary']:enabled:active {
1010
+ background-color: var(--_active);
1011
+ }
1012
+
1013
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule-summary']:focus-visible {
1014
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
1015
+ outline-offset: calc(var(--slotted-focus-ring-width, 2px) * -1);
1016
+ }
1017
+
1018
+ /*
1019
+ * The remove control stands level with the chevron, in the middle of the
1020
+ * header: the header's own height is the summary's, so the row it shares
1021
+ * with the summary is centred on it, and an expanded card's controls
1022
+ * below do not pull it down.
1023
+ */
1024
+ [data-ui='filter-bar'][data-part='builder'] [data-part='rule'] > [data-part='remove'] {
1025
+ align-self: center;
1026
+ grid-area: remove;
1027
+ grid-row: 1;
1028
+ justify-self: center;
1029
+ }
1030
+
1031
+ /*
1032
+ * A collapsed card holds only its sentence: the controls leave the picture
1033
+ * and the tab order. Expanded, they stack at the card's full width and
1034
+ * arrive from a step above, so the eye follows the card opening; closing
1035
+ * snaps, because the reader has already looked away to the next card.
1036
+ */
1037
+ [data-ui='filter-bar'][data-part='builder']
1038
+ [data-part='rule']:not([data-expanded])
1039
+ :is([data-part='field'], [data-part='operator'], [data-part='operand']) {
1040
+ display: none;
1041
+ visibility: hidden;
1042
+ }
1043
+
1044
+ [data-ui='filter-bar'][data-part='builder']
1045
+ [data-part='rule'][data-expanded]
1046
+ :is([data-part='field'], [data-part='operator'], [data-part='operand']) {
1047
+ grid-column: 1 / -1;
1048
+ margin-inline: var(--_seg-pad);
1049
+ transition:
1050
+ opacity var(--slotted-duration-medium) var(--slotted-easing-enter),
1051
+ translate var(--slotted-duration-medium) var(--slotted-easing-enter);
1052
+ }
1053
+
1054
+ @starting-style {
1055
+ [data-ui='filter-bar'][data-part='builder']
1056
+ [data-part='rule'][data-expanded]
1057
+ :is([data-part='field'], [data-part='operator'], [data-part='operand']) {
1058
+ opacity: 0;
1059
+ translate: 0 calc(-1 * var(--slotted-space-2, 0.25rem));
1060
+ }
1061
+ }
1062
+
1063
+ [data-ui='filter-bar'][data-part='builder'] [data-part='group'] > [data-part='actions'] {
1064
+ padding-inline-start: 0;
1065
+ }
1066
+
1067
+ /*
1068
+ * The builder's own Add rule and Add group share their line in two equal
1069
+ * halves, and Clear all and Save as view take the line under them. A
1070
+ * nested group's pair stays a quiet line at the start of its rail: three
1071
+ * lines of full-width buttons read as three copies of one control.
1072
+ */
1073
+ [data-ui='filter-bar'][data-part='builder']
1074
+ [data-part='group'][data-depth='0']
1075
+ > [data-part='actions'] {
1076
+ display: grid;
1077
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
1078
+ }
1079
+
1080
+ [data-ui='filter-bar'][data-part='builder']
1081
+ [data-part='group'][data-depth='0']
1082
+ > [data-part='actions']
1083
+ > :is([data-part='add-rule'], [data-part='add-group']) {
1084
+ inline-size: 100%;
1085
+ }
1086
+
1087
+ [data-ui='filter-bar'][data-part='builder']
1088
+ [data-part='group']
1089
+ > [data-part='actions']
1090
+ > :is([data-part='clear'], [data-part='save-preset']) {
1091
+ margin-inline-start: 0;
1092
+ }
1093
+ }
1094
+
1095
+ /* ── A builder row's remove control ─────────────────────────────────── */
1096
+
1097
+ [data-ui='filter-bar'] [data-part='rule'] > [data-part='remove'] {
1098
+ align-items: center;
1099
+ appearance: none;
1100
+ background: none;
1101
+ block-size: var(--slotted-target-min, 24px);
1102
+ border: 0;
1103
+ border-radius: var(--_control-radius);
1104
+ color: var(--_muted);
1105
+ cursor: pointer;
1106
+ display: inline-flex;
1107
+ font: inherit;
1108
+ inline-size: var(--slotted-target-min, 24px);
1109
+ justify-content: center;
1110
+ padding: 0;
1111
+ transition:
1112
+ background-color var(--_duration) var(--_easing),
1113
+ color var(--_duration) var(--_easing);
1114
+ }
1115
+
1116
+ [data-ui='filter-bar'] [data-part='rule'] > [data-part='remove'] > [data-ui='icon'] {
1117
+ --slotted-icon-size: var(--_icon);
1118
+ }
1119
+
1120
+ [data-ui='filter-bar'] [data-part='rule'] > [data-part='remove']:enabled:hover {
1121
+ background-color: var(--_hover);
1122
+ color: var(--_ink);
1123
+ }
1124
+
1125
+ [data-ui='filter-bar'] [data-part='rule'] > [data-part='remove']:enabled:active {
1126
+ background-color: var(--_active);
1127
+ color: var(--_ink);
1128
+ }
1129
+
1130
+ [data-ui='filter-bar'] [data-part='rule'] > [data-part='remove']:focus-visible {
1131
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
1132
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
1133
+ }
1134
+
1135
+ /* ── Narrow ─────────────────────────────────────────────────────────────
1136
+ *
1137
+ * Under the breakpoint — a phone — the bar keeps every condition in sight
1138
+ * on one row that scrolls sideways, instead of folding them away or
1139
+ * wrapping them into a wall of lines above the listing: the summary first,
1140
+ * which opens the builder as a sheet, then every chip, the folded ones too,
1141
+ * then the pinned fields still empty. A chip there is three targets, not
1142
+ * four — the field, the value with the operator's words in it, the remove
1143
+ * control — because every edit on a phone opens the builder, with the
1144
+ * condition's card expanded. The last chip cut by the edge is what says
1145
+ * the row goes on.
1146
+ *
1147
+ * The search stays on top, because a query is the one thing a reader on a
1148
+ * phone types first, and the count stays under the row, because it is the
1149
+ * answer.
1150
+ */
1151
+ @container slotted-filter-bar (max-width: 36rem) {
1152
+ [data-ui='filter-bar'] > [data-part='frame'] {
1153
+ grid-template-areas:
1154
+ 'search'
1155
+ 'bar'
1156
+ 'count';
1157
+ grid-template-columns: minmax(0, 1fr);
1158
+ }
1159
+
1160
+ [data-ui='filter-bar'] > [data-part='frame']:has(> [data-part='end']:not(:empty)) {
1161
+ grid-template-areas:
1162
+ 'search'
1163
+ 'bar'
1164
+ 'end'
1165
+ 'count';
1166
+ }
1167
+
1168
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='search'] {
1169
+ max-inline-size: none;
1170
+ }
1171
+
1172
+ /*
1173
+ * The row scrolls, so it clips at its padding edge, and a ring drawn
1174
+ * outside a pill or the summary would lose its outer pixels there. Every
1175
+ * member of the row draws its ring one width inside its own box instead —
1176
+ * the chip segments already do, and it is the answer a chip row inside a
1177
+ * control gives (`focus-ring.test.mjs`) — so the row takes no room it
1178
+ * would have to give back as a negative margin.
1179
+ */
1180
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='bar'] {
1181
+ flex-wrap: nowrap;
1182
+ overflow-x: auto;
1183
+ overscroll-behavior-inline: contain;
1184
+ scrollbar-width: none;
1185
+ }
1186
+
1187
+ [data-ui='filter-bar']
1188
+ > [data-part='frame']
1189
+ > [data-part='bar']
1190
+ > :is([data-part='summary'], [data-part='pill']):focus-visible {
1191
+ outline-offset: calc(var(--slotted-focus-ring-width, 2px) * -1);
1192
+ }
1193
+
1194
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='bar']::-webkit-scrollbar {
1195
+ display: none;
1196
+ }
1197
+
1198
+ [data-ui='filter-bar']
1199
+ > [data-part='frame']
1200
+ > [data-part='bar']
1201
+ > :is([data-part='summary'], [data-part='chip'], [data-part='pill']) {
1202
+ flex: none;
1203
+ }
1204
+
1205
+ [data-ui='filter-bar']
1206
+ > [data-part='frame']
1207
+ > [data-part='bar']
1208
+ > :is([data-part='more'], [data-part='add'], [data-part='clear'], [data-part='actions']),
1209
+ [data-ui='filter-bar'] [data-part='chip'] > [data-part='chip-operator'] {
1210
+ display: none;
1211
+ visibility: hidden;
1212
+ }
1213
+
1214
+ [data-ui='filter-bar']
1215
+ > [data-part='frame']
1216
+ > [data-part='bar']
1217
+ > [data-part='chip'][data-folded] {
1218
+ display: inline-flex;
1219
+ visibility: visible;
1220
+ }
1221
+
1222
+ [data-ui='filter-bar'] [data-part='chip-value'] > [data-operator] {
1223
+ display: inline;
1224
+ }
1225
+
1226
+ [data-ui='filter-bar'] [data-part='chip-value'] > [data-value] {
1227
+ max-inline-size: var(--slotted-filter-bar-value-max-narrow, 9rem);
1228
+ }
1229
+
1230
+ [data-ui='filter-bar'] [data-part='summary'] {
1231
+ display: inline-flex;
1232
+ visibility: visible;
1233
+ }
1234
+
1235
+ /* A line of its own here, with nothing to line up with. */
1236
+ [data-ui='filter-bar'] > [data-part='frame'] > [data-part='count'] {
1237
+ justify-self: start;
1238
+ padding-block: 0;
1239
+ }
1240
+ }
1241
+
1242
+ /**
1243
+ * A legend for a key the reader has none of. Where the primary input is a
1244
+ * touch screen the shortcut at the end of the search names nothing the
1245
+ * reader can press; the field is tapped. The key still works when a
1246
+ * keyboard is attached.
1247
+ */
1248
+ @media (hover: none) and (pointer: coarse) {
1249
+ [data-ui='filter-bar'] [data-part='search'] [data-ui='kbd'] {
1250
+ display: none;
1251
+ }
1252
+ }
1253
+
1254
+ /*
1255
+ * On a finger the remove control of a builder row and `more` are full
1256
+ * targets, and the box is the one a mouse sees: a transparent `::before`
1257
+ * centred on each reaches the comfortable size in both axes, so no row grows
1258
+ * and the builder's remove track keeps its width. A chip's segments take the
1259
+ * control height, which the theme already raises to the comfortable target.
1260
+ */
1261
+ @media (pointer: coarse) {
1262
+ [data-ui='filter-bar'] :is([data-part='more'], [data-part='rule'] > [data-part='remove']) {
1263
+ position: relative;
1264
+ }
1265
+
1266
+ [data-ui='filter-bar']
1267
+ :is([data-part='more'], [data-part='rule'] > [data-part='remove'])::before {
1268
+ content: '';
1269
+ inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
1270
+ position: absolute;
1271
+ }
1272
+ }
1273
+
1274
+ @media (forced-colors: active) {
1275
+ /* Forced colours remove the invalid edge's shadow; it comes back as an
1276
+ outline inside the border, and a focused invalid pill draws its ring
1277
+ double. A chip's ring belongs to a segment and leaves it alone. */
1278
+ [data-ui='filter-bar'] [data-part='pill'][data-invalid],
1279
+ [data-ui='filter-bar'] [data-part='chip'][data-invalid] {
1280
+ outline: var(--_invalid-edge-width) solid CanvasText;
1281
+ outline-offset: calc(-1 * (var(--_border-width) + var(--_invalid-edge-width)));
1282
+ }
1283
+
1284
+ [data-ui='filter-bar'] [data-part='pill'][data-invalid]:focus-visible {
1285
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
1286
+ var(--slotted-focus-ring-color, Highlight);
1287
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
1288
+ }
1289
+
1290
+ /* The seams would read as a second edge in the system palette; the segments keep one line each. */
1291
+ [data-ui='filter-bar'] [data-part='chip'] > * + * {
1292
+ border-inline-start-color: GrayText;
1293
+ }
1294
+
1295
+ [data-ui='filter-bar'] [data-part='chip'] > [aria-expanded='true'] {
1296
+ background-color: Highlight;
1297
+ color: HighlightText;
1298
+ }
1299
+ }
1300
+
1301
+ @media print {
1302
+ [data-ui='filter-bar'][data-sticky] {
1303
+ position: static;
1304
+ }
1305
+ }
1306
+ }