@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,719 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── The segmented time control ─────────────────────────────────────────
4
+ *
5
+ * The frame `date-field` draws, holding hour, minute, second and day period
6
+ * instead of year, month and day. The frame carries no role: `role="group"`
7
+ * is on the row, because the APG's spin-button example names the group as
8
+ * what tells a screen-reader user that the buttons are one time.
9
+ *
10
+ * There is no `input[type=time]` under any of it, for the reason
11
+ * `date-field` refuses `input[type=date]`: the browser's own control cannot
12
+ * be styled past its closed state, its panel is drawn by the operating
13
+ * system and answers no scheme, no density and no direction, and its field
14
+ * order is the browser's locale rather than the application's. The value
15
+ * reaches a form through a hidden input carrying `HH:mm:ss`.
16
+ */
17
+ [data-ui='time-field'][data-part='root'] {
18
+ /**
19
+ * Type, height, inline padding, gap and glyph size come from the control
20
+ * scales — the only ones the theme restates per density — so a time in a
21
+ * dense table shrinks whole rather than in parts.
22
+ */
23
+ --_font-size: var(--slotted-control-font-size-md, 13px);
24
+ --_height: var(--slotted-control-height-md, 34px);
25
+ --_padding-inline: var(--slotted-control-padding-inline-md, 13px);
26
+ --_gap: var(--slotted-control-gap-md, 8px);
27
+ --_icon-floor: var(--slotted-time-field-icon-size-min, 14px);
28
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
29
+
30
+ /* The same inner-control box every family in the library draws: the glyph,
31
+ and one step of air on each side, bounded by the frame's own height. */
32
+ --_action-size: var(
33
+ --slotted-time-field-action-size,
34
+ min(
35
+ calc(var(--_icon-size) + 2 * var(--slotted-space-2, 0.25rem)),
36
+ calc(
37
+ var(--_height) - var(--slotted-control-border-width, 1px) * 2 -
38
+ var(--slotted-space-2, 0.25rem)
39
+ )
40
+ )
41
+ );
42
+
43
+ align-items: center;
44
+ background-color: var(--slotted-time-field-background, var(--slotted-control-surface, Field));
45
+ border: var(--slotted-control-border-width, 1px) solid
46
+ var(--slotted-time-field-border-color, var(--slotted-control-border-color, ButtonBorder));
47
+ border-radius: var(--slotted-time-field-radius, var(--slotted-control-radius, 4px));
48
+ box-shadow: var(--slotted-time-field-shadow, var(--slotted-control-shadow, none));
49
+ box-sizing: border-box;
50
+ color: var(--slotted-time-field-color, var(--slotted-text, CanvasText));
51
+ display: flex;
52
+ font-family: var(--slotted-control-font-family, system-ui, sans-serif);
53
+ font-size: var(--_font-size);
54
+ gap: var(--_gap);
55
+ inline-size: 100%;
56
+ min-block-size: var(--_height);
57
+ min-inline-size: 0;
58
+ padding-inline: var(--_padding-inline);
59
+ transition:
60
+ background-color var(--slotted-control-transition-duration)
61
+ var(--slotted-control-transition-easing),
62
+ border-color var(--slotted-control-transition-duration)
63
+ var(--slotted-control-transition-easing),
64
+ box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
65
+ }
66
+
67
+ /**
68
+ * The button inside the frame is inset by its own gutter less than the value
69
+ * is, so the *glyph* ends the same distance from the border as the digits
70
+ * begin from it — the arithmetic `text-field` records, holding at every step
71
+ * and every density.
72
+ */
73
+ [data-ui='time-field'][data-part='root']:has(> [data-part='clear']:last-child) {
74
+ padding-inline-end: max(
75
+ 0px,
76
+ calc(var(--_padding-inline) - (var(--_action-size) - var(--_icon-size)) / 2)
77
+ );
78
+ }
79
+
80
+ /**
81
+ * The row of segments. It takes the whole of the space the adornments leave
82
+ * and puts the time at the reading edge, so an empty field and a full one
83
+ * start in the same place.
84
+ */
85
+ [data-ui='time-field'] [data-part='segments'] {
86
+ align-items: center;
87
+ /* It says what it is before it is pressed. A time is typed, so the pointer
88
+ over it is a caret — and the row rather than only the segments, because
89
+ the gap between two fields is part of the control the reader aims at. */
90
+ cursor: text;
91
+ display: flex;
92
+ flex: 1 1 auto;
93
+ min-inline-size: 0;
94
+ /* One line, whatever the frame's width: the row is clipped at the frame's
95
+ edge, as a text input clips a long value, and a field the reader steps
96
+ into is scrolled into view. The rule `date-field` records. */
97
+ overflow: hidden;
98
+ /* Digits that do not shift under one another as a value is typed over, and
99
+ four fields that keep their columns while the reader steps. */
100
+ font-variant-numeric: tabular-nums;
101
+
102
+ /**
103
+ * The sequence runs left to right in every document, because a clock time
104
+ * is a numeric run and that is how the bidirectional algorithm renders one:
105
+ * `Intl.DateTimeFormat` writes `٠٢:٣٠ م` for `ar-EG` with the hour leftmost,
106
+ * and it hands the parts back in that same order for every locale it knows.
107
+ *
108
+ * A flex row that took the paragraph's direction reversed them, and the
109
+ * right-to-left capture read `30:14` — a time nobody wrote, in a field that
110
+ * had done nothing wrong but inherit. The isolation keeps the decision
111
+ * inside the row, so the frame around it still reads with the document.
112
+ */
113
+ direction: ltr;
114
+ unicode-bidi: isolate;
115
+ }
116
+
117
+ /**
118
+ * The row is still placed with the document, though: a right-to-left form
119
+ * puts its values at the right edge, and a row whose own direction is now
120
+ * left-to-right would otherwise start at the left of a full-width box.
121
+ *
122
+ * `:dir()` rather than an ancestor `[dir]` selector, because the row's own
123
+ * `direction` above does not change what `:dir()` answers — that comes from
124
+ * the document — which is the reason every other family in this library
125
+ * reaches for it too.
126
+ */
127
+ [data-ui='time-field'] [data-part='segments']:dir(rtl) {
128
+ justify-content: flex-end;
129
+ }
130
+
131
+ /**
132
+ * One field of the time.
133
+ *
134
+ * Its width is stated rather than measured, so the row does not move when a
135
+ * single digit becomes two: `1ch` is the advance of a digit in a tabular
136
+ * face, and `--_digits` is how many the field can hold.
137
+ */
138
+ [data-ui='time-field'] [data-part='segment'] {
139
+ --_digits: 2;
140
+
141
+ background-color: transparent;
142
+ border-radius: var(--slotted-time-field-segment-radius, var(--slotted-radius-xs, 2px));
143
+ box-sizing: content-box;
144
+ /* The caret belongs to no segment: the whole field is replaced by typing,
145
+ and a caret between two digits would offer an edit the family refuses. */
146
+ caret-color: transparent;
147
+ color: inherit;
148
+ cursor: text;
149
+ font: inherit;
150
+ /* A field never shrinks under its digits: a row narrower than its value
151
+ overflows and is clipped at the frame, the way an input clips text. A
152
+ flex item shrunk to its minimum drew `mm` as `mr`. */
153
+ flex: 0 0 auto;
154
+ min-inline-size: var(--slotted-time-field-segment-min-inline-size, calc(var(--_digits) * 1ch));
155
+ padding-inline: var(
156
+ --slotted-time-field-segment-padding-inline,
157
+ var(--slotted-space-1, 0.125rem)
158
+ );
159
+ text-align: center;
160
+ transition:
161
+ background-color var(--slotted-control-transition-duration)
162
+ var(--slotted-control-transition-easing),
163
+ color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
164
+ /* A spin button is operated by keys and by the pointer; nothing in it is
165
+ text a reader selects, and a drag across it would look like one. */
166
+ user-select: none;
167
+ -webkit-tap-highlight-color: transparent;
168
+ }
169
+
170
+ /**
171
+ * The day period holds a word rather than digits, so it is sized by what it
172
+ * holds with a floor under it. `es-MX` writes `a.m.`, `en-US` writes `AM`
173
+ * and a locale may write more; a box cut to two characters would clip the
174
+ * first of those, and `max-content` with a floor clips none of them. The
175
+ * digit face is dropped here for the same reason: tabular figures inside a
176
+ * word letter-space it.
177
+ */
178
+ [data-ui='time-field'] [data-part='segment'][data-field='dayPeriod'] {
179
+ font-variant-numeric: normal;
180
+ min-inline-size: var(--slotted-time-field-period-min-inline-size, 2ch);
181
+ /* An abbreviation, in whatever case the locale itself writes it. */
182
+ text-transform: none;
183
+ white-space: nowrap;
184
+ }
185
+
186
+ /**
187
+ * A field with no value yet. It is muted rather than hidden, because the
188
+ * placeholder is the format — `hh:mm` in the reader's own words rather than
189
+ * in English — and a time being typed has to keep saying what is missing.
190
+ */
191
+ [data-ui='time-field'] [data-part='segment'][data-placeholder] {
192
+ color: var(--slotted-time-field-placeholder-color, var(--slotted-text-muted, GrayText));
193
+ }
194
+
195
+ /**
196
+ * The separator the locale writes. It is decoration: the group's accessible
197
+ * name already carries the whole time in words, so a screen reader reading
198
+ * "colon" between two numbers would be reading punctuation aloud.
199
+ */
200
+ [data-ui='time-field'] [data-part='literal'] {
201
+ color: var(--slotted-time-field-literal-color, var(--slotted-text-muted, GrayText));
202
+ flex: 0 0 auto;
203
+ /* Its own bidi run: a colon between two numbers takes the paragraph's
204
+ direction, and in a right-to-left document the time read back to front. */
205
+ unicode-bidi: plaintext;
206
+ user-select: none;
207
+ }
208
+
209
+ /* ── The relative expression ──────────────────────────────────────────
210
+ *
211
+ * The one entry no surveyed implementation has: a time written as a duration
212
+ * from now, resolved in view before it is accepted. It replaces the segments
213
+ * rather than sitting beside them, because both of them writing one value
214
+ * would be two controls disagreeing about which is the time.
215
+ *
216
+ * It is a real `input`, so a mobile keyboard opens and the platform supplies
217
+ * every text-editing key the reader already knows.
218
+ */
219
+ [data-ui='time-field'] [data-part='duration'] {
220
+ appearance: none;
221
+ background-color: transparent;
222
+ border: 0;
223
+ color: inherit;
224
+ flex: 1 1 auto;
225
+ font: inherit;
226
+ /* An expression is arithmetic, so its digits line up like the value's. */
227
+ font-variant-numeric: tabular-nums;
228
+ min-inline-size: 0;
229
+ outline: none;
230
+ padding: 0;
231
+
232
+ /**
233
+ * `+40m` is a left-to-right construct wherever it is typed: the sign leads,
234
+ * the unit follows the number, and the grammar is symbols rather than
235
+ * words. Left to the document it read `40m+` in the right-to-left capture.
236
+ */
237
+ direction: ltr;
238
+ unicode-bidi: isolate;
239
+ }
240
+
241
+ /* Left-to-right content in a right-to-left field still sits at the reading
242
+ edge, which is the treatment every form gives a phone number or an IBAN. */
243
+ [data-ui='time-field'] [data-part='duration']:dir(rtl) {
244
+ text-align: right;
245
+ }
246
+
247
+ [data-ui='time-field'] [data-part='duration']::placeholder {
248
+ color: var(--slotted-time-field-placeholder-color, var(--slotted-text-muted, GrayText));
249
+ }
250
+
251
+ /**
252
+ * What the expression resolves to, before anything commits.
253
+ *
254
+ * It is the answer rather than the question, so it takes the accent ink and
255
+ * the expression keeps the value's. The chevron between them is the mark the
256
+ * library already has for "and then"; it is direction-sensitive, so it turns
257
+ * with the document rather than pointing out of it.
258
+ */
259
+ [data-ui='time-field'] [data-part='preview'] {
260
+ /**
261
+ * One line box, not a flex row. The chevron sits *in* the line rather than
262
+ * beside it, which is the only way it can be aligned to the text at all —
263
+ * a flex box centres its children on the whole line box, and the letters
264
+ * do not live in the middle of that.
265
+ */
266
+ --_mark: 1cap;
267
+
268
+ color: var(--slotted-time-field-preview-color, var(--slotted-tone-accent-text));
269
+ display: block;
270
+ flex: 0 0 auto;
271
+ font-variant-numeric: tabular-nums;
272
+ white-space: nowrap;
273
+ }
274
+
275
+ /**
276
+ * The mark, on the text's own axis.
277
+ *
278
+ * Two things were wrong and only together: it was drawn at the control glyph
279
+ * size — sixteen pixels of chevron beside a nine-pixel capital, which is a
280
+ * control's mark standing in a sentence — and it was centred on the line box
281
+ * rather than on the band the letters occupy, so it read high. `1cap` is the
282
+ * size `status-indicator` already uses for a mark inside a line of text, and
283
+ * the `vertical-align` is the formula `docs/build-briefing.md` records under
284
+ * _Decisions already taken_: half the difference between the band and the
285
+ * mark, which lands the mark's centre on the middle of the band.
286
+ *
287
+ * The band is the midpoint of cap and x-height, which is the one `toast`
288
+ * chose after reading all three at four times the type size: `1cap` alone
289
+ * reads high, because every glyph in the scale overshoots a capital, and
290
+ * `1ex` alone reads low, because the mark then hangs below the descenders.
291
+ *
292
+ * The mark is the chevron the binding renders as the preview's first child,
293
+ * marked `directional`: it points along the reading direction, so the `icon`
294
+ * family turns it in a right-to-left context.
295
+ */
296
+ [data-ui='time-field'] [data-part='preview'] > [data-ui='icon'] {
297
+ --slotted-icon-size: var(--_mark);
298
+
299
+ margin-inline-end: var(--slotted-space-1, 0.125rem);
300
+ vertical-align: calc((((1cap + 1ex) / 2) - var(--_mark)) / 2);
301
+ }
302
+
303
+ /**
304
+ * A duration that ran past midnight. It resolves into a different day, and
305
+ * this library holds no day beside a time — so the preview changes tone and
306
+ * the family writes the day beside the time. Colour alone never says it:
307
+ * `docs/component-features.md` §2.5 refuses that, and a reader on a screen
308
+ * reader hears the same words the sighted reader sees.
309
+ */
310
+ [data-ui='time-field'] [data-part='preview'][data-crosses-midnight] {
311
+ color: var(--slotted-time-field-preview-next-color, var(--slotted-tone-warning-text));
312
+ }
313
+
314
+ /**
315
+ * While an expression is open the frame says so, because the control now
316
+ * takes text where a moment ago it took digits one field at a time. The
317
+ * border is the accent the preview is written in, so the two marks are one
318
+ * statement rather than two.
319
+ */
320
+ [data-ui='time-field'][data-part='root'][data-relative] {
321
+ border-color: var(
322
+ --slotted-time-field-relative-border-color,
323
+ var(--slotted-tone-accent-border)
324
+ );
325
+ }
326
+
327
+ /* ── Size ─────────────────────────────────────────────────────────────
328
+ *
329
+ * Three steps, matching the field the control sits in. A `size="sm"` field
330
+ * holding an `md` control is a field and a control disagreeing about one row.
331
+ */
332
+
333
+ [data-ui='time-field'][data-part='root'][data-size='sm'] {
334
+ --_font-size: var(--slotted-control-font-size-sm, 12px);
335
+ --_height: var(--slotted-control-height-sm, 30px);
336
+ --_padding-inline: var(--slotted-control-padding-inline-sm, 10px);
337
+ --_gap: var(--slotted-control-gap-sm, 6px);
338
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-sm, 14px));
339
+ }
340
+
341
+ [data-ui='time-field'][data-part='root'][data-size='md'] {
342
+ --_font-size: var(--slotted-control-font-size-md, 13px);
343
+ --_height: var(--slotted-control-height-md, 34px);
344
+ --_padding-inline: var(--slotted-control-padding-inline-md, 13px);
345
+ --_gap: var(--slotted-control-gap-md, 8px);
346
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
347
+ }
348
+
349
+ [data-ui='time-field'][data-part='root'][data-size='lg'] {
350
+ --_font-size: var(--slotted-control-font-size-lg, 14px);
351
+ --_height: var(--slotted-control-height-lg, 38px);
352
+ --_padding-inline: var(--slotted-control-padding-inline-lg, 16px);
353
+ --_gap: var(--slotted-control-gap-lg, 8px);
354
+ --_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-lg, 18px));
355
+ }
356
+
357
+ /* ── Variant ──────────────────────────────────────────────────────────
358
+ *
359
+ * The three steps `text-field` set, so a form holding a name and a time
360
+ * shows one frame twice rather than two frames.
361
+ */
362
+
363
+ [data-ui='time-field'][data-part='root'][data-variant='subtle'] {
364
+ background-color: var(
365
+ --slotted-time-field-subtle-background,
366
+ var(--slotted-surface-sunken, Canvas)
367
+ );
368
+ border-color: var(
369
+ --slotted-time-field-subtle-border-color,
370
+ var(--slotted-border-subtle, ButtonBorder)
371
+ );
372
+ }
373
+
374
+ [data-ui='time-field'][data-part='root'][data-variant='plain'] {
375
+ background-color: transparent;
376
+ border-color: transparent;
377
+ border-radius: var(--slotted-radius-none, 0px);
378
+ box-shadow: none;
379
+ }
380
+
381
+ /* ── Adornments ───────────────────────────────────────────────────────
382
+ *
383
+ * Named for the reading direction rather than for a side, and quieter than
384
+ * the value: an adornment annotates, and a reader looking for the time
385
+ * should not have to step over it.
386
+ */
387
+ [data-ui='time-field'] > [data-part='start'],
388
+ [data-ui='time-field'] > [data-part='end'] {
389
+ --slotted-icon-size: var(--_icon-size);
390
+
391
+ align-items: center;
392
+ color: var(--slotted-time-field-adornment-color, var(--slotted-text-muted, GrayText));
393
+ display: inline-flex;
394
+ flex: 0 0 auto;
395
+ gap: var(--slotted-space-2, 0.25rem);
396
+ line-height: 1;
397
+ unicode-bidi: plaintext;
398
+ }
399
+
400
+ [data-ui='time-field'] > [data-part='start'] > svg,
401
+ [data-ui='time-field'] > [data-part='end'] > svg {
402
+ block-size: var(--_icon-size);
403
+ inline-size: var(--_icon-size);
404
+ }
405
+
406
+ /* ── The clear button ─────────────────────────────────────────────────
407
+ *
408
+ * A real button, with the `Glyph` element the binding renders rather than a
409
+ * character, and held to the target minimum by the family rather than by the
410
+ * page.
411
+ */
412
+ [data-ui='time-field'] > [data-part='clear'] {
413
+ align-items: center;
414
+ appearance: none;
415
+ background-color: transparent;
416
+ block-size: var(--_action-size);
417
+ border: 0;
418
+ border-radius: var(--slotted-time-field-radius, var(--slotted-control-radius, 4px));
419
+ color: var(--slotted-time-field-action-color, var(--slotted-text-muted, GrayText));
420
+ cursor: pointer;
421
+ display: inline-flex;
422
+ flex: 0 0 auto;
423
+ inline-size: var(--_action-size);
424
+ justify-content: center;
425
+ padding: 0;
426
+ position: relative;
427
+ touch-action: manipulation;
428
+ transition:
429
+ background-color var(--slotted-control-transition-duration)
430
+ var(--slotted-control-transition-easing),
431
+ color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
432
+ -webkit-tap-highlight-color: transparent;
433
+ }
434
+
435
+ /**
436
+ * The hit area, recovered outside the paint. At `sm` with `dense` the whole
437
+ * control is 24 pixels tall — the WCAG 2.2 minimum — so nothing drawn inside
438
+ * it can also be 24. This grows the target back in both axes and leaves a
439
+ * larger one alone, which is the correction `docs/token-foundation.md`
440
+ * leaves to the family rather than to the theme.
441
+ */
442
+ [data-ui='time-field'] > [data-part='clear']::after {
443
+ content: '';
444
+ inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
445
+ position: absolute;
446
+ }
447
+
448
+ /* On a finger the target is the comfortable size in both axes and the box
449
+ is the one a mouse sees: the ground and the focus ring stay square, and
450
+ the hit area above grows to 44 pixels around them. After an end
451
+ adornment — a picker's trigger — it does not reach back across it. */
452
+ @media (pointer: coarse) {
453
+ [data-ui='time-field'] > [data-part='clear']::after {
454
+ inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
455
+ }
456
+
457
+ [data-ui='time-field'] > [data-part='end'] + [data-part='clear']::after {
458
+ inset-inline-start: 0;
459
+ }
460
+ }
461
+
462
+ /* The cross is drawn at the control's own icon step. */
463
+ [data-ui='time-field'] > [data-part='clear'] {
464
+ --slotted-icon-size: var(--_icon-size);
465
+ }
466
+
467
+ [data-ui='time-field'] > [data-part='clear']:hover:not(:disabled):not([data-disabled]) {
468
+ background-color: var(
469
+ --slotted-time-field-action-hover-background,
470
+ var(--slotted-tone-secondary-subtle-hover)
471
+ );
472
+ }
473
+
474
+ [data-ui='time-field'] > [data-part='clear']:active:not(:disabled):not([data-disabled]) {
475
+ background-color: var(
476
+ --slotted-time-field-action-active-background,
477
+ var(--slotted-tone-secondary-subtle-active)
478
+ );
479
+ }
480
+
481
+ /**
482
+ * The button carries its own ring rather than borrowing the frame's. Tabbing
483
+ * from the last segment to the clear button has to move something visible,
484
+ * and a ring around the whole frame would sit still while focus moved inside
485
+ * it.
486
+ */
487
+ [data-ui='time-field'] > [data-part='clear']:focus-visible {
488
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
489
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
490
+ }
491
+
492
+ /* ── States ───────────────────────────────────────────────────────────
493
+ *
494
+ * Written strongest last, and each weaker one guarded against every stronger
495
+ * one it can meet, which is the order `STATE_PRECEDENCE` sets.
496
+ */
497
+
498
+ [data-ui='time-field'][data-part='root']:hover:not([data-disabled]):not([data-readonly]):not(
499
+ [data-invalid]
500
+ ) {
501
+ border-color: var(--slotted-time-field-hover-border-color, var(--slotted-tone-accent-border));
502
+ }
503
+
504
+ [data-ui='time-field'][data-part='root'][data-variant='subtle']:hover:not([data-disabled]):not(
505
+ [data-readonly]
506
+ ):not([data-invalid]) {
507
+ background-color: var(
508
+ --slotted-time-field-subtle-hover-background,
509
+ var(--slotted-tone-secondary-subtle-hover)
510
+ );
511
+ }
512
+
513
+ [data-ui='time-field'][data-part='root'][data-variant='plain']:hover:not([data-disabled]):not(
514
+ [data-readonly]
515
+ ):not([data-invalid]) {
516
+ background-color: var(
517
+ --slotted-time-field-plain-hover-background,
518
+ var(--slotted-tone-secondary-subtle-hover)
519
+ );
520
+ border-color: transparent;
521
+ }
522
+
523
+ /**
524
+ * Focus is two marks and they answer two different questions.
525
+ *
526
+ * The frame's ring says the control has the reader's attention, exactly as
527
+ * every other control in the library says it. The band on one segment says
528
+ * which field a key will move, and no ring can say that: a second outline
529
+ * two pixels inside the first would be two rings nested, and at `dense`
530
+ * there is not room for two.
531
+ *
532
+ * Both are `:focus` and not `:focus-visible`, which is the rule `date-field`
533
+ * paid for: a browser matches `:focus-visible` on a text input for a pointer
534
+ * press as well as for a Tab, and refuses it for a `div` a pointer focused.
535
+ * Every segment here is a `div`, so a clicked field would look exactly as it
536
+ * had before the press. The expression is an `input` and takes the frame's
537
+ * ring on `:focus-within` for the same reason from the other side.
538
+ */
539
+ [data-ui='time-field'][data-part='root']:has([data-part='segment']:focus),
540
+ [data-ui='time-field'][data-part='root']:has([data-part='duration']:focus) {
541
+ box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
542
+ var(--slotted-focus-ring-color, Highlight);
543
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
544
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
545
+ }
546
+
547
+ /**
548
+ * The ring of a control whose box is not its own is drawn inside that box,
549
+ * the way the inner controls draw theirs inside the frame: two pixels past
550
+ * the control is two pixels over the next row's rule. No lift either — a
551
+ * shadow that appears only under focus is a cell that changes size when it
552
+ * is used.
553
+ */
554
+ [data-ui='time-field'][data-part='root'][data-variant='plain']:has([data-part='segment']:focus),
555
+ [data-ui='time-field'][data-part='root'][data-variant='plain']:has([data-part='duration']:focus) {
556
+ box-shadow: none;
557
+ outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
558
+ }
559
+
560
+ [data-ui='time-field'] [data-part='segment']:focus {
561
+ background-color: var(
562
+ --slotted-time-field-segment-focus-background,
563
+ var(--slotted-tone-accent-solid)
564
+ );
565
+ color: var(--slotted-time-field-segment-focus-color, var(--slotted-tone-accent-on-solid));
566
+ /* The frame already draws the ring, so the segment does not draw a second. */
567
+ outline: none;
568
+ }
569
+
570
+ /**
571
+ * Invalid outranks hover and is written after it: a control reporting an
572
+ * error should not stop looking wrong because the pointer is over it. The
573
+ * ring is retargeted rather than repainted, so the frame's outline, the
574
+ * shadow under it and the ring on the clear button all move at once.
575
+ */
576
+ [data-ui='time-field'][data-part='root'][data-invalid] {
577
+ --slotted-focus-ring-color: var(
578
+ --slotted-time-field-invalid-focus-ring-color,
579
+ var(--slotted-focus-ring-color-invalid, Highlight)
580
+ );
581
+ --_invalid-edge-width: var(
582
+ --slotted-time-field-invalid-edge-width,
583
+ var(--slotted-control-invalid-edge-width, 1px)
584
+ );
585
+ --_invalid-edge-color: var(
586
+ --slotted-time-field-invalid-border-color,
587
+ var(--slotted-tone-danger-border)
588
+ );
589
+ --_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
590
+
591
+ border-color: var(--slotted-time-field-invalid-border-color, var(--slotted-tone-danger-border));
592
+ /*
593
+ * Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
594
+ * `high-contrast` the danger border is the resting edge's black. The line
595
+ * is a shadow inside the border, so neither box nor text moves, and it
596
+ * takes the resting lift's place.
597
+ */
598
+ box-shadow: var(--_invalid-edge);
599
+ }
600
+
601
+ [data-ui='time-field'][data-part='root'][data-invalid]:has([data-part='segment']:focus),
602
+ [data-ui='time-field'][data-part='root'][data-invalid]:has([data-part='duration']:focus) {
603
+ box-shadow:
604
+ var(--_invalid-edge),
605
+ var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
606
+ var(--slotted-focus-ring-color, Highlight);
607
+ }
608
+
609
+ [data-ui='time-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
610
+ [data-part='segment']:focus
611
+ ),
612
+ [data-ui='time-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
613
+ [data-part='duration']:focus
614
+ ) {
615
+ /* The ring lies inside the box here, over the border and an edge as wide as
616
+ it; the edge reaches past the ring. */
617
+ box-shadow: inset 0 0 0
618
+ calc(
619
+ var(--slotted-focus-ring-offset, 2px) - var(--slotted-control-border-width, 1px) +
620
+ var(--_invalid-edge-width)
621
+ )
622
+ var(--_invalid-edge-color);
623
+ }
624
+
625
+ /*
626
+ * Forced colours remove every box shadow, and the edge with them. The
627
+ * width comes back as an outline inside the border, and a focused invalid
628
+ * control draws its ring double, which a valid ring is not.
629
+ */
630
+ @media (forced-colors: active) {
631
+ [data-ui='time-field'][data-part='root'][data-invalid] {
632
+ outline: var(--_invalid-edge-width) solid CanvasText;
633
+ outline-offset: calc(
634
+ -1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
635
+ );
636
+ }
637
+
638
+ [data-ui='time-field'][data-part='root'][data-invalid]:has([data-part='segment']:focus),
639
+ [data-ui='time-field'][data-part='root'][data-invalid]:has([data-part='duration']:focus) {
640
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
641
+ var(--slotted-focus-ring-color, Highlight);
642
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
643
+ }
644
+
645
+ [data-ui='time-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
646
+ [data-part='segment']:focus
647
+ ),
648
+ [data-ui='time-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
649
+ [data-part='duration']:focus
650
+ ) {
651
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
652
+ var(--slotted-focus-ring-color, Highlight);
653
+ outline-offset: calc(
654
+ -1 * (var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width))
655
+ );
656
+ }
657
+ }
658
+
659
+ /**
660
+ * Read-only stays focusable, readable and copyable, and must not be
661
+ * mistakable for disabled: the value is still real and still submitted. The
662
+ * ground says "not yours to change" and the ink says nothing at all.
663
+ */
664
+ [data-ui='time-field'][data-part='root'][data-readonly],
665
+ [data-ui='time-field'][data-part='root'][data-readonly] [data-part='segments'],
666
+ [data-ui='time-field'][data-part='root'][data-readonly] [data-part='segment'] {
667
+ cursor: default;
668
+ }
669
+
670
+ [data-ui='time-field'][data-part='root'][data-readonly] {
671
+ background-color: var(
672
+ --slotted-time-field-readonly-background,
673
+ var(--slotted-tone-secondary-subtle-hover)
674
+ );
675
+ }
676
+
677
+ /**
678
+ * Muted to the colour the theme measures rather than faded by opacity: a
679
+ * time nobody can read is a field nobody can check, and a disabled form
680
+ * still has to say what it holds.
681
+ */
682
+ [data-ui='time-field'][data-part='root'][data-disabled] {
683
+ background-color: var(
684
+ --slotted-time-field-disabled-background,
685
+ var(--slotted-disabled-background)
686
+ );
687
+ border-color: var(--slotted-time-field-disabled-border-color, var(--slotted-disabled-border));
688
+ /* Nothing a person cannot reach is lifted off the page. */
689
+ box-shadow: none;
690
+ color: var(--slotted-time-field-disabled-color, var(--slotted-disabled-foreground));
691
+ cursor: not-allowed;
692
+ }
693
+
694
+ [data-ui='time-field'][data-part='root'][data-disabled] [data-part='segments'],
695
+ [data-ui='time-field'][data-part='root'][data-disabled] [data-part='segment'] {
696
+ cursor: not-allowed;
697
+ }
698
+
699
+ [data-ui='time-field'][data-part='root'][data-disabled] > [data-part='start'],
700
+ [data-ui='time-field'][data-part='root'][data-disabled] > [data-part='end'],
701
+ [data-ui='time-field'][data-part='root'][data-disabled] [data-part='literal'],
702
+ [data-ui='time-field'][data-part='root'][data-disabled] [data-part='segment'][data-placeholder] {
703
+ color: var(--slotted-disabled-foreground);
704
+ }
705
+
706
+ @media (forced-colors: active) {
707
+ /* A transparent border has no system colour to be remapped to, so the
708
+ `plain` control would lose its only edge where an edge matters most. */
709
+ [data-ui='time-field'][data-part='root'][data-variant='plain'] {
710
+ border-color: ButtonBorder;
711
+ }
712
+
713
+ /* The preview's two tones both map to the same system ink, so the border
714
+ is what is left to say the control is in a different mode. */
715
+ [data-ui='time-field'][data-part='root'][data-relative] {
716
+ border-color: Highlight;
717
+ }
718
+ }
719
+ }