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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/package.json +276 -3
  4. package/src/accordion/accordion.css +96 -0
  5. package/src/accordion/accordion.tokens.json +14 -0
  6. package/src/action-bar/action-bar.css +455 -0
  7. package/src/action-bar/action-bar.tokens.json +50 -0
  8. package/src/action-sheet/action-sheet.css +462 -0
  9. package/src/action-sheet/action-sheet.tokens.json +59 -0
  10. package/src/alert/alert.css +764 -0
  11. package/src/alert/alert.tokens.json +77 -0
  12. package/src/app-shell/app-shell.css +1899 -0
  13. package/src/app-shell/app-shell.tokens.json +88 -0
  14. package/src/aspect-ratio/aspect-ratio.css +131 -0
  15. package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
  16. package/src/attachment/attachment.css +906 -0
  17. package/src/attachment/attachment.tokens.json +85 -0
  18. package/src/avatar/avatar.css +598 -0
  19. package/src/avatar/avatar.tokens.json +64 -0
  20. package/src/avatar-group/avatar-group.css +268 -0
  21. package/src/avatar-group/avatar-group.tokens.json +16 -0
  22. package/src/badge/badge.css +462 -0
  23. package/src/badge/badge.tokens.json +58 -0
  24. package/src/breadcrumb/breadcrumb.css +441 -0
  25. package/src/breadcrumb/breadcrumb.tokens.json +50 -0
  26. package/src/button/button.css +754 -0
  27. package/src/button/button.tokens.json +72 -0
  28. package/src/button-group/button-group.css +455 -0
  29. package/src/button-group/button-group.tokens.json +11 -0
  30. package/src/calendar/calendar.css +846 -0
  31. package/src/calendar/calendar.tokens.json +67 -0
  32. package/src/card/card.css +602 -0
  33. package/src/card/card.tokens.json +68 -0
  34. package/src/carousel/carousel.css +860 -0
  35. package/src/carousel/carousel.tokens.json +62 -0
  36. package/src/cascader/cascader.css +1320 -0
  37. package/src/cascader/cascader.tokens.json +103 -0
  38. package/src/chart/chart.css +1473 -0
  39. package/src/chart/chart.tokens.json +107 -0
  40. package/src/checkbox/checkbox.css +1011 -0
  41. package/src/checkbox/checkbox.tokens.json +102 -0
  42. package/src/checkbox-group/checkbox-group.css +70 -0
  43. package/src/checkbox-group/checkbox-group.tokens.json +6 -0
  44. package/src/code-block/code-block.css +555 -0
  45. package/src/code-block/code-block.tokens.json +69 -0
  46. package/src/collapsible/collapsible.css +546 -0
  47. package/src/collapsible/collapsible.tokens.json +55 -0
  48. package/src/color-picker/color-picker.css +607 -0
  49. package/src/color-picker/color-picker.tokens.json +58 -0
  50. package/src/column-manager/column-manager.css +452 -0
  51. package/src/column-manager/column-manager.tokens.json +43 -0
  52. package/src/combobox/combobox.css +1048 -0
  53. package/src/combobox/combobox.tokens.json +112 -0
  54. package/src/command-palette/command-palette.css +1000 -0
  55. package/src/command-palette/command-palette.tokens.json +75 -0
  56. package/src/container/container.css +198 -0
  57. package/src/container/container.tokens.json +18 -0
  58. package/src/context-menu/context-menu.css +104 -0
  59. package/src/context-menu/context-menu.tokens.json +11 -0
  60. package/src/currency-field/currency-field.css +787 -0
  61. package/src/currency-field/currency-field.tokens.json +80 -0
  62. package/src/dashboard-grid/dashboard-grid.css +748 -0
  63. package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
  64. package/src/data-grid/data-grid.css +2484 -0
  65. package/src/data-grid/data-grid.tokens.json +100 -0
  66. package/src/data-list/data-list.css +455 -0
  67. package/src/data-list/data-list.tokens.json +35 -0
  68. package/src/date-field/date-field.css +560 -0
  69. package/src/date-field/date-field.tokens.json +80 -0
  70. package/src/date-picker/date-picker.css +420 -0
  71. package/src/date-picker/date-picker.tokens.json +49 -0
  72. package/src/date-range-picker/date-range-picker.css +983 -0
  73. package/src/date-range-picker/date-range-picker.tokens.json +109 -0
  74. package/src/datetime-field/datetime-field.css +850 -0
  75. package/src/datetime-field/datetime-field.tokens.json +91 -0
  76. package/src/datetime-picker/datetime-picker.css +843 -0
  77. package/src/datetime-picker/datetime-picker.tokens.json +78 -0
  78. package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
  79. package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
  80. package/src/description-list/description-list.css +675 -0
  81. package/src/description-list/description-list.tokens.json +66 -0
  82. package/src/detail-panel/detail-panel.css +329 -0
  83. package/src/detail-panel/detail-panel.tokens.json +33 -0
  84. package/src/dialog/dialog.css +968 -0
  85. package/src/dialog/dialog.tokens.json +66 -0
  86. package/src/divider/divider.css +131 -0
  87. package/src/divider/divider.tokens.json +18 -0
  88. package/src/drawer/drawer.css +1303 -0
  89. package/src/drawer/drawer.tokens.json +75 -0
  90. package/src/entity-picker/entity-picker.css +1367 -0
  91. package/src/entity-picker/entity-picker.tokens.json +118 -0
  92. package/src/event-calendar/event-calendar.css +2148 -0
  93. package/src/event-calendar/event-calendar.tokens.json +87 -0
  94. package/src/field/field.css +707 -0
  95. package/src/field/field.tokens.json +58 -0
  96. package/src/field-group/field-group.css +226 -0
  97. package/src/field-group/field-group.tokens.json +33 -0
  98. package/src/filter-bar/filter-bar.css +1306 -0
  99. package/src/filter-bar/filter-bar.tokens.json +78 -0
  100. package/src/form/form.css +218 -0
  101. package/src/form/form.tokens.json +32 -0
  102. package/src/fullscreen/fullscreen.css +376 -0
  103. package/src/fullscreen/fullscreen.tokens.json +50 -0
  104. package/src/grid/grid.css +173 -0
  105. package/src/grid/grid.tokens.json +20 -0
  106. package/src/highlight/highlight.css +135 -0
  107. package/src/highlight/highlight.tokens.json +16 -0
  108. package/src/hover-card/hover-card.css +93 -0
  109. package/src/hover-card/hover-card.tokens.json +17 -0
  110. package/src/icon/icon.css +136 -0
  111. package/src/icon/icon.tokens.json +14 -0
  112. package/src/image/image.css +449 -0
  113. package/src/image/image.tokens.json +27 -0
  114. package/src/import-export-center/import-export-center.css +1175 -0
  115. package/src/import-export-center/import-export-center.tokens.json +84 -0
  116. package/src/inline/inline.css +149 -0
  117. package/src/inline/inline.tokens.json +14 -0
  118. package/src/inline-message/inline-message.css +396 -0
  119. package/src/inline-message/inline-message.tokens.json +19 -0
  120. package/src/kbd/kbd.css +490 -0
  121. package/src/kbd/kbd.tokens.json +40 -0
  122. package/src/link/link.css +237 -0
  123. package/src/link/link.tokens.json +25 -0
  124. package/src/list/list.css +240 -0
  125. package/src/list/list.tokens.json +42 -0
  126. package/src/list-item/list-item.css +937 -0
  127. package/src/list-item/list-item.tokens.json +114 -0
  128. package/src/listbox/listbox.css +674 -0
  129. package/src/listbox/listbox.tokens.json +72 -0
  130. package/src/loading-overlay/loading-overlay.css +730 -0
  131. package/src/loading-overlay/loading-overlay.tokens.json +48 -0
  132. package/src/markdown/markdown.css +168 -0
  133. package/src/markdown/markdown.tokens.json +18 -0
  134. package/src/menu/menu.css +817 -0
  135. package/src/menu/menu.tokens.json +78 -0
  136. package/src/menubar/menubar.css +170 -0
  137. package/src/menubar/menubar.tokens.json +32 -0
  138. package/src/message/message.css +1453 -0
  139. package/src/message/message.tokens.json +85 -0
  140. package/src/message-scroller/message-scroller.css +371 -0
  141. package/src/message-scroller/message-scroller.tokens.json +47 -0
  142. package/src/multi-select/multi-select.css +1427 -0
  143. package/src/multi-select/multi-select.tokens.json +114 -0
  144. package/src/navigation-menu/navigation-menu.css +1029 -0
  145. package/src/navigation-menu/navigation-menu.tokens.json +70 -0
  146. package/src/notification-center/notification-center.css +856 -0
  147. package/src/notification-center/notification-center.tokens.json +84 -0
  148. package/src/number-field/number-field.css +794 -0
  149. package/src/number-field/number-field.tokens.json +80 -0
  150. package/src/otp-input/otp-input.css +525 -0
  151. package/src/otp-input/otp-input.tokens.json +69 -0
  152. package/src/page/page.css +179 -0
  153. package/src/page/page.tokens.json +20 -0
  154. package/src/page-header/page-header.css +1181 -0
  155. package/src/page-header/page-header.tokens.json +71 -0
  156. package/src/pagination/pagination.css +500 -0
  157. package/src/pagination/pagination.tokens.json +56 -0
  158. package/src/popover/popover.css +526 -0
  159. package/src/popover/popover.tokens.json +60 -0
  160. package/src/progress-indicator/progress-indicator.css +1584 -0
  161. package/src/progress-indicator/progress-indicator.tokens.json +50 -0
  162. package/src/prompt-composer/prompt-composer.css +680 -0
  163. package/src/prompt-composer/prompt-composer.tokens.json +99 -0
  164. package/src/prose/prose.css +733 -0
  165. package/src/prose/prose.tokens.json +79 -0
  166. package/src/radio-group/radio-group.css +778 -0
  167. package/src/radio-group/radio-group.tokens.json +103 -0
  168. package/src/rating/rating.css +1071 -0
  169. package/src/rating/rating.tokens.json +88 -0
  170. package/src/result-state/result-state.css +980 -0
  171. package/src/result-state/result-state.tokens.json +63 -0
  172. package/src/runtime-properties.mjs +321 -0
  173. package/src/saved-views/saved-views.css +394 -0
  174. package/src/saved-views/saved-views.tokens.json +41 -0
  175. package/src/scroll-area/scroll-area.css +481 -0
  176. package/src/scroll-area/scroll-area.tokens.json +15 -0
  177. package/src/section/section.css +177 -0
  178. package/src/section/section.tokens.json +39 -0
  179. package/src/segmented-control/segmented-control.css +564 -0
  180. package/src/segmented-control/segmented-control.tokens.json +67 -0
  181. package/src/select/select.css +1593 -0
  182. package/src/select/select.tokens.json +110 -0
  183. package/src/side-navigation/side-navigation.css +1096 -0
  184. package/src/side-navigation/side-navigation.tokens.json +87 -0
  185. package/src/skeleton/skeleton.css +431 -0
  186. package/src/skeleton/skeleton.tokens.json +29 -0
  187. package/src/slider/slider.css +792 -0
  188. package/src/slider/slider.tokens.json +75 -0
  189. package/src/sparkline/sparkline.css +282 -0
  190. package/src/sparkline/sparkline.tokens.json +45 -0
  191. package/src/spinner/spinner.css +224 -0
  192. package/src/spinner/spinner.tokens.json +8 -0
  193. package/src/split-button/split-button.css +253 -0
  194. package/src/split-button/split-button.tokens.json +12 -0
  195. package/src/splitter/splitter.css +336 -0
  196. package/src/splitter/splitter.tokens.json +24 -0
  197. package/src/stack/stack.css +128 -0
  198. package/src/stack/stack.tokens.json +14 -0
  199. package/src/stat-card/stat-card.css +641 -0
  200. package/src/stat-card/stat-card.tokens.json +95 -0
  201. package/src/status-indicator/status-indicator.css +481 -0
  202. package/src/status-indicator/status-indicator.tokens.json +38 -0
  203. package/src/stepper/stepper.css +629 -0
  204. package/src/stepper/stepper.tokens.json +72 -0
  205. package/src/switch/switch.css +703 -0
  206. package/src/switch/switch.tokens.json +75 -0
  207. package/src/table/table.css +1482 -0
  208. package/src/table/table.tokens.json +69 -0
  209. package/src/table-of-contents/table-of-contents.css +533 -0
  210. package/src/table-of-contents/table-of-contents.tokens.json +53 -0
  211. package/src/tabs/tabs.css +998 -0
  212. package/src/tabs/tabs.tokens.json +82 -0
  213. package/src/tag/tag.css +768 -0
  214. package/src/tag/tag.tokens.json +85 -0
  215. package/src/tags-input/tags-input.css +757 -0
  216. package/src/tags-input/tags-input.tokens.json +84 -0
  217. package/src/text-field/text-field.css +900 -0
  218. package/src/text-field/text-field.tokens.json +87 -0
  219. package/src/textarea/textarea.css +365 -0
  220. package/src/textarea/textarea.tokens.json +59 -0
  221. package/src/time-field/time-field.css +719 -0
  222. package/src/time-field/time-field.tokens.json +86 -0
  223. package/src/time-picker/time-picker.css +665 -0
  224. package/src/time-picker/time-picker.tokens.json +66 -0
  225. package/src/time-range-picker/time-range-picker.css +1287 -0
  226. package/src/time-range-picker/time-range-picker.tokens.json +134 -0
  227. package/src/timeline/timeline.css +1596 -0
  228. package/src/timeline/timeline.tokens.json +96 -0
  229. package/src/toast/toast.css +943 -0
  230. package/src/toast/toast.tokens.json +90 -0
  231. package/src/toggle/toggle.css +290 -0
  232. package/src/toggle/toggle.tokens.json +54 -0
  233. package/src/toggle-group/toggle-group.css +463 -0
  234. package/src/toggle-group/toggle-group.tokens.json +54 -0
  235. package/src/toolbar/toolbar.css +434 -0
  236. package/src/toolbar/toolbar.tokens.json +49 -0
  237. package/src/tooltip/tooltip.css +368 -0
  238. package/src/tooltip/tooltip.tokens.json +39 -0
  239. package/src/top-navigation/top-navigation.css +1287 -0
  240. package/src/top-navigation/top-navigation.tokens.json +91 -0
  241. package/src/tour/tour.css +680 -0
  242. package/src/tour/tour.tokens.json +67 -0
  243. package/src/transfer/transfer.css +571 -0
  244. package/src/transfer/transfer.tokens.json +49 -0
  245. package/src/tree/tree.css +1000 -0
  246. package/src/tree/tree.tokens.json +80 -0
  247. package/src/typography/typography.css +211 -0
  248. package/src/typography/typography.tokens.json +31 -0
  249. package/src/upload/upload.css +1706 -0
  250. package/src/upload/upload.tokens.json +123 -0
  251. package/src/visually-hidden/visually-hidden.css +52 -0
  252. package/src/visually-hidden/visually-hidden.tokens.json +1 -0
  253. package/src/wizard/wizard.css +740 -0
  254. package/src/wizard/wizard.tokens.json +63 -0
