@slotted/styles 0.0.0-stage → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/package.json +276 -3
- package/src/accordion/accordion.css +96 -0
- package/src/accordion/accordion.tokens.json +14 -0
- package/src/action-bar/action-bar.css +455 -0
- package/src/action-bar/action-bar.tokens.json +50 -0
- package/src/action-sheet/action-sheet.css +462 -0
- package/src/action-sheet/action-sheet.tokens.json +59 -0
- package/src/alert/alert.css +764 -0
- package/src/alert/alert.tokens.json +77 -0
- package/src/app-shell/app-shell.css +1899 -0
- package/src/app-shell/app-shell.tokens.json +88 -0
- package/src/aspect-ratio/aspect-ratio.css +131 -0
- package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
- package/src/attachment/attachment.css +906 -0
- package/src/attachment/attachment.tokens.json +85 -0
- package/src/avatar/avatar.css +598 -0
- package/src/avatar/avatar.tokens.json +64 -0
- package/src/avatar-group/avatar-group.css +268 -0
- package/src/avatar-group/avatar-group.tokens.json +16 -0
- package/src/badge/badge.css +462 -0
- package/src/badge/badge.tokens.json +58 -0
- package/src/breadcrumb/breadcrumb.css +441 -0
- package/src/breadcrumb/breadcrumb.tokens.json +50 -0
- package/src/button/button.css +754 -0
- package/src/button/button.tokens.json +72 -0
- package/src/button-group/button-group.css +455 -0
- package/src/button-group/button-group.tokens.json +11 -0
- package/src/calendar/calendar.css +846 -0
- package/src/calendar/calendar.tokens.json +67 -0
- package/src/card/card.css +602 -0
- package/src/card/card.tokens.json +68 -0
- package/src/carousel/carousel.css +860 -0
- package/src/carousel/carousel.tokens.json +62 -0
- package/src/cascader/cascader.css +1320 -0
- package/src/cascader/cascader.tokens.json +103 -0
- package/src/chart/chart.css +1473 -0
- package/src/chart/chart.tokens.json +107 -0
- package/src/checkbox/checkbox.css +1011 -0
- package/src/checkbox/checkbox.tokens.json +102 -0
- package/src/checkbox-group/checkbox-group.css +70 -0
- package/src/checkbox-group/checkbox-group.tokens.json +6 -0
- package/src/code-block/code-block.css +555 -0
- package/src/code-block/code-block.tokens.json +69 -0
- package/src/collapsible/collapsible.css +546 -0
- package/src/collapsible/collapsible.tokens.json +55 -0
- package/src/color-picker/color-picker.css +607 -0
- package/src/color-picker/color-picker.tokens.json +58 -0
- package/src/column-manager/column-manager.css +452 -0
- package/src/column-manager/column-manager.tokens.json +43 -0
- package/src/combobox/combobox.css +1048 -0
- package/src/combobox/combobox.tokens.json +112 -0
- package/src/command-palette/command-palette.css +1000 -0
- package/src/command-palette/command-palette.tokens.json +75 -0
- package/src/container/container.css +198 -0
- package/src/container/container.tokens.json +18 -0
- package/src/context-menu/context-menu.css +104 -0
- package/src/context-menu/context-menu.tokens.json +11 -0
- package/src/currency-field/currency-field.css +787 -0
- package/src/currency-field/currency-field.tokens.json +80 -0
- package/src/dashboard-grid/dashboard-grid.css +748 -0
- package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
- package/src/data-grid/data-grid.css +2484 -0
- package/src/data-grid/data-grid.tokens.json +100 -0
- package/src/data-list/data-list.css +455 -0
- package/src/data-list/data-list.tokens.json +35 -0
- package/src/date-field/date-field.css +560 -0
- package/src/date-field/date-field.tokens.json +80 -0
- package/src/date-picker/date-picker.css +420 -0
- package/src/date-picker/date-picker.tokens.json +49 -0
- package/src/date-range-picker/date-range-picker.css +983 -0
- package/src/date-range-picker/date-range-picker.tokens.json +109 -0
- package/src/datetime-field/datetime-field.css +850 -0
- package/src/datetime-field/datetime-field.tokens.json +91 -0
- package/src/datetime-picker/datetime-picker.css +843 -0
- package/src/datetime-picker/datetime-picker.tokens.json +78 -0
- package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
- package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
- package/src/description-list/description-list.css +675 -0
- package/src/description-list/description-list.tokens.json +66 -0
- package/src/detail-panel/detail-panel.css +329 -0
- package/src/detail-panel/detail-panel.tokens.json +33 -0
- package/src/dialog/dialog.css +968 -0
- package/src/dialog/dialog.tokens.json +66 -0
- package/src/divider/divider.css +131 -0
- package/src/divider/divider.tokens.json +18 -0
- package/src/drawer/drawer.css +1303 -0
- package/src/drawer/drawer.tokens.json +75 -0
- package/src/entity-picker/entity-picker.css +1367 -0
- package/src/entity-picker/entity-picker.tokens.json +118 -0
- package/src/event-calendar/event-calendar.css +2148 -0
- package/src/event-calendar/event-calendar.tokens.json +87 -0
- package/src/field/field.css +707 -0
- package/src/field/field.tokens.json +58 -0
- package/src/field-group/field-group.css +226 -0
- package/src/field-group/field-group.tokens.json +33 -0
- package/src/filter-bar/filter-bar.css +1306 -0
- package/src/filter-bar/filter-bar.tokens.json +78 -0
- package/src/form/form.css +218 -0
- package/src/form/form.tokens.json +32 -0
- package/src/fullscreen/fullscreen.css +376 -0
- package/src/fullscreen/fullscreen.tokens.json +50 -0
- package/src/grid/grid.css +173 -0
- package/src/grid/grid.tokens.json +20 -0
- package/src/highlight/highlight.css +135 -0
- package/src/highlight/highlight.tokens.json +16 -0
- package/src/hover-card/hover-card.css +93 -0
- package/src/hover-card/hover-card.tokens.json +17 -0
- package/src/icon/icon.css +136 -0
- package/src/icon/icon.tokens.json +14 -0
- package/src/image/image.css +449 -0
- package/src/image/image.tokens.json +27 -0
- package/src/import-export-center/import-export-center.css +1175 -0
- package/src/import-export-center/import-export-center.tokens.json +84 -0
- package/src/inline/inline.css +149 -0
- package/src/inline/inline.tokens.json +14 -0
- package/src/inline-message/inline-message.css +396 -0
- package/src/inline-message/inline-message.tokens.json +19 -0
- package/src/kbd/kbd.css +490 -0
- package/src/kbd/kbd.tokens.json +40 -0
- package/src/link/link.css +237 -0
- package/src/link/link.tokens.json +25 -0
- package/src/list/list.css +240 -0
- package/src/list/list.tokens.json +42 -0
- package/src/list-item/list-item.css +937 -0
- package/src/list-item/list-item.tokens.json +114 -0
- package/src/listbox/listbox.css +674 -0
- package/src/listbox/listbox.tokens.json +72 -0
- package/src/loading-overlay/loading-overlay.css +730 -0
- package/src/loading-overlay/loading-overlay.tokens.json +48 -0
- package/src/markdown/markdown.css +168 -0
- package/src/markdown/markdown.tokens.json +18 -0
- package/src/menu/menu.css +817 -0
- package/src/menu/menu.tokens.json +78 -0
- package/src/menubar/menubar.css +170 -0
- package/src/menubar/menubar.tokens.json +32 -0
- package/src/message/message.css +1453 -0
- package/src/message/message.tokens.json +85 -0
- package/src/message-scroller/message-scroller.css +371 -0
- package/src/message-scroller/message-scroller.tokens.json +47 -0
- package/src/multi-select/multi-select.css +1427 -0
- package/src/multi-select/multi-select.tokens.json +114 -0
- package/src/navigation-menu/navigation-menu.css +1029 -0
- package/src/navigation-menu/navigation-menu.tokens.json +70 -0
- package/src/notification-center/notification-center.css +856 -0
- package/src/notification-center/notification-center.tokens.json +84 -0
- package/src/number-field/number-field.css +794 -0
- package/src/number-field/number-field.tokens.json +80 -0
- package/src/otp-input/otp-input.css +525 -0
- package/src/otp-input/otp-input.tokens.json +69 -0
- package/src/page/page.css +179 -0
- package/src/page/page.tokens.json +20 -0
- package/src/page-header/page-header.css +1181 -0
- package/src/page-header/page-header.tokens.json +71 -0
- package/src/pagination/pagination.css +500 -0
- package/src/pagination/pagination.tokens.json +56 -0
- package/src/popover/popover.css +526 -0
- package/src/popover/popover.tokens.json +60 -0
- package/src/progress-indicator/progress-indicator.css +1584 -0
- package/src/progress-indicator/progress-indicator.tokens.json +50 -0
- package/src/prompt-composer/prompt-composer.css +680 -0
- package/src/prompt-composer/prompt-composer.tokens.json +99 -0
- package/src/prose/prose.css +733 -0
- package/src/prose/prose.tokens.json +79 -0
- package/src/radio-group/radio-group.css +778 -0
- package/src/radio-group/radio-group.tokens.json +103 -0
- package/src/rating/rating.css +1071 -0
- package/src/rating/rating.tokens.json +88 -0
- package/src/result-state/result-state.css +980 -0
- package/src/result-state/result-state.tokens.json +63 -0
- package/src/runtime-properties.mjs +321 -0
- package/src/saved-views/saved-views.css +394 -0
- package/src/saved-views/saved-views.tokens.json +41 -0
- package/src/scroll-area/scroll-area.css +481 -0
- package/src/scroll-area/scroll-area.tokens.json +15 -0
- package/src/section/section.css +177 -0
- package/src/section/section.tokens.json +39 -0
- package/src/segmented-control/segmented-control.css +564 -0
- package/src/segmented-control/segmented-control.tokens.json +67 -0
- package/src/select/select.css +1593 -0
- package/src/select/select.tokens.json +110 -0
- package/src/side-navigation/side-navigation.css +1096 -0
- package/src/side-navigation/side-navigation.tokens.json +87 -0
- package/src/skeleton/skeleton.css +431 -0
- package/src/skeleton/skeleton.tokens.json +29 -0
- package/src/slider/slider.css +792 -0
- package/src/slider/slider.tokens.json +75 -0
- package/src/sparkline/sparkline.css +282 -0
- package/src/sparkline/sparkline.tokens.json +45 -0
- package/src/spinner/spinner.css +224 -0
- package/src/spinner/spinner.tokens.json +8 -0
- package/src/split-button/split-button.css +253 -0
- package/src/split-button/split-button.tokens.json +12 -0
- package/src/splitter/splitter.css +336 -0
- package/src/splitter/splitter.tokens.json +24 -0
- package/src/stack/stack.css +128 -0
- package/src/stack/stack.tokens.json +14 -0
- package/src/stat-card/stat-card.css +641 -0
- package/src/stat-card/stat-card.tokens.json +95 -0
- package/src/status-indicator/status-indicator.css +481 -0
- package/src/status-indicator/status-indicator.tokens.json +38 -0
- package/src/stepper/stepper.css +629 -0
- package/src/stepper/stepper.tokens.json +72 -0
- package/src/switch/switch.css +703 -0
- package/src/switch/switch.tokens.json +75 -0
- package/src/table/table.css +1482 -0
- package/src/table/table.tokens.json +69 -0
- package/src/table-of-contents/table-of-contents.css +533 -0
- package/src/table-of-contents/table-of-contents.tokens.json +53 -0
- package/src/tabs/tabs.css +998 -0
- package/src/tabs/tabs.tokens.json +82 -0
- package/src/tag/tag.css +768 -0
- package/src/tag/tag.tokens.json +85 -0
- package/src/tags-input/tags-input.css +757 -0
- package/src/tags-input/tags-input.tokens.json +84 -0
- package/src/text-field/text-field.css +900 -0
- package/src/text-field/text-field.tokens.json +87 -0
- package/src/textarea/textarea.css +365 -0
- package/src/textarea/textarea.tokens.json +59 -0
- package/src/time-field/time-field.css +719 -0
- package/src/time-field/time-field.tokens.json +86 -0
- package/src/time-picker/time-picker.css +665 -0
- package/src/time-picker/time-picker.tokens.json +66 -0
- package/src/time-range-picker/time-range-picker.css +1287 -0
- package/src/time-range-picker/time-range-picker.tokens.json +134 -0
- package/src/timeline/timeline.css +1596 -0
- package/src/timeline/timeline.tokens.json +96 -0
- package/src/toast/toast.css +943 -0
- package/src/toast/toast.tokens.json +90 -0
- package/src/toggle/toggle.css +290 -0
- package/src/toggle/toggle.tokens.json +54 -0
- package/src/toggle-group/toggle-group.css +463 -0
- package/src/toggle-group/toggle-group.tokens.json +54 -0
- package/src/toolbar/toolbar.css +434 -0
- package/src/toolbar/toolbar.tokens.json +49 -0
- package/src/tooltip/tooltip.css +368 -0
- package/src/tooltip/tooltip.tokens.json +39 -0
- package/src/top-navigation/top-navigation.css +1287 -0
- package/src/top-navigation/top-navigation.tokens.json +91 -0
- package/src/tour/tour.css +680 -0
- package/src/tour/tour.tokens.json +67 -0
- package/src/transfer/transfer.css +571 -0
- package/src/transfer/transfer.tokens.json +49 -0
- package/src/tree/tree.css +1000 -0
- package/src/tree/tree.tokens.json +80 -0
- package/src/typography/typography.css +211 -0
- package/src/typography/typography.tokens.json +31 -0
- package/src/upload/upload.css +1706 -0
- package/src/upload/upload.tokens.json +123 -0
- package/src/visually-hidden/visually-hidden.css +52 -0
- package/src/visually-hidden/visually-hidden.tokens.json +1 -0
- package/src/wizard/wizard.css +740 -0
- package/src/wizard/wizard.tokens.json +63 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── The segmented datetime control ──────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* One frame, one `role="group"` and one value, holding all seven fields the
|
|
6
|
+
* two halves have between them. It is not a `date-field` beside a
|
|
7
|
+
* `time-field`: two nested family roots would be two groups, two values and
|
|
8
|
+
* two segment states, and the order of the halves and the literal between
|
|
9
|
+
* them come from one combined `Intl.DateTimeFormat` that neither of them can
|
|
10
|
+
* see. The frame carries no role of its own — the APG's spin-button example
|
|
11
|
+
* names the group as what tells a screen-reader user that the buttons are
|
|
12
|
+
* one value.
|
|
13
|
+
*
|
|
14
|
+
* There is no `input[type=datetime-local]` under any of it, for the reason
|
|
15
|
+
* both siblings refuse their own native input: the browser's control cannot
|
|
16
|
+
* be styled past its closed state, its panel is drawn by the operating
|
|
17
|
+
* system and answers no scheme, no density and no direction, and its field
|
|
18
|
+
* order is the browser's locale rather than the application's. The value
|
|
19
|
+
* reaches a form through a hidden input carrying `YYYY-MM-DDTHH:mm:ss`.
|
|
20
|
+
*/
|
|
21
|
+
[data-ui='datetime-field'][data-part='root'] {
|
|
22
|
+
/**
|
|
23
|
+
* Type, height, inline padding, gap and glyph size come from the control
|
|
24
|
+
* scales — the only ones the theme restates per density — so a datetime in
|
|
25
|
+
* a dense table shrinks whole rather than in parts.
|
|
26
|
+
*/
|
|
27
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
28
|
+
--_height: var(--slotted-control-height-md, 34px);
|
|
29
|
+
--_padding-inline: var(--slotted-control-padding-inline-md, 13px);
|
|
30
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
31
|
+
--_icon-floor: var(--slotted-datetime-field-icon-size-min, 14px);
|
|
32
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The distance between the two halves, and the only place in the row that
|
|
36
|
+
* is wider than a segment's own padding. Seven fields in one line read as
|
|
37
|
+
* one long number without it; with it they read as a date and a time, which
|
|
38
|
+
* is what the value is.
|
|
39
|
+
*/
|
|
40
|
+
--_half-gap: var(--slotted-datetime-field-half-gap, var(--slotted-space-3, 0.375rem));
|
|
41
|
+
|
|
42
|
+
/* The same inner-control box every family in the library draws: the glyph,
|
|
43
|
+
and one step of air on each side, bounded by the frame's own height. */
|
|
44
|
+
--_action-size: var(
|
|
45
|
+
--slotted-datetime-field-action-size,
|
|
46
|
+
min(
|
|
47
|
+
calc(var(--_icon-size) + 2 * var(--slotted-space-2, 0.25rem)),
|
|
48
|
+
calc(
|
|
49
|
+
var(--_height) - var(--slotted-control-border-width, 1px) * 2 -
|
|
50
|
+
var(--slotted-space-2, 0.25rem)
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
align-items: center;
|
|
56
|
+
background-color: var(
|
|
57
|
+
--slotted-datetime-field-background,
|
|
58
|
+
var(--slotted-control-surface, Field)
|
|
59
|
+
);
|
|
60
|
+
border: var(--slotted-control-border-width, 1px) solid
|
|
61
|
+
var(--slotted-datetime-field-border-color, var(--slotted-control-border-color, ButtonBorder));
|
|
62
|
+
border-radius: var(--slotted-datetime-field-radius, var(--slotted-control-radius, 4px));
|
|
63
|
+
box-shadow: var(--slotted-datetime-field-shadow, var(--slotted-control-shadow, none));
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
color: var(--slotted-datetime-field-color, var(--slotted-text, CanvasText));
|
|
66
|
+
display: flex;
|
|
67
|
+
font-family: var(--slotted-control-font-family, system-ui, sans-serif);
|
|
68
|
+
font-size: var(--_font-size);
|
|
69
|
+
gap: var(--_gap);
|
|
70
|
+
inline-size: 100%;
|
|
71
|
+
min-block-size: var(--_height);
|
|
72
|
+
min-inline-size: 0;
|
|
73
|
+
padding-inline: var(--_padding-inline);
|
|
74
|
+
/* The row inside can take two lines, so the frame's height is a floor and
|
|
75
|
+
not a fixed measure, and the padding block keeps the two lines off the
|
|
76
|
+
border rather than letting them sit against it. */
|
|
77
|
+
padding-block: var(--slotted-datetime-field-padding-block, var(--slotted-space-1, 0.125rem));
|
|
78
|
+
transition:
|
|
79
|
+
background-color var(--slotted-control-transition-duration)
|
|
80
|
+
var(--slotted-control-transition-easing),
|
|
81
|
+
border-color var(--slotted-control-transition-duration)
|
|
82
|
+
var(--slotted-control-transition-easing),
|
|
83
|
+
box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The button inside the frame is inset by its own gutter less than the value
|
|
88
|
+
* is, so the *glyph* ends the same distance from the border as the digits
|
|
89
|
+
* begin from it — the arithmetic `text-field` records, holding at every step
|
|
90
|
+
* and every density.
|
|
91
|
+
*/
|
|
92
|
+
[data-ui='datetime-field'][data-part='root']:has(> [data-part='clear']:last-child) {
|
|
93
|
+
padding-inline-end: max(
|
|
94
|
+
0px,
|
|
95
|
+
calc(var(--_padding-inline) - (var(--_action-size) - var(--_icon-size)) / 2)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The row of halves. It takes the whole of the space the adornments leave and
|
|
101
|
+
* puts the value at the reading edge, so an empty field and a full one start
|
|
102
|
+
* in the same place.
|
|
103
|
+
*
|
|
104
|
+
* **It is one line, at every width.** Seven fields do not fit a narrow
|
|
105
|
+
* container in every locale; the row used to wrap between its two halves,
|
|
106
|
+
* and a control two lines tall broke every row it stood in — a filter row,
|
|
107
|
+
* a table cell, a toolbar — and read as a different control from the field
|
|
108
|
+
* beside it. It is now clipped at the frame's edge, the way a text input
|
|
109
|
+
* clips a value longer than itself, and the half the reader steps into is
|
|
110
|
+
* scrolled into view, because a clipped box is still a scroll container to
|
|
111
|
+
* the focus. Each half stays one flex item, so the value never breaks
|
|
112
|
+
* inside a date or inside a time.
|
|
113
|
+
*/
|
|
114
|
+
[data-ui='datetime-field'] [data-part='segments'] {
|
|
115
|
+
align-items: center;
|
|
116
|
+
column-gap: var(--_half-gap);
|
|
117
|
+
/* It says what it is before it is pressed. A datetime is typed, so the
|
|
118
|
+
pointer over it is a caret — and the row rather than only the segments,
|
|
119
|
+
because the gap between two fields is part of the control the reader
|
|
120
|
+
aims at. */
|
|
121
|
+
cursor: text;
|
|
122
|
+
display: flex;
|
|
123
|
+
flex: 1 1 auto;
|
|
124
|
+
min-inline-size: 0;
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* One half, and the reason the row can only break in one place.
|
|
130
|
+
*
|
|
131
|
+
* The sequence inside runs left to right in every document, because a date
|
|
132
|
+
* and a clock time are both numeric runs and that is how the bidirectional
|
|
133
|
+
* algorithm renders one: `Intl.DateTimeFormat` writes `٠٢:٣٠ م` for `ar-EG`
|
|
134
|
+
* with the hour leftmost, and hands the parts back in that order for every
|
|
135
|
+
* locale it knows. A flex row that took the paragraph's direction reversed
|
|
136
|
+
* them, and the right-to-left capture read `30:14` — a time nobody wrote, in
|
|
137
|
+
* a field that had done nothing but inherit. The isolation keeps the decision
|
|
138
|
+
* inside the half, so the frame around it still reads with the document.
|
|
139
|
+
*/
|
|
140
|
+
[data-ui='datetime-field'] [data-part='half'] {
|
|
141
|
+
align-items: center;
|
|
142
|
+
direction: ltr;
|
|
143
|
+
display: flex;
|
|
144
|
+
flex: 0 0 auto;
|
|
145
|
+
/* Digits that do not shift under one another as a value is typed over, and
|
|
146
|
+
seven fields that keep their columns while the reader steps. */
|
|
147
|
+
font-variant-numeric: tabular-nums;
|
|
148
|
+
unicode-bidi: isolate;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The halves are still placed with the document, though: a right-to-left form
|
|
153
|
+
* puts its values at the right edge, and a row whose halves are now
|
|
154
|
+
* left-to-right would otherwise start at the left of a full-width box.
|
|
155
|
+
*
|
|
156
|
+
* `:dir()` rather than an ancestor `[dir]` selector, because the halves' own
|
|
157
|
+
* `direction` above does not change what `:dir()` answers — that comes from
|
|
158
|
+
* the document — which is the reason every other family in this library
|
|
159
|
+
* reaches for it too.
|
|
160
|
+
*/
|
|
161
|
+
[data-ui='datetime-field'] [data-part='segments']:dir(rtl) {
|
|
162
|
+
justify-content: flex-end;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* One field of the value.
|
|
167
|
+
*
|
|
168
|
+
* Its width is stated rather than measured, so the row does not move when a
|
|
169
|
+
* single digit becomes two: `1ch` is the advance of a digit in a tabular
|
|
170
|
+
* face, and `--_digits` is how many the field can hold.
|
|
171
|
+
*/
|
|
172
|
+
[data-ui='datetime-field'] [data-part='segment'] {
|
|
173
|
+
--_digits: 2;
|
|
174
|
+
|
|
175
|
+
background-color: transparent;
|
|
176
|
+
border-radius: var(--slotted-datetime-field-segment-radius, var(--slotted-radius-xs, 2px));
|
|
177
|
+
box-sizing: content-box;
|
|
178
|
+
/* The caret belongs to no segment: the whole field is replaced by typing,
|
|
179
|
+
and a caret between two digits would offer an edit the family refuses. */
|
|
180
|
+
caret-color: transparent;
|
|
181
|
+
color: inherit;
|
|
182
|
+
cursor: text;
|
|
183
|
+
font: inherit;
|
|
184
|
+
/* A field never shrinks under its digits: a row narrower than its value
|
|
185
|
+
overflows and is clipped at the frame, the way an input clips text. A
|
|
186
|
+
flex item shrunk to its minimum drew `mm` as `mr`. */
|
|
187
|
+
flex: 0 0 auto;
|
|
188
|
+
min-inline-size: var(
|
|
189
|
+
--slotted-datetime-field-segment-min-inline-size,
|
|
190
|
+
calc(var(--_digits) * 1ch)
|
|
191
|
+
);
|
|
192
|
+
padding-inline: var(
|
|
193
|
+
--slotted-datetime-field-segment-padding-inline,
|
|
194
|
+
var(--slotted-space-1, 0.125rem)
|
|
195
|
+
);
|
|
196
|
+
text-align: center;
|
|
197
|
+
transition:
|
|
198
|
+
background-color var(--slotted-control-transition-duration)
|
|
199
|
+
var(--slotted-control-transition-easing),
|
|
200
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
201
|
+
/* A spin button is operated by keys and by the pointer; nothing in it is
|
|
202
|
+
text a reader selects, and a drag across it would look like one. */
|
|
203
|
+
user-select: none;
|
|
204
|
+
-webkit-tap-highlight-color: transparent;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* The one field with four digits rather than two. Stated here rather than in
|
|
208
|
+
the markup, so a year is the same width in both bindings and the harness. */
|
|
209
|
+
[data-ui='datetime-field'] [data-part='segment'][data-field='year'] {
|
|
210
|
+
--_digits: 4;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The day period holds a word rather than digits, so it is sized by what it
|
|
215
|
+
* holds with a floor under it. `es-MX` writes `a.m.`, `en-US` writes `AM`
|
|
216
|
+
* and a locale may write more; a box cut to two characters would clip the
|
|
217
|
+
* first of those, and a floor clips none of them. The digit face is dropped
|
|
218
|
+
* here for the same reason: tabular figures inside a word letter-space it.
|
|
219
|
+
*/
|
|
220
|
+
[data-ui='datetime-field'] [data-part='segment'][data-field='dayPeriod'] {
|
|
221
|
+
font-variant-numeric: normal;
|
|
222
|
+
min-inline-size: var(--slotted-datetime-field-period-min-inline-size, 2ch);
|
|
223
|
+
/* An abbreviation, in whatever case the locale itself writes it. */
|
|
224
|
+
text-transform: none;
|
|
225
|
+
white-space: nowrap;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* A field with no value yet. It is muted rather than hidden, because the
|
|
230
|
+
* placeholder is the format — `dd/mm/aaaa hh:mm` in the reader's own words
|
|
231
|
+
* rather than in English — and a value being typed has to keep saying what
|
|
232
|
+
* is missing.
|
|
233
|
+
*/
|
|
234
|
+
[data-ui='datetime-field'] [data-part='segment'][data-placeholder] {
|
|
235
|
+
color: var(--slotted-datetime-field-placeholder-color, var(--slotted-text-muted, GrayText));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The separator the locale writes. It is decoration: the group's accessible
|
|
240
|
+
* name already carries the whole value in words, so a screen reader reading
|
|
241
|
+
* "slash" between two numbers would be reading punctuation aloud.
|
|
242
|
+
*/
|
|
243
|
+
[data-ui='datetime-field'] [data-part='literal'] {
|
|
244
|
+
color: var(--slotted-datetime-field-literal-color, var(--slotted-text-muted, GrayText));
|
|
245
|
+
flex: 0 0 auto;
|
|
246
|
+
/* Its own bidi run: a colon between two numbers takes the paragraph's
|
|
247
|
+
direction, and in a right-to-left document the time read back to front. */
|
|
248
|
+
unicode-bidi: plaintext;
|
|
249
|
+
user-select: none;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The literal the locale writes *between* the halves — a comma in `pt-BR`,
|
|
254
|
+
* `en-US` and `de-DE`, a bare space in `fr-FR`, `ja-JP` and `zh-CN`. It sits
|
|
255
|
+
* at the end of the first half rather
|
|
256
|
+
* than between the two, so a wrap leaves it at the end of its line, which is
|
|
257
|
+
* where a comma in a sentence goes. A boundary the locale writes as
|
|
258
|
+
* whitespace is not drawn at all, and the row's column gap separates the
|
|
259
|
+
* halves instead.
|
|
260
|
+
*/
|
|
261
|
+
[data-ui='datetime-field'] [data-part='literal'][data-boundary] {
|
|
262
|
+
margin-inline-end: 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* ── The relative expression ──────────────────────────────────────────
|
|
266
|
+
*
|
|
267
|
+
* The entry no surveyed implementation has, widened here by two unit letters:
|
|
268
|
+
* `+2d` and `+1w` are a datetime written as a duration from now, resolved in
|
|
269
|
+
* view before it is accepted. It replaces the segments rather than sitting
|
|
270
|
+
* beside them, because both of them writing one value would be two controls
|
|
271
|
+
* disagreeing about which is the datetime.
|
|
272
|
+
*
|
|
273
|
+
* It is a real `input`, so a mobile keyboard opens and the platform supplies
|
|
274
|
+
* every text-editing key the reader already knows.
|
|
275
|
+
*/
|
|
276
|
+
[data-ui='datetime-field'] [data-part='duration'] {
|
|
277
|
+
appearance: none;
|
|
278
|
+
background-color: transparent;
|
|
279
|
+
border: 0;
|
|
280
|
+
color: inherit;
|
|
281
|
+
flex: 1 1 auto;
|
|
282
|
+
font: inherit;
|
|
283
|
+
/* An expression is arithmetic, so its digits line up like the value's. */
|
|
284
|
+
font-variant-numeric: tabular-nums;
|
|
285
|
+
min-inline-size: 0;
|
|
286
|
+
outline: none;
|
|
287
|
+
padding: 0;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* `+2d` is a left-to-right construct wherever it is typed: the sign leads,
|
|
291
|
+
* the unit follows the number, and the grammar is symbols rather than
|
|
292
|
+
* words. Left to the document it read `2d+` in the right-to-left capture.
|
|
293
|
+
*/
|
|
294
|
+
direction: ltr;
|
|
295
|
+
unicode-bidi: isolate;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* Left-to-right content in a right-to-left field still sits at the reading
|
|
299
|
+
edge, which is the treatment every form gives a phone number or an IBAN. */
|
|
300
|
+
[data-ui='datetime-field'] [data-part='duration']:dir(rtl) {
|
|
301
|
+
text-align: right;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
[data-ui='datetime-field'] [data-part='duration']::placeholder {
|
|
305
|
+
color: var(--slotted-datetime-field-placeholder-color, var(--slotted-text-muted, GrayText));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* What the expression resolves to, before anything commits.
|
|
310
|
+
*
|
|
311
|
+
* It is the answer rather than the question, so it takes the accent ink and
|
|
312
|
+
* the expression keeps the value's. The chevron between them is the mark the
|
|
313
|
+
* library already has for "and then"; it is direction-sensitive, so it turns
|
|
314
|
+
* with the document rather than pointing out of it.
|
|
315
|
+
*/
|
|
316
|
+
[data-ui='datetime-field'] [data-part='preview'] {
|
|
317
|
+
/**
|
|
318
|
+
* One line box, not a flex row. The chevron sits *in* the line rather than
|
|
319
|
+
* beside it, which is the only way it can be aligned to the text at all —
|
|
320
|
+
* a flex box centres its children on the whole line box, and the letters
|
|
321
|
+
* do not live in the middle of that.
|
|
322
|
+
*/
|
|
323
|
+
--_mark: 1cap;
|
|
324
|
+
|
|
325
|
+
color: var(--slotted-datetime-field-preview-color, var(--slotted-tone-accent-text));
|
|
326
|
+
display: block;
|
|
327
|
+
flex: 0 0 auto;
|
|
328
|
+
font-variant-numeric: tabular-nums;
|
|
329
|
+
white-space: nowrap;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* The preview is a whole datetime in words and digits — `12 set, 14:40` —
|
|
333
|
+
* rather than the single numeric run `time-field` shows, so it is laid out
|
|
334
|
+
* by its own content and not by the paragraph around it. Left to the
|
|
335
|
+
* document, the right-to-left capture read `set, 14:40 12`: three runs
|
|
336
|
+
* reordered, and a date nobody wrote. `plaintext` gives an Arabic preview
|
|
337
|
+
* the same courtesy from the other side.
|
|
338
|
+
*/
|
|
339
|
+
unicode-bidi: plaintext;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* The mark, on the text's own axis. `1cap` is the size a mark inside a line
|
|
344
|
+
* of text takes, and the `vertical-align` is the formula
|
|
345
|
+
* `docs/build-briefing.md` records under _Decisions already taken_: half the
|
|
346
|
+
* difference between the band and the mark, which lands the mark's centre on
|
|
347
|
+
* the middle of the band. The band is the midpoint of cap and x-height,
|
|
348
|
+
* chosen after all three were read at four times the type size.
|
|
349
|
+
*
|
|
350
|
+
* The mark is the chevron the binding renders as the preview's first child,
|
|
351
|
+
* marked `directional`: it points along the reading direction, so the `icon`
|
|
352
|
+
* family turns it in a right-to-left context.
|
|
353
|
+
*/
|
|
354
|
+
[data-ui='datetime-field'] [data-part='preview'] > [data-ui='icon'] {
|
|
355
|
+
--slotted-icon-size: var(--_mark);
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* The gap is on both sides rather than on the end alone. The preview's own
|
|
359
|
+
* direction is resolved from its content — `12 set, 14:40` reads
|
|
360
|
+
* left-to-right, `15:20` has no strong character and takes the paragraph's
|
|
361
|
+
* — so which side of the mark the words land on is not known here. A single
|
|
362
|
+
* end margin left the chevron flush against the digits in exactly the row
|
|
363
|
+
* that had no strong character to state a direction with.
|
|
364
|
+
*/
|
|
365
|
+
margin-inline: var(--slotted-space-1, 0.125rem);
|
|
366
|
+
vertical-align: calc((((1cap + 1ex) / 2) - var(--_mark)) / 2);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* A duration that landed on a different day.
|
|
371
|
+
*
|
|
372
|
+
* `time-field` paints this in the warning tone, because a `PlainTime` has no
|
|
373
|
+
* day and the crossing is a fact the reader has to be warned about. Here the
|
|
374
|
+
* day is part of the value: `+2d` is *supposed* to move it, and a warning
|
|
375
|
+
* every time the reader asks for one would be crying wolf. What the preview
|
|
376
|
+
* does instead is say the date out loud — the family writes it into the
|
|
377
|
+
* preview whenever it differs from today — and this rule only lifts the date
|
|
378
|
+
* out of the surrounding time so the eye lands on the part that moved.
|
|
379
|
+
*/
|
|
380
|
+
[data-ui='datetime-field'] [data-part='preview'][data-rolls-day] > [data-part='preview-date'] {
|
|
381
|
+
color: var(--slotted-datetime-field-preview-day-color, var(--slotted-tone-accent-text));
|
|
382
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* While an expression is open the frame says so, because the control now
|
|
387
|
+
* takes text where a moment ago it took digits one field at a time. The
|
|
388
|
+
* border is the accent the preview is written in, so the two marks are one
|
|
389
|
+
* statement rather than two.
|
|
390
|
+
*/
|
|
391
|
+
[data-ui='datetime-field'][data-part='root'][data-relative] {
|
|
392
|
+
border-color: var(
|
|
393
|
+
--slotted-datetime-field-relative-border-color,
|
|
394
|
+
var(--slotted-tone-accent-border)
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* ── Size ─────────────────────────────────────────────────────────────
|
|
399
|
+
*
|
|
400
|
+
* Three steps, matching the field the control sits in. A `size="sm"` field
|
|
401
|
+
* holding an `md` control is a field and a control disagreeing about one row.
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
[data-ui='datetime-field'][data-part='root'][data-size='sm'] {
|
|
405
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
406
|
+
--_height: var(--slotted-control-height-sm, 30px);
|
|
407
|
+
--_padding-inline: var(--slotted-control-padding-inline-sm, 10px);
|
|
408
|
+
--_gap: var(--slotted-control-gap-sm, 6px);
|
|
409
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-sm, 14px));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
[data-ui='datetime-field'][data-part='root'][data-size='md'] {
|
|
413
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
414
|
+
--_height: var(--slotted-control-height-md, 34px);
|
|
415
|
+
--_padding-inline: var(--slotted-control-padding-inline-md, 13px);
|
|
416
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
417
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-md, 16px));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
[data-ui='datetime-field'][data-part='root'][data-size='lg'] {
|
|
421
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
422
|
+
--_height: var(--slotted-control-height-lg, 38px);
|
|
423
|
+
--_padding-inline: var(--slotted-control-padding-inline-lg, 16px);
|
|
424
|
+
--_gap: var(--slotted-control-gap-lg, 8px);
|
|
425
|
+
--_icon-size: max(var(--_icon-floor), var(--slotted-control-icon-size-lg, 18px));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* ── Variant ──────────────────────────────────────────────────────────
|
|
429
|
+
*
|
|
430
|
+
* The three steps `text-field` set, so a form holding a name and a datetime
|
|
431
|
+
* shows one frame twice rather than two frames.
|
|
432
|
+
*/
|
|
433
|
+
|
|
434
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='subtle'] {
|
|
435
|
+
background-color: var(
|
|
436
|
+
--slotted-datetime-field-subtle-background,
|
|
437
|
+
var(--slotted-surface-sunken, Canvas)
|
|
438
|
+
);
|
|
439
|
+
border-color: var(
|
|
440
|
+
--slotted-datetime-field-subtle-border-color,
|
|
441
|
+
var(--slotted-border-subtle, ButtonBorder)
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'] {
|
|
446
|
+
background-color: transparent;
|
|
447
|
+
border-color: transparent;
|
|
448
|
+
border-radius: var(--slotted-radius-none, 0px);
|
|
449
|
+
box-shadow: none;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* ── Adornments ───────────────────────────────────────────────────────
|
|
453
|
+
*
|
|
454
|
+
* Named for the reading direction rather than for a side, and quieter than
|
|
455
|
+
* the value: an adornment annotates, and a reader looking for the datetime
|
|
456
|
+
* should not have to step over it.
|
|
457
|
+
*/
|
|
458
|
+
[data-ui='datetime-field'] > [data-part='start'],
|
|
459
|
+
[data-ui='datetime-field'] > [data-part='end'] {
|
|
460
|
+
--slotted-icon-size: var(--_icon-size);
|
|
461
|
+
|
|
462
|
+
align-items: center;
|
|
463
|
+
color: var(--slotted-datetime-field-adornment-color, var(--slotted-text-muted, GrayText));
|
|
464
|
+
display: inline-flex;
|
|
465
|
+
flex: 0 0 auto;
|
|
466
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
467
|
+
line-height: 1;
|
|
468
|
+
unicode-bidi: plaintext;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
[data-ui='datetime-field'] > [data-part='start'] > svg,
|
|
472
|
+
[data-ui='datetime-field'] > [data-part='end'] > svg {
|
|
473
|
+
block-size: var(--_icon-size);
|
|
474
|
+
inline-size: var(--_icon-size);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* ── The clear button ─────────────────────────────────────────────────
|
|
478
|
+
*
|
|
479
|
+
* A real button, with the `Glyph` element the binding renders rather than a
|
|
480
|
+
* character, and held to the target minimum by the family rather than by the
|
|
481
|
+
* page. It empties both halves: half a value is not a value this family
|
|
482
|
+
* holds.
|
|
483
|
+
*/
|
|
484
|
+
[data-ui='datetime-field'] > [data-part='clear'] {
|
|
485
|
+
align-items: center;
|
|
486
|
+
appearance: none;
|
|
487
|
+
background-color: transparent;
|
|
488
|
+
block-size: var(--_action-size);
|
|
489
|
+
border: 0;
|
|
490
|
+
border-radius: var(--slotted-datetime-field-radius, var(--slotted-control-radius, 4px));
|
|
491
|
+
color: var(--slotted-datetime-field-action-color, var(--slotted-text-muted, GrayText));
|
|
492
|
+
cursor: pointer;
|
|
493
|
+
display: inline-flex;
|
|
494
|
+
flex: 0 0 auto;
|
|
495
|
+
inline-size: var(--_action-size);
|
|
496
|
+
justify-content: center;
|
|
497
|
+
padding: 0;
|
|
498
|
+
position: relative;
|
|
499
|
+
touch-action: manipulation;
|
|
500
|
+
transition:
|
|
501
|
+
background-color var(--slotted-control-transition-duration)
|
|
502
|
+
var(--slotted-control-transition-easing),
|
|
503
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
504
|
+
-webkit-tap-highlight-color: transparent;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* The hit area, recovered outside the paint. At `sm` with `dense` the whole
|
|
509
|
+
* control is 24 pixels tall — the WCAG 2.2 minimum — so nothing drawn inside
|
|
510
|
+
* it can also be 24. This grows the target back in both axes and leaves a
|
|
511
|
+
* larger one alone, which is the correction `docs/token-foundation.md`
|
|
512
|
+
* leaves to the family rather than to the theme.
|
|
513
|
+
*/
|
|
514
|
+
[data-ui='datetime-field'] > [data-part='clear']::after {
|
|
515
|
+
content: '';
|
|
516
|
+
inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
|
|
517
|
+
position: absolute;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/* On a finger the target is the comfortable size in both axes and the box
|
|
521
|
+
is the one a mouse sees: the ground and the focus ring stay square, and
|
|
522
|
+
the hit area above grows to 44 pixels around them. After an end
|
|
523
|
+
adornment — a picker's trigger — it does not reach back across it. */
|
|
524
|
+
@media (pointer: coarse) {
|
|
525
|
+
[data-ui='datetime-field'] > [data-part='clear']::after {
|
|
526
|
+
inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
[data-ui='datetime-field'] > [data-part='end'] + [data-part='clear']::after {
|
|
530
|
+
inset-inline-start: 0;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/* The cross is drawn at the control's own icon step. */
|
|
535
|
+
[data-ui='datetime-field'] > [data-part='clear'] {
|
|
536
|
+
--slotted-icon-size: var(--_icon-size);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
[data-ui='datetime-field'] > [data-part='clear']:hover:not(:disabled):not([data-disabled]) {
|
|
540
|
+
background-color: var(
|
|
541
|
+
--slotted-datetime-field-action-hover-background,
|
|
542
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
[data-ui='datetime-field'] > [data-part='clear']:active:not(:disabled):not([data-disabled]) {
|
|
547
|
+
background-color: var(
|
|
548
|
+
--slotted-datetime-field-action-active-background,
|
|
549
|
+
var(--slotted-tone-secondary-subtle-active)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* The button carries its own ring rather than borrowing the frame's. Tabbing
|
|
555
|
+
* from the last segment to the clear button has to move something visible,
|
|
556
|
+
* and a ring around the whole frame would sit still while focus moved inside
|
|
557
|
+
* it.
|
|
558
|
+
*/
|
|
559
|
+
[data-ui='datetime-field'] > [data-part='clear']:focus-visible {
|
|
560
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
561
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* ── States ───────────────────────────────────────────────────────────
|
|
565
|
+
*
|
|
566
|
+
* Written strongest last, and each weaker one guarded against every stronger
|
|
567
|
+
* one it can meet, which is the order `STATE_PRECEDENCE` sets.
|
|
568
|
+
*/
|
|
569
|
+
|
|
570
|
+
[data-ui='datetime-field'][data-part='root']:hover:not([data-disabled]):not([data-readonly]):not(
|
|
571
|
+
[data-invalid]
|
|
572
|
+
):not([data-incomplete]) {
|
|
573
|
+
border-color: var(
|
|
574
|
+
--slotted-datetime-field-hover-border-color,
|
|
575
|
+
var(--slotted-tone-accent-border)
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='subtle']:hover:not(
|
|
580
|
+
[data-disabled]
|
|
581
|
+
):not([data-readonly]):not([data-invalid]) {
|
|
582
|
+
background-color: var(
|
|
583
|
+
--slotted-datetime-field-subtle-hover-background,
|
|
584
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain']:hover:not([data-disabled]):not(
|
|
589
|
+
[data-readonly]
|
|
590
|
+
):not([data-invalid]) {
|
|
591
|
+
background-color: var(
|
|
592
|
+
--slotted-datetime-field-plain-hover-background,
|
|
593
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
594
|
+
);
|
|
595
|
+
border-color: transparent;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* One half written and the other empty.
|
|
600
|
+
*
|
|
601
|
+
* It is not `invalid`: nothing the reader wrote is wrong, and a form that
|
|
602
|
+
* shouted at a half-typed value would shout at every value on the way in. It
|
|
603
|
+
* is not silence either — a date sitting above an empty time is exactly the
|
|
604
|
+
* state a reader walks away from, and G3 of the inventory is that walk. So
|
|
605
|
+
* the frame takes the warning border, one tone short of the danger the
|
|
606
|
+
* refused value gets, and only once the reader has left the control: while a
|
|
607
|
+
* segment inside still has focus they are mid-entry and there is nothing to
|
|
608
|
+
* report yet.
|
|
609
|
+
*
|
|
610
|
+
* It sits under `invalid` and above `hover` in the precedence order, and each
|
|
611
|
+
* of the two is guarded by specificity rather than by source order.
|
|
612
|
+
*
|
|
613
|
+
* **The tone is the second signal here, not the first.** The high-contrast
|
|
614
|
+
* scheme collapses every tone border to black — the theme's decision, and the
|
|
615
|
+
* one `invalid` already lives with — so a state carried by colour alone would
|
|
616
|
+
* have nothing left to say there. It is not carried by colour: the missing
|
|
617
|
+
* half is drawing its placeholder, in the reader's own words, beside a half
|
|
618
|
+
* that is drawing digits. That contrast is shape and it survives every scheme,
|
|
619
|
+
* forced colours, and a reader who cannot tell amber from grey, which is what
|
|
620
|
+
* `docs/component-features.md` §2.5 asks for. The border is what makes the
|
|
621
|
+
* whole control legible at a glance from across a long form.
|
|
622
|
+
*
|
|
623
|
+
* A broken border was drawn here instead and read as an alarm rather than a
|
|
624
|
+
* reminder — louder at rest than `invalid` is, for a state that is weaker
|
|
625
|
+
* than it.
|
|
626
|
+
*/
|
|
627
|
+
[data-ui='datetime-field'][data-part='root'][data-incomplete]:not([data-disabled]):not(
|
|
628
|
+
[data-readonly]
|
|
629
|
+
):not([data-invalid]):not(:focus-within) {
|
|
630
|
+
border-color: var(
|
|
631
|
+
--slotted-datetime-field-incomplete-border-color,
|
|
632
|
+
var(--slotted-tone-warning-border)
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Focus is two marks and they answer two different questions.
|
|
638
|
+
*
|
|
639
|
+
* The frame's ring says the control has the reader's attention, exactly as
|
|
640
|
+
* every other control in the library says it. The band on one segment says
|
|
641
|
+
* which field a key will move, and no ring can say that: a second outline
|
|
642
|
+
* two pixels inside the first would be two rings nested, and at `dense`
|
|
643
|
+
* there is not room for two.
|
|
644
|
+
*
|
|
645
|
+
* Both are `:focus` and not `:focus-visible`, which is the rule `date-field`
|
|
646
|
+
* paid for: a browser matches `:focus-visible` on a text input for a pointer
|
|
647
|
+
* press as well as for a Tab, and refuses it for a `div` a pointer focused.
|
|
648
|
+
* Every segment here is a `div`, so a clicked field would look exactly as it
|
|
649
|
+
* had before the press. The expression is an `input` and takes the frame's
|
|
650
|
+
* ring the same way from the other side.
|
|
651
|
+
*/
|
|
652
|
+
[data-ui='datetime-field'][data-part='root']:has([data-part='segment']:focus),
|
|
653
|
+
[data-ui='datetime-field'][data-part='root']:has([data-part='duration']:focus) {
|
|
654
|
+
box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
|
|
655
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
656
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
657
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* The ring of a control whose box is not its own is drawn inside that box,
|
|
662
|
+
* the way the inner controls draw theirs inside the frame: two pixels past
|
|
663
|
+
* the control is two pixels over the next row's rule. No lift either — a
|
|
664
|
+
* shadow that appears only under focus is a cell that changes size when it
|
|
665
|
+
* is used.
|
|
666
|
+
*/
|
|
667
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain']:has(
|
|
668
|
+
[data-part='segment']:focus
|
|
669
|
+
),
|
|
670
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain']:has(
|
|
671
|
+
[data-part='duration']:focus
|
|
672
|
+
) {
|
|
673
|
+
box-shadow: none;
|
|
674
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
[data-ui='datetime-field'] [data-part='segment']:focus {
|
|
678
|
+
background-color: var(
|
|
679
|
+
--slotted-datetime-field-segment-focus-background,
|
|
680
|
+
var(--slotted-tone-accent-solid)
|
|
681
|
+
);
|
|
682
|
+
color: var(--slotted-datetime-field-segment-focus-color, var(--slotted-tone-accent-on-solid));
|
|
683
|
+
/* The frame already draws the ring, so the segment does not draw a second. */
|
|
684
|
+
outline: none;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Invalid outranks hover and incomplete and is written after both: a control
|
|
689
|
+
* reporting an error should not stop looking wrong because the pointer is
|
|
690
|
+
* over it, and a value a bound refuses is wrong whether or not it is whole.
|
|
691
|
+
* The ring is retargeted rather than repainted, so the frame's outline, the
|
|
692
|
+
* shadow under it and the ring on the clear button all move at once.
|
|
693
|
+
*/
|
|
694
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid] {
|
|
695
|
+
--slotted-focus-ring-color: var(
|
|
696
|
+
--slotted-datetime-field-invalid-focus-ring-color,
|
|
697
|
+
var(--slotted-focus-ring-color-invalid, Highlight)
|
|
698
|
+
);
|
|
699
|
+
--_invalid-edge-width: var(
|
|
700
|
+
--slotted-datetime-field-invalid-edge-width,
|
|
701
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
702
|
+
);
|
|
703
|
+
--_invalid-edge-color: var(
|
|
704
|
+
--slotted-datetime-field-invalid-border-color,
|
|
705
|
+
var(--slotted-tone-danger-border)
|
|
706
|
+
);
|
|
707
|
+
--_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
|
|
708
|
+
|
|
709
|
+
border-color: var(
|
|
710
|
+
--slotted-datetime-field-invalid-border-color,
|
|
711
|
+
var(--slotted-tone-danger-border)
|
|
712
|
+
);
|
|
713
|
+
/*
|
|
714
|
+
* Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
|
|
715
|
+
* `high-contrast` the danger border is the resting edge's black. The line
|
|
716
|
+
* is a shadow inside the border, so neither box nor text moves, and it
|
|
717
|
+
* takes the resting lift's place.
|
|
718
|
+
*/
|
|
719
|
+
box-shadow: var(--_invalid-edge);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid]:has([data-part='segment']:focus),
|
|
723
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid]:has([data-part='duration']:focus) {
|
|
724
|
+
box-shadow:
|
|
725
|
+
var(--_invalid-edge),
|
|
726
|
+
var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
|
|
727
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
731
|
+
[data-part='segment']:focus
|
|
732
|
+
),
|
|
733
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
734
|
+
[data-part='duration']:focus
|
|
735
|
+
) {
|
|
736
|
+
/* The ring lies inside the box here, over the border and an edge as wide as
|
|
737
|
+
it; the edge reaches past the ring. */
|
|
738
|
+
box-shadow: inset 0 0 0
|
|
739
|
+
calc(
|
|
740
|
+
var(--slotted-focus-ring-offset, 2px) - var(--slotted-control-border-width, 1px) +
|
|
741
|
+
var(--_invalid-edge-width)
|
|
742
|
+
)
|
|
743
|
+
var(--_invalid-edge-color);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/*
|
|
747
|
+
* Forced colours remove every box shadow, and the edge with them. The
|
|
748
|
+
* width comes back as an outline inside the border, and a focused invalid
|
|
749
|
+
* control draws its ring double, which a valid ring is not.
|
|
750
|
+
*/
|
|
751
|
+
@media (forced-colors: active) {
|
|
752
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid] {
|
|
753
|
+
outline: var(--_invalid-edge-width) solid CanvasText;
|
|
754
|
+
outline-offset: calc(
|
|
755
|
+
-1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid]:has([data-part='segment']:focus),
|
|
760
|
+
[data-ui='datetime-field'][data-part='root'][data-invalid]:has([data-part='duration']:focus) {
|
|
761
|
+
outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
|
|
762
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
763
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
767
|
+
[data-part='segment']:focus
|
|
768
|
+
),
|
|
769
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'][data-invalid]:has(
|
|
770
|
+
[data-part='duration']:focus
|
|
771
|
+
) {
|
|
772
|
+
outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
|
|
773
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
774
|
+
outline-offset: calc(
|
|
775
|
+
-1 * (var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width))
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Read-only stays focusable, readable and copyable, and must not be
|
|
782
|
+
* mistakable for disabled: the value is still real and still submitted. The
|
|
783
|
+
* ground says "not yours to change" and the ink says nothing at all.
|
|
784
|
+
*/
|
|
785
|
+
[data-ui='datetime-field'][data-part='root'][data-readonly],
|
|
786
|
+
[data-ui='datetime-field'][data-part='root'][data-readonly] [data-part='segments'],
|
|
787
|
+
[data-ui='datetime-field'][data-part='root'][data-readonly] [data-part='segment'] {
|
|
788
|
+
cursor: default;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
[data-ui='datetime-field'][data-part='root'][data-readonly] {
|
|
792
|
+
background-color: var(
|
|
793
|
+
--slotted-datetime-field-readonly-background,
|
|
794
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Muted to the colour the theme measures rather than faded by opacity: a
|
|
800
|
+
* datetime nobody can read is a field nobody can check, and a disabled form
|
|
801
|
+
* still has to say what it holds.
|
|
802
|
+
*/
|
|
803
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] {
|
|
804
|
+
background-color: var(
|
|
805
|
+
--slotted-datetime-field-disabled-background,
|
|
806
|
+
var(--slotted-disabled-background)
|
|
807
|
+
);
|
|
808
|
+
border-color: var(
|
|
809
|
+
--slotted-datetime-field-disabled-border-color,
|
|
810
|
+
var(--slotted-disabled-border)
|
|
811
|
+
);
|
|
812
|
+
/* Nothing a person cannot reach is lifted off the page. */
|
|
813
|
+
box-shadow: none;
|
|
814
|
+
color: var(--slotted-datetime-field-disabled-color, var(--slotted-disabled-foreground));
|
|
815
|
+
cursor: not-allowed;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] [data-part='segments'],
|
|
819
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] [data-part='segment'] {
|
|
820
|
+
cursor: not-allowed;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] > [data-part='start'],
|
|
824
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] > [data-part='end'],
|
|
825
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled] [data-part='literal'],
|
|
826
|
+
[data-ui='datetime-field'][data-part='root'][data-disabled]
|
|
827
|
+
[data-part='segment'][data-placeholder] {
|
|
828
|
+
color: var(--slotted-disabled-foreground);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
@media (forced-colors: active) {
|
|
832
|
+
/* A transparent border has no system colour to be remapped to, so the
|
|
833
|
+
`plain` control would lose its only edge where an edge matters most. */
|
|
834
|
+
[data-ui='datetime-field'][data-part='root'][data-variant='plain'] {
|
|
835
|
+
border-color: ButtonBorder;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/* Warning, accent and danger all map to the same system ink, so the two
|
|
839
|
+
modes that are told apart by tone need a second signal. The expression
|
|
840
|
+
borrows the highlight; the incomplete frame borrows the thicker border
|
|
841
|
+
the system reserves for something asking to be finished. */
|
|
842
|
+
[data-ui='datetime-field'][data-part='root'][data-relative] {
|
|
843
|
+
border-color: Highlight;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/* The broken line above is already shape rather than tone, so forced
|
|
847
|
+
colours need nothing added here; the rule is stated in the scheme block
|
|
848
|
+
and inherited. */
|
|
849
|
+
}
|
|
850
|
+
}
|