@@ -0,0 +1,983 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── Two fields in one frame, a trigger, and a calendar behind them ─────
4
+ *
5
+ * ┌─ root ──────────────────────────────────────────────────────────────┐
6
+ * │ ┌─ control ── the frame ─────────────────────────────────────────┐ │
7
+ * │ │ ┌ start ─────┐ ┌ end ───────┐ │ │
8
+ * │ │ │ date-field │ – │ date-field │ ┌ clear ┐ ┌ trigger ┐ │ │
9
+ * │ │ └────────────┘ └────────────┘ └───────┘ └─────────┘ │ │
10
+ * │ └────────────────────────────────────────────────────────────────┘ │
11
+ * └─────────────────────────────────────────────────────────────────────┘
12
+ * ┌─ positioner ─ the layer ───────────────────────────────────┐
13
+ * │ ┌─ content ─ the surface ─────────────────────────────┐ │
14
+ * │ │ head: start – end · length · limit │ │
15
+ * │ │ presets │ calendar │ │
16
+ * │ │ footer │ │
17
+ * │ └─────────────────────────────────────────────────────┘ │
18
+ * └───────────────────────────────────────────────────────────┘
19
+ *
20
+ * The three families inside keep their own `data-ui`. Nothing here selects a
21
+ * segment or a day: this stylesheet owns the frame, the separator, the two
22
+ * buttons in it, the layer, and the band that says what the reader has.
23
+ *
24
+ * **The frame speaks to the two fields through their tokens and never
25
+ * through their parts.** A `date-field` in `variant="plain"` still draws its
26
+ * own border width, its own inline padding, its hover wash and its focus ring
27
+ * — each is a decision the field takes for the case where it stands alone.
28
+ * Inside this frame each of them is zeroed on the wrapper the field sits in,
29
+ * by restating the custom property the field reads, so the frame is the one
30
+ * box a reader sees and the field's stylesheet is not reached into.
31
+ */
32
+ [data-ui='date-range-picker'][data-part='root'] {
33
+ display: block;
34
+ /* The anchor the positioner measures is the control, and a block root
35
+ lets the frame take the width the form gave it. */
36
+ inline-size: 100%;
37
+ }
38
+
39
+ /* ── The frame ────────────────────────────────────────────────────────
40
+ *
41
+ * The same frame `date-field` draws for one date, drawn once for two: the
42
+ * control scales for height, type and gutter, and the field's own variant
43
+ * tokens for the ground, so a form holding a date and a range shows one
44
+ * frame twice rather than two frames.
45
+ */
46
+ [data-ui='date-range-picker'] [data-part='control'] {
47
+ --_font-size: var(--slotted-control-font-size-md, 13px);
48
+ --_height: var(--slotted-control-height-md, 34px);
49
+ --_padding-inline: var(--slotted-control-padding-inline-md, 13px);
50
+ --_gap: var(--slotted-control-gap-md, 8px);
51
+ --_icon-size: var(--slotted-control-icon-size-md, 16px);
52
+ --_border-width: var(--slotted-control-border-width, 1px);
53
+
54
+ /* The same inner-control box every family in the library draws: the glyph,
55
+ and one step of air on each side, bounded by the frame's own height. */
56
+ --_action-size: var(
57
+ --slotted-date-range-picker-action-size,
58
+ min(
59
+ calc(var(--_icon-size) + 2 * var(--slotted-space-2, 0.25rem)),
60
+ calc(var(--_height) - var(--_border-width) * 2 - var(--slotted-space-2, 0.25rem))
61
+ )
62
+ );
63
+
64
+ align-items: center;
65
+ background-color: var(
66
+ --slotted-date-range-picker-background,
67
+ var(--slotted-control-surface, Field)
68
+ );
69
+ border: var(--_border-width) solid
70
+ var(
71
+ --slotted-date-range-picker-border-color,
72
+ var(--slotted-control-border-color, ButtonBorder)
73
+ );
74
+ border-radius: var(--slotted-date-range-picker-radius, var(--slotted-control-radius, 4px));
75
+ box-shadow: var(--slotted-date-range-picker-shadow, var(--slotted-control-shadow, none));
76
+ box-sizing: border-box;
77
+ color: var(--slotted-date-range-picker-color, var(--slotted-text, CanvasText));
78
+ display: flex;
79
+ /* One line, whatever the frame's width. Two dates do not fit a frame
80
+ narrower than about 300 pixels, and the end used to drop to a second
81
+ line — a control two lines tall broke every row it stood in and read
82
+ as a different control from the field beside it. The two fields now
83
+ give way as `date-field` does, each clipping its own digits at its
84
+ edge, and the buttons at the reading end stay where they are because
85
+ they stand out of the flow. The field the reader steps into scrolls
86
+ its own digits into view. */
87
+ font-family: var(--slotted-control-font-family, system-ui, sans-serif);
88
+ font-size: var(--_font-size);
89
+ gap: var(--slotted-space-1, 0.125rem);
90
+ inline-size: 100%;
91
+ min-block-size: var(--_height);
92
+ min-inline-size: 0;
93
+ padding-inline: var(--_padding-inline);
94
+ position: relative;
95
+ transition:
96
+ background-color var(--slotted-control-transition-duration)
97
+ var(--slotted-control-transition-easing),
98
+ border-color var(--slotted-control-transition-duration)
99
+ var(--slotted-control-transition-easing),
100
+ box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
101
+ }
102
+
103
+ /**
104
+ * The two buttons stand out of the flow, at the reading end, and the frame
105
+ * keeps their room as padding. Two things follow. The buttons are inset by
106
+ * their own gutter less than the value is, so the glyph ends where the
107
+ * digits begin — the arithmetic `text-field` records. And when the frame is
108
+ * too narrow for two dates, the fields give way and the trigger does not:
109
+ * an in-flow trigger was the first thing to be pushed, and the guaranteed
110
+ * way in was the first thing lost.
111
+ */
112
+ [data-ui='date-range-picker'] [data-part='control'] {
113
+ --_action-inset: max(
114
+ 0px,
115
+ calc(var(--_padding-inline) - (var(--_action-size) - var(--_icon-size)) / 2)
116
+ );
117
+ --_actions: 1;
118
+
119
+ padding-inline-end: calc(
120
+ var(--_action-inset) + var(--_action-size) * var(--_actions) +
121
+ var(--slotted-space-1, 0.125rem) * (var(--_actions) - 1) + var(--slotted-space-2, 0.25rem)
122
+ );
123
+ }
124
+
125
+ [data-ui='date-range-picker'] [data-part='control']:has(> [data-part='clear']) {
126
+ --_actions: 2;
127
+ }
128
+
129
+ [data-ui='date-range-picker'][data-part='root'][data-size='sm'] [data-part='control'] {
130
+ --_font-size: var(--slotted-control-font-size-sm, 12px);
131
+ --_height: var(--slotted-control-height-sm, 30px);
132
+ --_padding-inline: var(--slotted-control-padding-inline-sm, 11px);
133
+ --_gap: var(--slotted-control-gap-sm, 6px);
134
+ --_icon-size: var(--slotted-control-icon-size-sm, 14px);
135
+ }
136
+
137
+ [data-ui='date-range-picker'][data-part='root'][data-size='lg'] [data-part='control'] {
138
+ --_font-size: var(--slotted-control-font-size-lg, 14px);
139
+ --_height: var(--slotted-control-height-lg, 38px);
140
+ --_padding-inline: var(--slotted-control-padding-inline-lg, 15px);
141
+ --_gap: var(--slotted-control-gap-lg, 10px);
142
+ --_icon-size: var(--slotted-control-icon-size-lg, 18px);
143
+ }
144
+
145
+ /* ── Variant ──────────────────────────────────────────────────────────
146
+ *
147
+ * The three steps `text-field` set, through the field's own variant tokens,
148
+ * so a theme that retunes `subtle` for one date retunes it for two.
149
+ */
150
+ [data-ui='date-range-picker'][data-part='root'][data-variant='subtle'] [data-part='control'] {
151
+ background-color: var(
152
+ --slotted-date-range-picker-subtle-background,
153
+ var(--slotted-surface-sunken, Canvas)
154
+ );
155
+ border-color: var(
156
+ --slotted-date-range-picker-subtle-border-color,
157
+ var(--slotted-border-subtle, ButtonBorder)
158
+ );
159
+ }
160
+
161
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain'] [data-part='control'] {
162
+ background-color: transparent;
163
+ border-color: transparent;
164
+ border-radius: var(--slotted-radius-none, 0px);
165
+ box-shadow: none;
166
+ }
167
+
168
+ /* ── The two ends ─────────────────────────────────────────────────────
169
+ *
170
+ * Each holds one `date-field` in `plain`, and speaks to it in the only
171
+ * language this stylesheet is allowed: the tokens the field reads. The
172
+ * field's border, inline padding, hover wash, invalid edge and focus ring
173
+ * are all zeroed here, and the frame answers each of them once for both.
174
+ */
175
+ [data-ui='date-range-picker'] [data-part='start'],
176
+ [data-ui='date-range-picker'] [data-part='end'] {
177
+ --slotted-control-border-width: 0px;
178
+ --slotted-control-padding-inline-sm: 0px;
179
+ --slotted-control-padding-inline-md: 0px;
180
+ --slotted-control-padding-inline-lg: 0px;
181
+ --slotted-control-height-sm: calc(var(--_height) - var(--_border-width) * 2);
182
+ --slotted-control-height-md: calc(var(--_height) - var(--_border-width) * 2);
183
+ --slotted-control-height-lg: calc(var(--_height) - var(--_border-width) * 2);
184
+ --slotted-date-field-plain-hover-background: transparent;
185
+ --slotted-date-field-invalid-border-color: transparent;
186
+ --slotted-date-field-shadow: none;
187
+ --slotted-control-shadow-focus-geometry: 0 0 0 0;
188
+ --slotted-focus-ring-width: 0px;
189
+
190
+ display: flex;
191
+ flex: 0 1 auto;
192
+ min-inline-size: 0;
193
+ position: relative;
194
+ }
195
+
196
+ /**
197
+ * The end being edited. `position` says which, and the mark is an underline
198
+ * at the frame's inner edge rather than a ring: a ring inside a ring is two
199
+ * rings, and at `dense` there is no room for two. It is painted only while
200
+ * the reader is in the control — the panel open, or focus inside the frame —
201
+ * because at rest there is no "next press" for it to name.
202
+ */
203
+ [data-ui='date-range-picker'] [data-part='start']::after,
204
+ [data-ui='date-range-picker'] [data-part='end']::after {
205
+ background-color: var(
206
+ --slotted-date-range-picker-current-color,
207
+ var(--slotted-tone-accent-solid)
208
+ );
209
+ block-size: var(--slotted-date-range-picker-current-size, 2px);
210
+ border-start-end-radius: var(--slotted-radius-xs, 2px);
211
+ border-start-start-radius: var(--slotted-radius-xs, 2px);
212
+ content: '';
213
+ inset-block-end: 0;
214
+ inset-inline: 0;
215
+ opacity: 0;
216
+ position: absolute;
217
+ transition: opacity var(--slotted-control-transition-duration)
218
+ var(--slotted-control-transition-easing);
219
+ }
220
+
221
+ [data-ui='date-range-picker'][data-part='root']:is(
222
+ [data-open],
223
+ :has([data-part='control']:focus-within)
224
+ )
225
+ :is([data-part='start'], [data-part='end'])[data-current]::after {
226
+ opacity: 1;
227
+ }
228
+
229
+ /* A dash between the two ends, in the adornment's ink: it annotates, and a
230
+ reader looking for either date should not have to step over it. */
231
+ [data-ui='date-range-picker'] [data-part='separator'] {
232
+ color: var(--slotted-date-range-picker-separator-color, var(--slotted-text-muted, GrayText));
233
+ flex: 0 0 auto;
234
+ padding-inline: var(--slotted-space-2, 0.25rem);
235
+ user-select: none;
236
+ }
237
+
238
+ /* ── The two buttons ──────────────────────────────────────────────────
239
+ *
240
+ * Clear and trigger take the inner-control box, at the reading end. The
241
+ * trigger is always drawn: ChromeOS turns `Alt`+`ArrowDown` into `PageDown`
242
+ * before the browser sees it, so the chord cannot be the only way in.
243
+ */
244
+ [data-ui='date-range-picker'] [data-part='clear'],
245
+ [data-ui='date-range-picker'] [data-part='trigger'] {
246
+ --slotted-icon-size: var(--_icon-size);
247
+
248
+ align-items: center;
249
+ appearance: none;
250
+ background-color: transparent;
251
+ block-size: var(--_action-size);
252
+ border: 0;
253
+ border-radius: var(--slotted-date-range-picker-radius, var(--slotted-control-radius, 4px));
254
+ color: var(--slotted-date-range-picker-action-color, var(--slotted-text-muted, GrayText));
255
+ cursor: pointer;
256
+ display: inline-flex;
257
+ flex: 0 0 auto;
258
+ inline-size: var(--_action-size);
259
+ /* Centred on the first line of the frame, whichever line the end is on. */
260
+ inset-block-start: calc((var(--_height) - var(--_border-width) * 2 - var(--_action-size)) / 2);
261
+ inset-inline-end: var(--_action-inset);
262
+ justify-content: center;
263
+ padding: 0;
264
+ position: absolute;
265
+ touch-action: manipulation;
266
+ transition:
267
+ background-color var(--slotted-control-transition-duration)
268
+ var(--slotted-control-transition-easing),
269
+ color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
270
+ -webkit-tap-highlight-color: transparent;
271
+ }
272
+
273
+ [data-ui='date-range-picker'] [data-part='clear'] {
274
+ inset-inline-end: calc(
275
+ var(--_action-inset) + var(--_action-size) + var(--slotted-space-1, 0.125rem)
276
+ );
277
+ }
278
+
279
+ /* The hit area, recovered outside the paint, exactly as every other inner
280
+ control in the library recovers it. */
281
+ [data-ui='date-range-picker'] [data-part='clear']::after,
282
+ [data-ui='date-range-picker'] [data-part='trigger']::after {
283
+ content: '';
284
+ inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
285
+ position: absolute;
286
+ }
287
+
288
+ /* On a finger the target is the comfortable size in both axes and the box
289
+ is the one a mouse sees: the ground and the focus ring stay square, and
290
+ the hit area above grows to 44 pixels around them. Side by side, neither
291
+ button reaches across the edge the two share. */
292
+ @media (pointer: coarse) {
293
+ [data-ui='date-range-picker'] :is([data-part='clear'], [data-part='trigger'])::after {
294
+ inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
295
+ }
296
+
297
+ [data-ui='date-range-picker']
298
+ [data-part='clear']:has(+ [data-part='trigger']:not([hidden]))::after {
299
+ inset-inline-end: 0;
300
+ }
301
+
302
+ [data-ui='date-range-picker'] [data-part='clear']:not([hidden]) + [data-part='trigger']::after {
303
+ inset-inline-start: 0;
304
+ }
305
+ }
306
+
307
+ /* The cross and the calendar are `Glyph` elements the binding draws, at the
308
+ field's icon step. The calendar does not turn and does not become a
309
+ chevron while the surface is open: what opens is a month, not a list, and
310
+ the trigger goes on saying what kind of value it holds. */
311
+
312
+ [data-ui='date-range-picker'] [data-part='clear']:hover:not(:disabled):not([data-disabled]),
313
+ [data-ui='date-range-picker'] [data-part='trigger']:hover:not(:disabled):not([data-disabled]) {
314
+ background-color: var(
315
+ --slotted-date-range-picker-action-hover-background,
316
+ var(--slotted-tone-secondary-subtle-hover)
317
+ );
318
+ }
319
+
320
+ [data-ui='date-range-picker'] [data-part='clear']:active:not(:disabled):not([data-disabled]),
321
+ [data-ui='date-range-picker'] [data-part='trigger']:active:not(:disabled):not([data-disabled]) {
322
+ background-color: var(
323
+ --slotted-date-range-picker-action-active-background,
324
+ var(--slotted-tone-secondary-subtle-active)
325
+ );
326
+ }
327
+
328
+ /* Their own rings: tabbing from the last segment to a button has to move
329
+ something visible, and the frame's ring would sit still. */
330
+ [data-ui='date-range-picker'] [data-part='clear']:focus-visible,
331
+ [data-ui='date-range-picker'] [data-part='trigger']:focus-visible {
332
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
333
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
334
+ }
335
+
336
+ [data-ui='date-range-picker'] [data-part='clear']:disabled,
337
+ [data-ui='date-range-picker'] [data-part='trigger']:disabled {
338
+ color: var(--slotted-disabled-foreground);
339
+ cursor: not-allowed;
340
+ }
341
+
342
+ /* ── States on the frame ──────────────────────────────────────────────
343
+ *
344
+ * Written strongest last, and each weaker one guarded against every stronger
345
+ * one it can meet, which is the order `STATE_PRECEDENCE` sets.
346
+ */
347
+
348
+ [data-ui='date-range-picker'][data-part='root']:not([data-disabled]):not([data-readonly]):not(
349
+ [data-invalid]
350
+ )
351
+ [data-part='control']:hover {
352
+ border-color: var(
353
+ --slotted-date-range-picker-hover-border-color,
354
+ var(--slotted-tone-accent-border)
355
+ );
356
+ }
357
+
358
+ [data-ui='date-range-picker'][data-part='root'][data-variant='subtle']:not([data-disabled]):not(
359
+ [data-readonly]
360
+ ):not([data-invalid])
361
+ [data-part='control']:hover {
362
+ background-color: var(
363
+ --slotted-date-range-picker-subtle-hover-background,
364
+ var(--slotted-tone-secondary-subtle-hover)
365
+ );
366
+ }
367
+
368
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain']:not([data-disabled]):not(
369
+ [data-readonly]
370
+ ):not([data-invalid])
371
+ [data-part='control']:hover {
372
+ background-color: var(
373
+ --slotted-date-range-picker-plain-hover-background,
374
+ var(--slotted-tone-secondary-subtle-hover)
375
+ );
376
+ border-color: transparent;
377
+ }
378
+
379
+ /**
380
+ * The frame's ring says the control has the reader's attention, once for
381
+ * both fields; the band on one segment — the field's own — says which of
382
+ * the six a key will move. The two buttons in the frame draw their own ring
383
+ * and are excluded here, so a focused trigger is not two rings.
384
+ *
385
+ * `:focus-within` and not `:focus-visible`, for the reason `date-field`
386
+ * records: every part of a segmented field is a `div`, and a browser refuses
387
+ * `:focus-visible` on a `div` a pointer focused.
388
+ */
389
+ [data-ui='date-range-picker']
390
+ [data-part='control']:focus-within:not(
391
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
392
+ ) {
393
+ box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
394
+ var(--slotted-focus-ring-color, Highlight);
395
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
396
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
397
+ }
398
+
399
+ /**
400
+ * The ring of a control whose box is not its own is drawn inside that box,
401
+ * the way the inner controls draw theirs inside the frame: two pixels past
402
+ * the control is two pixels over the next row's rule. No lift either — a
403
+ * shadow that appears only under focus is a cell that changes size when it
404
+ * is used.
405
+ */
406
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain']
407
+ [data-part='control']:focus-within:not(
408
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
409
+ ) {
410
+ box-shadow: none;
411
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
412
+ }
413
+
414
+ /* Invalid outranks hover and is written after it. The ring is retargeted
415
+ rather than repainted, so the frame's outline and the ring on the two
416
+ buttons all move at once. */
417
+ [data-ui='date-range-picker'][data-part='root'][data-invalid] {
418
+ --slotted-focus-ring-color: var(
419
+ --slotted-date-range-picker-invalid-focus-ring-color,
420
+ var(--slotted-focus-ring-color-invalid, Highlight)
421
+ );
422
+ }
423
+
424
+ [data-ui='date-range-picker'][data-part='root'][data-invalid] [data-part='control'] {
425
+ --_invalid-edge-width: var(
426
+ --slotted-date-range-picker-invalid-edge-width,
427
+ var(--slotted-control-invalid-edge-width, 1px)
428
+ );
429
+ --_invalid-edge-color: var(
430
+ --slotted-date-range-picker-invalid-border-color,
431
+ var(--slotted-tone-danger-border)
432
+ );
433
+ --_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
434
+
435
+ border-color: var(
436
+ --slotted-date-range-picker-invalid-border-color,
437
+ var(--slotted-tone-danger-border)
438
+ );
439
+ /*
440
+ * Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
441
+ * `high-contrast` the danger border is the resting edge's black. The line
442
+ * is a shadow inside the border, so neither box nor text moves, and it
443
+ * takes the resting lift's place.
444
+ */
445
+ box-shadow: var(--_invalid-edge);
446
+ }
447
+
448
+ [data-ui='date-range-picker'][data-invalid]
449
+ [data-part='control']:focus-within:not(
450
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
451
+ ) {
452
+ box-shadow:
453
+ var(--_invalid-edge),
454
+ var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
455
+ var(--slotted-focus-ring-color, Highlight);
456
+ }
457
+
458
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain'][data-invalid]
459
+ [data-part='control']:focus-within:not(
460
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
461
+ ) {
462
+ /* The ring lies inside the box here, over the border and an edge as wide as
463
+ it; the edge reaches past the ring. */
464
+ box-shadow: inset 0 0 0
465
+ calc(
466
+ var(--slotted-focus-ring-offset, 2px) - var(--_border-width) + var(--_invalid-edge-width)
467
+ )
468
+ var(--_invalid-edge-color);
469
+ }
470
+
471
+ /*
472
+ * Forced colours remove every box shadow, and the edge with them. The
473
+ * width comes back as an outline inside the border, and a focused invalid
474
+ * control draws its ring double, which a valid ring is not.
475
+ */
476
+ @media (forced-colors: active) {
477
+ [data-ui='date-range-picker'][data-part='root'][data-invalid] [data-part='control'] {
478
+ outline: var(--_invalid-edge-width) solid CanvasText;
479
+ outline-offset: calc(-1 * (var(--_border-width) + var(--_invalid-edge-width)));
480
+ }
481
+
482
+ [data-ui='date-range-picker'][data-invalid]
483
+ [data-part='control']:focus-within:not(
484
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
485
+ ) {
486
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
487
+ var(--slotted-focus-ring-color, Highlight);
488
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
489
+ }
490
+
491
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain'][data-invalid]
492
+ [data-part='control']:focus-within:not(
493
+ :has(> [data-part='clear']:focus, > [data-part='trigger']:focus)
494
+ ) {
495
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
496
+ var(--slotted-focus-ring-color, Highlight);
497
+ outline-offset: calc(
498
+ -1 * (var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width))
499
+ );
500
+ }
501
+ }
502
+
503
+ /* Read-only stays focusable, readable and copyable, and must not be
504
+ mistakable for disabled: the ground says "not yours to change". */
505
+ [data-ui='date-range-picker'][data-part='root'][data-readonly] [data-part='control'] {
506
+ background-color: var(
507
+ --slotted-date-range-picker-readonly-background,
508
+ var(--slotted-tone-secondary-subtle-hover)
509
+ );
510
+ cursor: default;
511
+ }
512
+
513
+ /* Muted to the colour the theme measures rather than faded by opacity: a
514
+ disabled form still has to say what it holds. */
515
+ [data-ui='date-range-picker'][data-part='root'][data-disabled] [data-part='control'] {
516
+ background-color: var(
517
+ --slotted-date-range-picker-disabled-background,
518
+ var(--slotted-disabled-background)
519
+ );
520
+ border-color: var(
521
+ --slotted-date-range-picker-disabled-border-color,
522
+ var(--slotted-disabled-border)
523
+ );
524
+ box-shadow: none;
525
+ color: var(--slotted-date-range-picker-disabled-color, var(--slotted-disabled-foreground));
526
+ cursor: not-allowed;
527
+ }
528
+
529
+ [data-ui='date-range-picker'][data-part='root'][data-disabled] [data-part='separator'] {
530
+ color: var(--slotted-disabled-foreground);
531
+ }
532
+
533
+ /* ── The layer ────────────────────────────────────────────────────────
534
+ *
535
+ * What `packages/core/src/positioner` writes `position`, `left` and `top`
536
+ * onto. The painted surface is one element in, as every overlay is built.
537
+ */
538
+ [data-ui='date-range-picker'][data-part='positioner'] {
539
+ /* Only the block inset: the positioner writes `left` and never `right`,
540
+ and `inset-inline-start` would resolve to `right` in a right-to-left
541
+ document and win over the measured value. */
542
+ position: absolute;
543
+ inset-block-start: 0;
544
+
545
+ box-sizing: border-box;
546
+ isolation: isolate;
547
+ /* The layer is only a frame around the surface; the surface takes the
548
+ pointer, and the frame must not swallow a press meant for the page. */
549
+ pointer-events: none;
550
+ z-index: calc(var(--slotted-z-index-popover, 1100) + var(--slotted-layer-depth, 0));
551
+
552
+ /* The layer arrives along the axis it opens on and never along the inline
553
+ one, which is the rule `select` records. */
554
+ --_enter-distance: var(--slotted-space-2, 0.25rem);
555
+ opacity: 0;
556
+ translate: 0 calc(-1 * var(--_enter-distance));
557
+ transition:
558
+ opacity var(--slotted-duration-short) var(--slotted-easing-exit),
559
+ translate var(--slotted-duration-short) var(--slotted-easing-exit);
560
+ }
561
+
562
+ [data-ui='date-range-picker'][data-part='positioner'][data-side='top'] {
563
+ translate: 0 var(--_enter-distance);
564
+ }
565
+
566
+ /* A lateral side has no block direction to arrive from, so it only fades. */
567
+ [data-ui='date-range-picker'][data-part='positioner']:is(
568
+ [data-side='left'],
569
+ [data-side='right']
570
+ ) {
571
+ translate: 0 0;
572
+ }
573
+
574
+ [data-ui='date-range-picker'][data-part='positioner'][data-open] {
575
+ opacity: 1;
576
+ translate: 0 0;
577
+ transition: none;
578
+ }
579
+
580
+ /* The entrance is a keyframe animation and not a transition, for the reason
581
+ `select` records: a transition starts from the style the browser computed
582
+ last, and the positioner writes `data-side` in the same style update as
583
+ the family opens the layer. Only a layer the positioner placed arrives. */
584
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned] {
585
+ animation: slotted-date-range-picker-enter-down var(--slotted-duration-medium)
586
+ var(--slotted-easing-enter);
587
+ }
588
+
589
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned][data-side='top'] {
590
+ animation-name: slotted-date-range-picker-enter-up;
591
+ }
592
+
593
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned]:is(
594
+ [data-side='left'],
595
+ [data-side='right']
596
+ ) {
597
+ animation-name: slotted-date-range-picker-fade;
598
+ }
599
+
600
+ @keyframes slotted-date-range-picker-enter-down {
601
+ from {
602
+ opacity: 0;
603
+ translate: 0 calc(-1 * var(--_enter-distance));
604
+ }
605
+ }
606
+
607
+ @keyframes slotted-date-range-picker-enter-up {
608
+ from {
609
+ opacity: 0;
610
+ translate: 0 var(--_enter-distance);
611
+ }
612
+ }
613
+
614
+ @keyframes slotted-date-range-picker-fade {
615
+ from {
616
+ opacity: 0;
617
+ }
618
+ }
619
+
620
+ /* Stillness keeps the fade and drops the movement. */
621
+ @media (prefers-reduced-motion: reduce) {
622
+ [data-ui='date-range-picker'][data-part='positioner'],
623
+ [data-ui='date-range-picker'][data-part='positioner'][data-side='top'],
624
+ [data-ui='date-range-picker'][data-part='positioner'][data-side='left'],
625
+ [data-ui='date-range-picker'][data-part='positioner'][data-side='right'] {
626
+ translate: 0 0;
627
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
628
+ }
629
+
630
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned],
631
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned][data-side='top'],
632
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned][data-side='left'],
633
+ [data-ui='date-range-picker'][data-part='positioner'][data-open][data-positioned][data-side='right'] {
634
+ animation-name: slotted-date-range-picker-fade;
635
+ }
636
+ }
637
+
638
+ /* The anchor scrolled out of its own clipper. The calendar is still open and
639
+ still belongs to a control the reader can no longer see. */
640
+ [data-ui='date-range-picker'][data-part='positioner'][data-detached] {
641
+ visibility: hidden;
642
+ }
643
+
644
+ /**
645
+ * The surface. It is sized by the calendar inside it rather than by the
646
+ * control it hangs from, and it is a grid of `auto` tracks for the reason
647
+ * `date-picker` records: a percentage flex basis against a shrink-wrapped
648
+ * container makes the surface wider than anything in it.
649
+ */
650
+ [data-ui='date-range-picker'] [data-part='content'] {
651
+ background-color: var(
652
+ --slotted-date-range-picker-surface,
653
+ var(--slotted-surface-raised, Canvas)
654
+ );
655
+ border: var(--slotted-border-width-thin, 1px) solid
656
+ var(
657
+ --slotted-date-range-picker-content-border-color,
658
+ var(--slotted-border-subtle, ButtonBorder)
659
+ );
660
+ border-radius: var(--slotted-date-range-picker-content-radius, var(--slotted-radius-lg, 8px));
661
+ box-shadow: var(--slotted-date-range-picker-content-shadow, var(--slotted-shadow-md));
662
+ box-sizing: border-box;
663
+ color: var(--slotted-date-range-picker-content-color, var(--slotted-text, CanvasText));
664
+ display: grid;
665
+ gap: var(--slotted-space-5, 0.75rem);
666
+ grid-template-areas: 'head' 'panels' 'footer';
667
+ /* `minmax(0, 1fr)` and not `auto`: an `auto` track takes the calendar's
668
+ own two-month width as its floor, and the cap below is then a cap on a
669
+ box the track has already overflowed. A zero floor lets the track be
670
+ the room there is, and the calendar's `max-inline-size: 100%` follows. */
671
+ grid-template-columns: minmax(0, 1fr);
672
+ /* Capped by the room the positioner measured, so two months that do not
673
+ fit beside each other wrap under one another — the calendar's own
674
+ container rule — instead of running past the edge of the screen. */
675
+ max-inline-size: var(--slotted-positioner-available-width, 100vw);
676
+ padding: var(--slotted-date-range-picker-content-padding, var(--slotted-space-5, 0.75rem));
677
+ /* The layer above gave up the pointer; the surface takes it back. */
678
+ pointer-events: auto;
679
+ }
680
+
681
+ /**
682
+ * The shortcuts and the calendar, as a wrapping row. Where there is room
683
+ * the column of shortcuts stands beside the grid; where there is not, the
684
+ * grid drops under it and the column stretches into a list across the
685
+ * surface — no container query, because a surface sized by its contents
686
+ * has no width of its own to ask about, which is the rule `datetime-picker`
687
+ * records for its own two panels.
688
+ */
689
+ [data-ui='date-range-picker'] [data-part='panels'] {
690
+ align-items: flex-start;
691
+ display: flex;
692
+ flex-wrap: wrap;
693
+ gap: var(--slotted-space-5, 0.75rem);
694
+ grid-area: panels;
695
+ min-inline-size: 0;
696
+ }
697
+
698
+ [data-ui='date-range-picker'] [data-part='panels'] > [data-ui='calendar'] {
699
+ flex: 0 1 auto;
700
+ }
701
+
702
+ /* ── The band ─────────────────────────────────────────────────────────
703
+ *
704
+ * What the reader has so far, and how long it is. The two ends are drawn
705
+ * as they stand, the one not yet chosen as its placeholder, and the length
706
+ * answers the question the reader is actually asking while the run is
707
+ * being drawn — the reason `docs/history/designs/2026-09-09-date-design.md`
708
+ * §12 records.
709
+ *
710
+ * It renders at the panel's width and contributes nothing to it, by the
711
+ * pair `datetime-picker` records: `inline-size: 0` takes the band out of the
712
+ * grid's measurement and `min-inline-size: 100%` puts it back at the track's
713
+ * own width, so the calendar decides how wide the panel is and the band
714
+ * wraps inside whatever it decides.
715
+ */
716
+ [data-ui='date-range-picker'] [data-part='head'] {
717
+ box-sizing: border-box;
718
+ inline-size: 0;
719
+ min-inline-size: 100%;
720
+
721
+ align-items: baseline;
722
+ border-block-end: var(--slotted-border-width-thin, 1px) solid
723
+ var(
724
+ --slotted-date-range-picker-content-border-color,
725
+ var(--slotted-border-subtle, ButtonBorder)
726
+ );
727
+ column-gap: var(--slotted-space-2, 0.25rem);
728
+ display: flex;
729
+ flex-wrap: wrap;
730
+ grid-area: head;
731
+ /* Air under the words before the rule — the same step the footer keeps
732
+ between its rule and its buttons, so the two bands hold the surface
733
+ symmetrically. */
734
+ padding-block-end: var(--slotted-space-3, 0.375rem);
735
+ row-gap: var(--slotted-space-1, 0.125rem);
736
+ }
737
+
738
+ /* The two ends and the dash between them are one run, and the run does not
739
+ break inside itself: "12 Sep –" on one line and "16 Sep" on the next is a
740
+ range read twice. */
741
+ [data-ui='date-range-picker'] [data-part='head'] > [data-part='head-start'],
742
+ [data-ui='date-range-picker'] [data-part='head'] > [data-part='head-end'] {
743
+ color: var(--slotted-date-range-picker-content-color, var(--slotted-text, CanvasText));
744
+ font-size: var(--slotted-font-size-sm, 13px);
745
+ font-variant-numeric: tabular-nums;
746
+ font-weight: var(--slotted-font-weight-medium, 500);
747
+ /* A date in words is a mixed run — a month name beside numbers — so it is
748
+ laid out by its own content rather than by the paragraph. */
749
+ unicode-bidi: plaintext;
750
+ white-space: nowrap;
751
+ }
752
+
753
+ /* The dash between the two ends is a text node the bindings write between
754
+ the two spans, and not a pseudo-element on either: each span lays its own
755
+ date out by that date's first strong character, and a pseudo-element on
756
+ one of them followed the date's direction rather than the band's. As a
757
+ flex item of its own the dash stands between the two ends in the band's
758
+ direction, which is the only direction it has. */
759
+
760
+ /* The end not yet written, drawn as its placeholder beside the one that is;
761
+ and the pending one, which is a proposal and reads as the band's own
762
+ proposal in the calendar does. */
763
+ [data-ui='date-range-picker']
764
+ [data-part='head']
765
+ :is([data-part='head-start'], [data-part='head-end'])[data-placeholder] {
766
+ color: var(--slotted-date-range-picker-placeholder-color, var(--slotted-text-muted, GrayText));
767
+ font-weight: var(--slotted-font-weight-regular, 400);
768
+ }
769
+
770
+ [data-ui='date-range-picker']
771
+ [data-part='head']
772
+ :is([data-part='head-start'], [data-part='head-end'])[data-pending] {
773
+ color: var(--slotted-date-range-picker-pending-color, var(--slotted-tone-accent-text));
774
+ }
775
+
776
+ /**
777
+ * The length. It stands at the reading end of the band, in the accent's
778
+ * text tone, because it is the number the band exists to say; pending, it
779
+ * keeps the tone and drops a step of weight, the way the calendar's pending
780
+ * band keeps the tint and drops a step of share.
781
+ */
782
+ [data-ui='date-range-picker'] [data-part='length'] {
783
+ color: var(--slotted-date-range-picker-length-color, var(--slotted-tone-accent-text));
784
+ font-size: var(--slotted-font-size-sm, 13px);
785
+ font-variant-numeric: tabular-nums;
786
+ font-weight: var(--slotted-font-weight-semibold, 600);
787
+ margin-inline-start: auto;
788
+ padding-inline-start: var(--slotted-space-4, 0.5rem);
789
+ /* "10 days" is a number and a word, and a right-to-left band would put
790
+ the word first; the run is laid out by its own first strong character. */
791
+ unicode-bidi: plaintext;
792
+ white-space: nowrap;
793
+ }
794
+
795
+ [data-ui='date-range-picker'] [data-part='length'][data-pending] {
796
+ font-weight: var(--slotted-font-weight-medium, 500);
797
+ }
798
+
799
+ /* An empty length still holds its line, so the band does not grow a row
800
+ when the first end is chosen. */
801
+ [data-ui='date-range-picker'] [data-part='length']:empty::before {
802
+ content: '\200B';
803
+ }
804
+
805
+ /* The limit, on a row of its own under the ends: a rule the grid enforces
806
+ is a rule the band names. */
807
+ [data-ui='date-range-picker'] [data-part='limit'] {
808
+ color: var(--slotted-date-range-picker-limit-color, var(--slotted-text-muted, GrayText));
809
+ flex-basis: 100%;
810
+ font-size: var(--slotted-font-size-xs, 11px);
811
+ unicode-bidi: plaintext;
812
+ }
813
+
814
+ /* ── The shortcuts ────────────────────────────────────────────────────
815
+ *
816
+ * This family ships no list of them: "last 7 days" is a business calendar,
817
+ * and a library that guessed at one would be guessing at a fiscal year. It
818
+ * ships the column they stand in and the button each one is.
819
+ */
820
+ [data-ui='date-range-picker'] [data-part='presets'] {
821
+ align-items: stretch;
822
+ display: flex;
823
+ /* It grows: beside the grid it takes what the surface leaves, and alone
824
+ on a line — the grid having dropped under it — it becomes a list across
825
+ the surface rather than a column against one edge. No rule between the
826
+ two: a rule on the end edge is right beside the grid and wrong above
827
+ it, and the column cannot know which it is. */
828
+ flex: 1 0 auto;
829
+ flex-direction: column;
830
+ gap: var(--slotted-space-1, 0.125rem);
831
+ }
832
+
833
+ /**
834
+ * One shortcut. Quiet — no edge, the surface's ink — and reading from the
835
+ * start edge: stretched and centred, four labels form a ragged column with
836
+ * no left edge to run down. Selected, it takes the accent's subtle ground
837
+ * and text, which is the pair `calendar` gives a day inside the run.
838
+ */
839
+ [data-ui='date-range-picker'] [data-part='preset'] {
840
+ appearance: none;
841
+ background-color: transparent;
842
+ border: 0;
843
+ border-radius: var(--slotted-date-range-picker-radius, var(--slotted-control-radius, 4px));
844
+ color: var(--slotted-date-range-picker-content-color, var(--slotted-text, CanvasText));
845
+ cursor: pointer;
846
+ font-family: var(--slotted-control-font-family, system-ui, sans-serif);
847
+ font-size: var(--slotted-font-size-sm, 13px);
848
+ line-height: var(--slotted-line-height-sm, 1.4);
849
+ min-block-size: var(--slotted-target-min, 24px);
850
+ padding-block: var(--slotted-space-2, 0.25rem);
851
+ padding-inline: var(--slotted-space-3, 0.375rem);
852
+ text-align: start;
853
+ touch-action: manipulation;
854
+ transition:
855
+ background-color var(--slotted-control-transition-duration)
856
+ var(--slotted-control-transition-easing),
857
+ color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
858
+ white-space: nowrap;
859
+ -webkit-tap-highlight-color: transparent;
860
+ }
861
+
862
+ [data-ui='date-range-picker'] [data-part='preset']:hover:not(:disabled):not([data-selected]) {
863
+ background-color: var(
864
+ --slotted-date-range-picker-preset-hover-background,
865
+ var(--slotted-tone-secondary-subtle-hover)
866
+ );
867
+ }
868
+
869
+ [data-ui='date-range-picker'] [data-part='preset']:active:not(:disabled):not([data-selected]) {
870
+ background-color: var(
871
+ --slotted-date-range-picker-preset-active-background,
872
+ var(--slotted-tone-secondary-subtle-active)
873
+ );
874
+ }
875
+
876
+ [data-ui='date-range-picker'] [data-part='preset'][data-selected] {
877
+ background-color: var(
878
+ --slotted-date-range-picker-preset-selected-background,
879
+ var(--slotted-tone-accent-subtle-hover)
880
+ );
881
+ color: var(--slotted-date-range-picker-preset-selected-color, var(--slotted-tone-accent-text));
882
+ font-weight: var(--slotted-font-weight-medium, 500);
883
+ }
884
+
885
+ [data-ui='date-range-picker'] [data-part='preset']:focus-visible {
886
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
887
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
888
+ }
889
+
890
+ [data-ui='date-range-picker'] [data-part='preset']:disabled {
891
+ color: var(--slotted-disabled-foreground);
892
+ cursor: not-allowed;
893
+ }
894
+
895
+ [data-ui='date-range-picker'] [data-part='footer'] {
896
+ box-sizing: border-box;
897
+ inline-size: 0;
898
+ min-inline-size: 100%;
899
+
900
+ align-items: center;
901
+ border-block-start: var(--slotted-border-width-thin, 1px) solid
902
+ var(
903
+ --slotted-date-range-picker-content-border-color,
904
+ var(--slotted-border-subtle, ButtonBorder)
905
+ );
906
+ display: flex;
907
+ gap: var(--slotted-space-2, 0.25rem);
908
+ grid-area: footer;
909
+ justify-content: flex-end;
910
+ padding-block-start: var(--slotted-space-4, 0.5rem);
911
+ }
912
+
913
+ /* ── Narrow, on a finger ──────────────────────────────────────────────
914
+ *
915
+ * §2.7 of `docs/component-features.md` asks for it by name: a popover on the
916
+ * desktop and a sheet in a narrow viewport. The DOM does not change, so the
917
+ * decision is testable without a device.
918
+ */
919
+ @media (max-width: 480px) and (pointer: coarse) {
920
+ [data-ui='date-range-picker'][data-part='positioner'][data-presentation='sheet'] {
921
+ /* A sheet is not placed against an anchor, it is placed against the
922
+ screen. */
923
+ inset: auto 0 0;
924
+ position: fixed;
925
+ translate: 0 var(--slotted-space-9, 2rem);
926
+ }
927
+
928
+ [data-ui='date-range-picker'][data-part='positioner'][data-presentation='sheet'][data-open] {
929
+ translate: 0 0;
930
+ }
931
+
932
+ [data-ui='date-range-picker'][data-part='positioner'][data-presentation='sheet']
933
+ [data-part='content'] {
934
+ border-end-end-radius: 0;
935
+ border-end-start-radius: 0;
936
+ border-inline: 0;
937
+ border-block-end: 0;
938
+ inline-size: var(--slotted-date-range-picker-sheet-inline-size, 100%);
939
+ max-inline-size: none;
940
+ /* Two months wider than the screen would scroll sideways; the grid
941
+ inside answers its own width and drops to one column instead. */
942
+ justify-content: center;
943
+ }
944
+
945
+ [data-ui='date-range-picker'][data-part='positioner'][data-presentation='sheet']
946
+ [data-part='presets'] {
947
+ /* Beside the grid there is no room for a column of shortcuts, so they
948
+ become a row above it and keep their labels. */
949
+ flex-direction: row;
950
+ flex-wrap: wrap;
951
+ }
952
+ }
953
+
954
+ @media (forced-colors: active) {
955
+ /* A transparent border has no system colour to be remapped to, so the
956
+ `plain` frame would lose its only edge where an edge matters most. */
957
+ [data-ui='date-range-picker'][data-part='root'][data-variant='plain'] [data-part='control'] {
958
+ border-color: ButtonBorder;
959
+ }
960
+
961
+ /* The underline is painted with a background, and forced colours replace
962
+ a background that is not one of the system's own. */
963
+ [data-ui='date-range-picker'] [data-part='start']::after,
964
+ [data-ui='date-range-picker'] [data-part='end']::after {
965
+ forced-color-adjust: none;
966
+ }
967
+
968
+ /* The underline keeps its ink as the text's own: a tone has nothing to
969
+ map to here, and `currentcolor` is whatever the system gave the frame. */
970
+ [data-ui='date-range-picker'] [data-part='start']::after,
971
+ [data-ui='date-range-picker'] [data-part='end']::after {
972
+ background-color: currentcolor;
973
+ }
974
+
975
+ /* A tinted ground has nothing to tint in a palette the system replaced;
976
+ the selected shortcut takes the pair the system keeps legible. */
977
+ [data-ui='date-range-picker'] [data-part='preset'][data-selected] {
978
+ background-color: Highlight;
979
+ color: HighlightText;
980
+ forced-color-adjust: none;
981
+ }
982
+ }
983
+ }