@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,846 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── The month grid ─────────────────────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* Seven columns of two-digit numbers, which makes this the densest grid the
|
|
6
|
+
* library draws and the one place where `dense` either holds or collapses.
|
|
7
|
+
* Everything below is sized from the control scales, so the cell shrinks
|
|
8
|
+
* with the type rather than independently of it, and the touch target is
|
|
9
|
+
* recovered outside the paint where the scale falls under the minimum.
|
|
10
|
+
*
|
|
11
|
+
* The grid is a real `table` with the `grid` role on it. The APG's own note
|
|
12
|
+
* is why nothing here writes `row`, `columnheader` or `gridcell`: those are
|
|
13
|
+
* implied by `tr`, `th` and `td`, and writing them again would be the
|
|
14
|
+
* duplication `patterns.md` refuses.
|
|
15
|
+
*/
|
|
16
|
+
[data-ui='calendar'][data-part='root'] {
|
|
17
|
+
/**
|
|
18
|
+
* The reflow container. It is on the root and the grids are one element
|
|
19
|
+
* in, because `container-type: inline-size` makes an element a query
|
|
20
|
+
* container for its descendants and not for itself — the rule the briefing
|
|
21
|
+
* records, and the reason `stepper` and `description-list` have the same
|
|
22
|
+
* split.
|
|
23
|
+
*/
|
|
24
|
+
container-type: inline-size;
|
|
25
|
+
container-name: slotted-calendar;
|
|
26
|
+
|
|
27
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
28
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
29
|
+
--_icon-size: var(--slotted-control-icon-size-md, 16px);
|
|
30
|
+
/**
|
|
31
|
+
* A cell is square, and its side is the control height rather than a scale
|
|
32
|
+
* of its own: a calendar cell is a control the reader presses, and one
|
|
33
|
+
* drawn to a different rhythm from the buttons above it reads as a second
|
|
34
|
+
* component. `dense` takes it to 26 pixels, above the WCAG minimum, and
|
|
35
|
+
* the coarse-pointer block the theme emits takes it to 44 on touch.
|
|
36
|
+
*/
|
|
37
|
+
--_cell-size: var(--slotted-calendar-cell-size, var(--slotted-control-height-md, 34px));
|
|
38
|
+
/** Seven days, four months, four years: the shape of the three views. */
|
|
39
|
+
--_columns: 7;
|
|
40
|
+
--_months: 1;
|
|
41
|
+
--_month-gap: var(--slotted-space-6, 1rem);
|
|
42
|
+
/** The week number is a column of its own when a product asks for one. */
|
|
43
|
+
--_week-number-columns: 0;
|
|
44
|
+
--_grid-size: calc(var(--_cell-size) * (var(--_columns) + var(--_week-number-columns)));
|
|
45
|
+
|
|
46
|
+
background-color: var(--slotted-calendar-background, transparent);
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
color: var(--slotted-calendar-color, var(--slotted-text, CanvasText));
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
font-family: var(--slotted-control-font-family, system-ui, sans-serif);
|
|
52
|
+
font-size: var(--_font-size);
|
|
53
|
+
gap: var(--_gap);
|
|
54
|
+
/**
|
|
55
|
+
* The width is stated from the scales rather than measured from the grid
|
|
56
|
+
* inside, and it has to be: `container-type: inline-size` applies
|
|
57
|
+
* `contain: inline-size`, so the contents no longer contribute anything to
|
|
58
|
+
* the element's own width. Written `fit-content`, the root resolved to
|
|
59
|
+
* almost nothing and the heading was clipped to one character while the
|
|
60
|
+
* table overflowed it — a picture that looked like a calendar and was a
|
|
61
|
+
* collapsed box, which is the defect `references/stories.md` records for
|
|
62
|
+
* `pagination` and the audit exists to catch.
|
|
63
|
+
*
|
|
64
|
+
* A stated width also survives the flex container inside a popover, where
|
|
65
|
+
* a contained element contributing nothing would collapse the surface.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* The row that pages the month is the other thing this width has to hold.
|
|
69
|
+
*
|
|
70
|
+
* `dense` takes a cell to 26 pixels, so seven columns are 182 — and
|
|
71
|
+
* `setembro de 2026` between two triggers needs rather more than that. The
|
|
72
|
+
* heading was clipped at both ends and read `etembro de 2026`, which is a
|
|
73
|
+
* month nobody can read above a grid whose only subject is that month.
|
|
74
|
+
* `contain: inline-size` means the contents cannot ask for the room
|
|
75
|
+
* themselves, so the floor is stated: twenty characters of the heading's
|
|
76
|
+
* own type, the three glyph boxes of the two triggers and the view mark,
|
|
77
|
+
* and the gaps between them. It is written in `ch` and in the icon scale
|
|
78
|
+
* rather than in pixels, so it moves with the type and with density instead
|
|
79
|
+
* of being a guess that is right at one step.
|
|
80
|
+
*
|
|
81
|
+
* At `comfortable` the grid is wider than the floor and nothing changes. A
|
|
82
|
+
* locale with a longer month name still loses its tail, from a floor high
|
|
83
|
+
* enough to keep the name readable, and `grids` centres the month under the
|
|
84
|
+
* row it belongs to.
|
|
85
|
+
*/
|
|
86
|
+
--_heading-floor: calc(20ch + 3 * var(--_icon-size) + 4 * var(--_gap));
|
|
87
|
+
|
|
88
|
+
inline-size: max(
|
|
89
|
+
calc(var(--_grid-size) * var(--_months) + var(--_month-gap) * (var(--_months) - 1)),
|
|
90
|
+
var(--_heading-floor)
|
|
91
|
+
);
|
|
92
|
+
max-inline-size: 100%;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[data-ui='calendar'][data-part='root'][data-week-numbers] {
|
|
96
|
+
--_week-number-columns: 0.75;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[data-ui='calendar'][data-part='root'][data-months='2'] {
|
|
100
|
+
--_months: 2;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-ui='calendar'][data-part='root'][data-months='3'] {
|
|
104
|
+
--_months: 3;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* ── Where the reflow happens ──────────────────────────────────────────
|
|
109
|
+
*
|
|
110
|
+
* The cell is not stepped at a breakpoint; it is the room there is, divided
|
|
111
|
+
* by the columns there are, bounded above by the control height and below by
|
|
112
|
+
* the WCAG 2.2 minimum. `100cqi` is the root's own inline size, and these
|
|
113
|
+
* two elements are the first that may read it — a container query, and a
|
|
114
|
+
* container query unit, resolve against an **ancestor** container, so the
|
|
115
|
+
* root setting its own cell size from its own width did nothing at all. The
|
|
116
|
+
* first 240-pixel capture of this family showed a calendar at full size
|
|
117
|
+
* overflowing its stage, with a rule in the sheet that looked like it
|
|
118
|
+
* should have prevented exactly that.
|
|
119
|
+
*
|
|
120
|
+
* Continuous rather than stepped, because a literal threshold cannot be
|
|
121
|
+
* right at three densities: seven comfortable columns are 238 pixels and
|
|
122
|
+
* seven dense ones are 182, so any number that fits one shrinks the other
|
|
123
|
+
* while it still had room. CSS forbids a custom property in a container
|
|
124
|
+
* condition, which is what makes `clamp()` the answer here rather than a
|
|
125
|
+
* query.
|
|
126
|
+
*/
|
|
127
|
+
[data-ui='calendar'] [data-part='header'],
|
|
128
|
+
[data-ui='calendar'] [data-part='grids'],
|
|
129
|
+
[data-ui='calendar'] [data-part='months'],
|
|
130
|
+
[data-ui='calendar'] [data-part='years'] {
|
|
131
|
+
--_cell-size: clamp(
|
|
132
|
+
var(--slotted-target-min, 24px),
|
|
133
|
+
calc(100cqi / (var(--_columns) + var(--_week-number-columns)) / var(--_months)),
|
|
134
|
+
var(--slotted-calendar-cell-size, var(--slotted-control-height-md, 34px))
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ── The heading row ──────────────────────────────────────────────────
|
|
139
|
+
*
|
|
140
|
+
* The month, and the two triggers that page it. The heading sits between
|
|
141
|
+
* them rather than beside them, because it is the thing being changed and a
|
|
142
|
+
* label at one end would make the pair read as two unrelated buttons.
|
|
143
|
+
*/
|
|
144
|
+
[data-ui='calendar'] [data-part='header'] {
|
|
145
|
+
align-items: center;
|
|
146
|
+
display: flex;
|
|
147
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
148
|
+
/* The row is as wide as the grid under it, so the two triggers sit over
|
|
149
|
+
the first and last columns rather than floating above the middle. */
|
|
150
|
+
inline-size: 100%;
|
|
151
|
+
justify-content: space-between;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The month and year, and the way up to the month and year views. It is a
|
|
156
|
+
* button because pressing it does something; it looks like a heading because
|
|
157
|
+
* that is what it says. The live region is on it: paging with a key changes
|
|
158
|
+
* nothing that focus lands on, so without this nothing announces the month.
|
|
159
|
+
*/
|
|
160
|
+
[data-ui='calendar'] [data-part='heading'] {
|
|
161
|
+
--slotted-icon-size: var(--_icon-size);
|
|
162
|
+
|
|
163
|
+
appearance: none;
|
|
164
|
+
background-color: transparent;
|
|
165
|
+
border: 0;
|
|
166
|
+
border-radius: var(--slotted-calendar-radius, var(--slotted-control-radius, 4px));
|
|
167
|
+
color: inherit;
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
display: inline-flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
172
|
+
font: inherit;
|
|
173
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
174
|
+
/**
|
|
175
|
+
* It takes the room between the two triggers, and gives it back only when
|
|
176
|
+
* there is not enough — `setembro de 2026` and two triggers are 239 pixels
|
|
177
|
+
* against a seven-column grid of 238, so without this the trigger at the
|
|
178
|
+
* end fell to a second line.
|
|
179
|
+
*
|
|
180
|
+
* The first capture of this family showed the opposite failure, and it is
|
|
181
|
+
* worth naming because the two look like the same rule: the heading read
|
|
182
|
+
* `d`, one character, with the grid overflowing under it. That was the
|
|
183
|
+
* root's `inline-size: fit-content` under `contain: inline-size`, not this
|
|
184
|
+
* — a contained root has no contents to be sized by, so it collapsed and
|
|
185
|
+
* took the heading with it. With the root's width stated, shrinking here
|
|
186
|
+
* is bounded by the grid and stops long before a character.
|
|
187
|
+
*/
|
|
188
|
+
flex: 1 1 auto;
|
|
189
|
+
/**
|
|
190
|
+
* `safe`, and it is the whole difference between a month that is cut and a
|
|
191
|
+
* month that is unreadable.
|
|
192
|
+
*
|
|
193
|
+
* Centred without it, an overflowing heading is clipped at **both** ends:
|
|
194
|
+
* at `dense` the row read `etembro de 2026` with the view mark sliced off,
|
|
195
|
+
* which is a month nobody can read above a grid whose only subject is that
|
|
196
|
+
* month. `safe` falls back to start alignment exactly when the item would
|
|
197
|
+
* overflow, so the name always begins where it begins and only its tail is
|
|
198
|
+
* lost. Found from `datetime-picker`'s panel, reproduced on this family
|
|
199
|
+
* alone at `dense` and 720 pixels.
|
|
200
|
+
*
|
|
201
|
+
* `text-overflow: ellipsis` stood here and did nothing: it applies to the
|
|
202
|
+
* inline content of a block container, and the label is an anonymous flex
|
|
203
|
+
* item beside the mark. It is removed rather than left to look like a
|
|
204
|
+
* guard that is holding.
|
|
205
|
+
*/
|
|
206
|
+
justify-content: safe center;
|
|
207
|
+
min-inline-size: 0;
|
|
208
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
209
|
+
padding-inline: var(--slotted-space-2, 0.25rem);
|
|
210
|
+
/* One line, whatever the locale writes: a month name that wrapped would
|
|
211
|
+
take the grid down with it. */
|
|
212
|
+
overflow: hidden;
|
|
213
|
+
white-space: nowrap;
|
|
214
|
+
transition: background-color var(--slotted-control-transition-duration)
|
|
215
|
+
var(--slotted-control-transition-easing);
|
|
216
|
+
-webkit-tap-highlight-color: transparent;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* The chevron after the name is a `Glyph` the binding draws. The way up is
|
|
220
|
+
closed at the top: a year view has nowhere further to go, so the binding
|
|
221
|
+
leaves the mark that offered it out rather than showing it disabled. */
|
|
222
|
+
|
|
223
|
+
/* ── The two page triggers ────────────────────────────────────────────
|
|
224
|
+
*
|
|
225
|
+
* The chevrons are `Glyph` elements in `directional` icon boxes, mirrored
|
|
226
|
+
* under `dir="rtl"` by the icon's own rule, which is how one glyph serves
|
|
227
|
+
* both document directions.
|
|
228
|
+
*/
|
|
229
|
+
[data-ui='calendar'] [data-part='previous'],
|
|
230
|
+
[data-ui='calendar'] [data-part='next'] {
|
|
231
|
+
--slotted-icon-size: var(--_icon-size);
|
|
232
|
+
|
|
233
|
+
align-items: center;
|
|
234
|
+
appearance: none;
|
|
235
|
+
background-color: transparent;
|
|
236
|
+
block-size: var(--_cell-size);
|
|
237
|
+
border: 0;
|
|
238
|
+
border-radius: var(--slotted-calendar-radius, var(--slotted-control-radius, 4px));
|
|
239
|
+
color: var(--slotted-calendar-trigger-color, var(--slotted-text-muted, GrayText));
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
display: inline-flex;
|
|
242
|
+
flex: 0 0 auto;
|
|
243
|
+
inline-size: var(--_cell-size);
|
|
244
|
+
justify-content: center;
|
|
245
|
+
padding: 0;
|
|
246
|
+
touch-action: manipulation;
|
|
247
|
+
transition:
|
|
248
|
+
background-color var(--slotted-control-transition-duration)
|
|
249
|
+
var(--slotted-control-transition-easing),
|
|
250
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
251
|
+
-webkit-tap-highlight-color: transparent;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* One glyph, turned. The chevron points along the writing direction, so the
|
|
256
|
+
* "previous" trigger is the "next" artwork rotated a half turn; the icon's
|
|
257
|
+
* RTL mirror composes with the turn, so both follow `dir` without a second
|
|
258
|
+
* glyph — the rule `pagination` set.
|
|
259
|
+
*/
|
|
260
|
+
[data-ui='calendar'] [data-part='previous'] > [data-ui='icon'] {
|
|
261
|
+
rotate: 180deg;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* ── The grids ────────────────────────────────────────────────────────
|
|
265
|
+
*
|
|
266
|
+
* One month, or two, or three. They wrap rather than scroll: a calendar
|
|
267
|
+
* narrower than its months is a calendar with fewer months, and a sideways
|
|
268
|
+
* scrollbar under a grid is a control nobody finds.
|
|
269
|
+
*/
|
|
270
|
+
[data-ui='calendar'] [data-part='grids'] {
|
|
271
|
+
display: flex;
|
|
272
|
+
/**
|
|
273
|
+
* A second month that does not fit goes under the first rather than being
|
|
274
|
+
* withdrawn. Hiding it needs a literal breakpoint — CSS forbids a custom
|
|
275
|
+
* property in a container condition — and one literal cannot be right at
|
|
276
|
+
* three densities: two months are 492 pixels at `comfortable` and 380 at
|
|
277
|
+
* `dense`, so any threshold that fits one hides a month that fits in the
|
|
278
|
+
* other. Wrapping is answered by the same arithmetic at every density, and
|
|
279
|
+
* it never takes away a month the consumer asked for.
|
|
280
|
+
*/
|
|
281
|
+
flex-wrap: wrap;
|
|
282
|
+
gap: var(--_month-gap);
|
|
283
|
+
justify-content: center;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
[data-ui='calendar'] [data-part='grid'] {
|
|
287
|
+
border-collapse: collapse;
|
|
288
|
+
/* A table sizes itself from its content and would give a 31-day column one
|
|
289
|
+
pixel more than a 30-day one. Fixed layout hands every column the same
|
|
290
|
+
width, which is what makes the seven read as a week. */
|
|
291
|
+
table-layout: fixed;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The month's own name. With one month it is out of the picture and not out
|
|
296
|
+
* of the document — the heading above already shows it, and a screen reader
|
|
297
|
+
* reaching the grid still needs it.
|
|
298
|
+
*/
|
|
299
|
+
[data-ui='calendar'] [data-part='grid-caption'] {
|
|
300
|
+
block-size: 1px;
|
|
301
|
+
clip-path: inset(50%);
|
|
302
|
+
inline-size: 1px;
|
|
303
|
+
overflow: hidden;
|
|
304
|
+
position: absolute;
|
|
305
|
+
white-space: nowrap;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* With more than one month it is shown, because the heading then names the
|
|
310
|
+
* span rather than a month and nothing else would say which grid is which.
|
|
311
|
+
* A reader looking at two grids and one title has to count columns to find
|
|
312
|
+
* out where October starts.
|
|
313
|
+
*/
|
|
314
|
+
[data-ui='calendar'][data-part='root']:is([data-months='2'], [data-months='3'])
|
|
315
|
+
[data-part='grid-caption'] {
|
|
316
|
+
block-size: auto;
|
|
317
|
+
clip-path: none;
|
|
318
|
+
color: var(--slotted-calendar-weekday-color, var(--slotted-text-muted, GrayText));
|
|
319
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
320
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
321
|
+
inline-size: auto;
|
|
322
|
+
overflow: visible;
|
|
323
|
+
padding-block-end: var(--slotted-space-2, 0.25rem);
|
|
324
|
+
position: static;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The weekday. Two characters is what a column can hold; the full name
|
|
329
|
+
* reaches assistive technology through `abbr`, which is the APG's own
|
|
330
|
+
* answer and the difference between hearing "Mo" and hearing "Monday".
|
|
331
|
+
*/
|
|
332
|
+
[data-ui='calendar'] [data-part='week-header'] {
|
|
333
|
+
block-size: var(--_cell-size);
|
|
334
|
+
color: var(--slotted-calendar-weekday-color, var(--slotted-text-muted, GrayText));
|
|
335
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
336
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
337
|
+
inline-size: var(--_cell-size);
|
|
338
|
+
padding: 0;
|
|
339
|
+
/* Never truncated. A weekday cut to one character is not a weekday, and
|
|
340
|
+
the locale already chose how short its own narrow form is. */
|
|
341
|
+
text-overflow: clip;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
[data-ui='calendar'] [data-part='week-number'] {
|
|
345
|
+
color: var(--slotted-calendar-week-number-color, var(--slotted-text-muted, GrayText));
|
|
346
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
347
|
+
font-variant-numeric: tabular-nums;
|
|
348
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
349
|
+
/* Narrower than a day: it annotates the row rather than belonging to it,
|
|
350
|
+
and a column as wide as a day would read as an eighth day. */
|
|
351
|
+
inline-size: calc(var(--_cell-size) * 0.75);
|
|
352
|
+
padding: 0;
|
|
353
|
+
/* The line that says the numbers are not part of the week. */
|
|
354
|
+
border-inline-end: var(--slotted-border-width-thin, 1px) solid
|
|
355
|
+
var(--slotted-calendar-week-number-border-color, var(--slotted-border-subtle, ButtonBorder));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
[data-ui='calendar'] [data-part='cell'] {
|
|
359
|
+
padding: 0;
|
|
360
|
+
/* The containing block of the range band below. */
|
|
361
|
+
position: relative;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The day the reader presses.
|
|
366
|
+
*
|
|
367
|
+
* It is the `td`'s child rather than the `td` itself, because the range band
|
|
368
|
+
* has to run edge to edge across a cell while the day itself stays a circle
|
|
369
|
+
* inside it — two shapes, and one element cannot be both.
|
|
370
|
+
*/
|
|
371
|
+
[data-ui='calendar'] [data-part='day'] {
|
|
372
|
+
/**
|
|
373
|
+
* A rule short of the cell on every side, in every cell.
|
|
374
|
+
*
|
|
375
|
+
* Two chosen days side by side are two solid boxes, and filling the cell
|
|
376
|
+
* put their rounded corners against each other with no air between —
|
|
377
|
+
* which reads as one shape with a dent rather than as two days. The inset
|
|
378
|
+
* is taken everywhere rather than only where it shows, so no day is a
|
|
379
|
+
* different size from the day beside it.
|
|
380
|
+
*/
|
|
381
|
+
--_inset: var(--slotted-border-width-thin, 1px);
|
|
382
|
+
|
|
383
|
+
align-items: center;
|
|
384
|
+
block-size: calc(var(--_cell-size) - var(--_inset) * 2);
|
|
385
|
+
inline-size: calc(var(--_cell-size) - var(--_inset) * 2);
|
|
386
|
+
margin: var(--_inset);
|
|
387
|
+
border-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
388
|
+
box-sizing: border-box;
|
|
389
|
+
color: inherit;
|
|
390
|
+
cursor: pointer;
|
|
391
|
+
display: flex;
|
|
392
|
+
font-variant-numeric: tabular-nums;
|
|
393
|
+
/**
|
|
394
|
+
* A column, and the second row is today's dot — reserved in every cell,
|
|
395
|
+
* painted in one. Left to today alone the mark either overlapped the digit
|
|
396
|
+
* or moved it, and a month whose ninth sits two pixels higher than its
|
|
397
|
+
* tenth is a month with a dent in it.
|
|
398
|
+
*/
|
|
399
|
+
flex-direction: column;
|
|
400
|
+
/* The digit and the dot are not the same thing, and a mark against the
|
|
401
|
+
descender of a nine reads as part of it. */
|
|
402
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
403
|
+
justify-content: center;
|
|
404
|
+
/* The digit's own box, and nothing more: the leading a paragraph needs is
|
|
405
|
+
air the cell has to give to the dot instead. */
|
|
406
|
+
line-height: 1;
|
|
407
|
+
/* Above the range band, which is painted on the cell behind it. */
|
|
408
|
+
position: relative;
|
|
409
|
+
transition:
|
|
410
|
+
background-color var(--slotted-control-transition-duration)
|
|
411
|
+
var(--slotted-control-transition-easing),
|
|
412
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
413
|
+
user-select: none;
|
|
414
|
+
-webkit-tap-highlight-color: transparent;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* The hit area, recovered outside the paint. At `dense` the cell is 26
|
|
419
|
+
* pixels and the WCAG 2.2 minimum is 24, so nothing is needed there; a theme
|
|
420
|
+
* whose control scale is smaller gets the target back without the family
|
|
421
|
+
* having to know which theme it is under.
|
|
422
|
+
*/
|
|
423
|
+
[data-ui='calendar'] [data-part='day']::after {
|
|
424
|
+
content: '';
|
|
425
|
+
inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
|
|
426
|
+
position: absolute;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* ── The month and year views ─────────────────────────────────────────
|
|
430
|
+
*
|
|
431
|
+
* Four columns rather than seven, and a cell wide enough for a month's short
|
|
432
|
+
* name. A reader entering a birth date must not press "previous month" four
|
|
433
|
+
* hundred times, which is what these two exist to prevent.
|
|
434
|
+
*/
|
|
435
|
+
[data-ui='calendar'] [data-part='months'],
|
|
436
|
+
[data-ui='calendar'] [data-part='years'] {
|
|
437
|
+
display: grid;
|
|
438
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
439
|
+
grid-template-columns: repeat(4, 1fr);
|
|
440
|
+
/* The same box the day grid occupies, so switching view does not resize
|
|
441
|
+
the surface the reader is looking at. */
|
|
442
|
+
inline-size: calc(var(--_cell-size) * var(--_columns));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
[data-ui='calendar'] [data-part='period'] {
|
|
446
|
+
align-items: center;
|
|
447
|
+
appearance: none;
|
|
448
|
+
background-color: transparent;
|
|
449
|
+
block-size: var(--_cell-size);
|
|
450
|
+
border: 0;
|
|
451
|
+
border-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
452
|
+
color: inherit;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
display: flex;
|
|
455
|
+
font: inherit;
|
|
456
|
+
justify-content: center;
|
|
457
|
+
padding-inline: var(--slotted-space-2, 0.25rem);
|
|
458
|
+
transition:
|
|
459
|
+
background-color var(--slotted-control-transition-duration)
|
|
460
|
+
var(--slotted-control-transition-easing),
|
|
461
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
462
|
+
-webkit-tap-highlight-color: transparent;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* ── What a cell can say ──────────────────────────────────────────────
|
|
466
|
+
*
|
|
467
|
+
* Seven facts, and every one of them paired with a shape rather than carried
|
|
468
|
+
* by colour alone. Under `forced-colors` a palette becomes four system
|
|
469
|
+
* colours, and every surveyed calendar loses six of these seven there.
|
|
470
|
+
*/
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Today. Marked apart from the selection, as §6 of the feature guide asks in
|
|
474
|
+
* those words: a reader who has chosen today must still see both facts.
|
|
475
|
+
*
|
|
476
|
+
* A dot rather than a ring, and the reason is in the first focus capture of
|
|
477
|
+
* this family: today was drawn as a ring and focus is drawn as a ring, so a
|
|
478
|
+
* reader arrowing onto today saw one box and could not tell which of the two
|
|
479
|
+
* it was. Two facts need two shapes. The dot is `currentcolor`, so it takes
|
|
480
|
+
* the ink of whatever state the day is in — the accent text at rest, the
|
|
481
|
+
* fill's own ink when the day is chosen, the disabled grey when nothing is
|
|
482
|
+
* operable — and it survives `forced-colors`, where `currentcolor` is
|
|
483
|
+
* already the system's ink.
|
|
484
|
+
*/
|
|
485
|
+
[data-ui='calendar'] [data-part='day'][data-today] {
|
|
486
|
+
color: var(--slotted-calendar-today-color, var(--slotted-tone-accent-text));
|
|
487
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
[data-ui='calendar'] [data-part='day']::before {
|
|
491
|
+
/* Reserved in every cell and painted in one, so the row keeps its line. */
|
|
492
|
+
background-color: transparent;
|
|
493
|
+
block-size: var(--slotted-space-2, 0.25rem);
|
|
494
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
495
|
+
content: '';
|
|
496
|
+
flex: 0 0 auto;
|
|
497
|
+
inline-size: var(--slotted-space-2, 0.25rem);
|
|
498
|
+
/* After the digit: a `::before` is the first flex item until it is told
|
|
499
|
+
otherwise, and the mark belongs under the number. */
|
|
500
|
+
order: 1;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
[data-ui='calendar'] [data-part='day'][data-today]::before {
|
|
504
|
+
background-color: currentcolor;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* A day of the month either side. Quiet enough to read as context and legible
|
|
509
|
+
* enough to press, because a reader who wants 1 October while looking at
|
|
510
|
+
* September should be able to.
|
|
511
|
+
*/
|
|
512
|
+
[data-ui='calendar'] [data-part='cell'][data-outside-month] [data-part='day'] {
|
|
513
|
+
color: var(--slotted-calendar-outside-color, var(--slotted-text-muted, GrayText));
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Refused by the predicate. It stays focusable — `aria-disabled`, never
|
|
518
|
+
* `disabled` — so a reader can arrow onto it and be told why, and the strike
|
|
519
|
+
* is what says so without colour.
|
|
520
|
+
*/
|
|
521
|
+
[data-ui='calendar'] [data-part='cell'][data-unavailable] [data-part='day'] {
|
|
522
|
+
color: var(--slotted-calendar-unavailable-color, var(--slotted-disabled-foreground));
|
|
523
|
+
cursor: not-allowed;
|
|
524
|
+
text-decoration: line-through;
|
|
525
|
+
text-decoration-thickness: var(--slotted-border-width-thin, 1px);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* The band behind a run.
|
|
530
|
+
*
|
|
531
|
+
* It is painted on the cell rather than on the day, so it runs edge to edge
|
|
532
|
+
* while the day keeps its own shape on top of it — two shapes, and one
|
|
533
|
+
* element cannot be both. It covers the whole cell at both ends as well as
|
|
534
|
+
* in the middle: starting it at the midpoint of the end cell left a sliver
|
|
535
|
+
* of band showing past the solid end, which read as an extra border hanging
|
|
536
|
+
* half a cell outside the run.
|
|
537
|
+
*
|
|
538
|
+
* **It has no edge, and it is not a tone surface.** `accent-surface` is a
|
|
539
|
+
* ground calibrated to sit under text rather than to separate from it, and
|
|
540
|
+
* in `high-contrast` it is the page's own white — so a run drawn with it
|
|
541
|
+
* disappeared in one scheme out of three. The rules that fixed that were
|
|
542
|
+
* worse: two horizontal lines that stopped at every row break and ran out
|
|
543
|
+
* from under both ends.
|
|
544
|
+
*
|
|
545
|
+
* The foundation already names the answer for a ground with no edge, and
|
|
546
|
+
* `highlight` already draws it: mix the tone's `text` role, the one role
|
|
547
|
+
* every scheme keeps legible over the surface, at the share
|
|
548
|
+
* `--slotted-highlight-tint-opacity` holds. One share, one gesture, all
|
|
549
|
+
* three schemes, and nothing to align across a row break.
|
|
550
|
+
*/
|
|
551
|
+
[data-ui='calendar'] [data-part='cell'][data-in-range]::before,
|
|
552
|
+
[data-ui='calendar'] [data-part='cell'][data-range-start]::before,
|
|
553
|
+
[data-ui='calendar'] [data-part='cell'][data-range-end]::before {
|
|
554
|
+
background-color: var(
|
|
555
|
+
--slotted-calendar-range-background,
|
|
556
|
+
color-mix(
|
|
557
|
+
in oklab,
|
|
558
|
+
var(--slotted-tone-accent-text) calc(var(--slotted-highlight-tint-opacity, 0.075) * 100%),
|
|
559
|
+
transparent
|
|
560
|
+
)
|
|
561
|
+
);
|
|
562
|
+
content: '';
|
|
563
|
+
inset: 0;
|
|
564
|
+
position: absolute;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* The run's two outer corners, and no inner ones: the band is one shape that
|
|
569
|
+
* begins and ends, and a square cut at either end reads as a run that was
|
|
570
|
+
* clipped rather than one that stops.
|
|
571
|
+
*/
|
|
572
|
+
[data-ui='calendar'] [data-part='cell'][data-range-start]::before {
|
|
573
|
+
border-end-start-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
574
|
+
border-start-start-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
[data-ui='calendar'] [data-part='cell'][data-range-end]::before {
|
|
578
|
+
border-end-end-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
579
|
+
border-start-end-radius: var(--slotted-calendar-day-radius, var(--slotted-radius-sm, 4px));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* A run has two outer corners and no inner ones. The end that faces into the
|
|
584
|
+
* range is square, so the solid meets the band edge to edge; the end that
|
|
585
|
+
* faces out keeps the day's own radius. A range of one day is both ends at
|
|
586
|
+
* once and stays round on both sides, which is why each rule excludes the
|
|
587
|
+
* other.
|
|
588
|
+
*/
|
|
589
|
+
[data-ui='calendar']
|
|
590
|
+
[data-part='cell'][data-range-start]:not([data-range-end])
|
|
591
|
+
[data-part='day'] {
|
|
592
|
+
border-end-end-radius: 0;
|
|
593
|
+
border-start-end-radius: 0;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
[data-ui='calendar']
|
|
597
|
+
[data-part='cell'][data-range-end]:not([data-range-start])
|
|
598
|
+
[data-part='day'] {
|
|
599
|
+
border-end-start-radius: 0;
|
|
600
|
+
border-start-start-radius: 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Inside the run the day gives up its inset and its corners, so the tint is
|
|
605
|
+
* continuous from one cell to the next and the run reads as one band rather
|
|
606
|
+
* than as a row of tinted squares with gaps between them.
|
|
607
|
+
*/
|
|
608
|
+
[data-ui='calendar']
|
|
609
|
+
[data-part='cell']:is([data-in-range], [data-range-start], [data-range-end])
|
|
610
|
+
[data-part='day'] {
|
|
611
|
+
--_inset: 0px;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* A day inside the run takes the accent's own text tone. The tint alone is a
|
|
616
|
+
* band read through; the ink is what says the day belongs to the run in a
|
|
617
|
+
* scheme where the band is faint, and it is the second half of the pair the
|
|
618
|
+
* foundation asks for where there is no edge to make one with.
|
|
619
|
+
*/
|
|
620
|
+
[data-ui='calendar'] [data-part='cell'][data-in-range] [data-part='day'] {
|
|
621
|
+
border-radius: 0;
|
|
622
|
+
color: var(--slotted-calendar-in-range-color, var(--slotted-tone-accent-text));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* ── The pending band ───────────────────────────────────────────────────
|
|
627
|
+
*
|
|
628
|
+
* While the run has one end, the band runs from it to the day the reader is
|
|
629
|
+
* on — under the pointer, or focused — and that band is a proposal rather
|
|
630
|
+
* than a choice. It takes the same tint at half the share, so the chosen run
|
|
631
|
+
* and the pending one are the same gesture at two weights, and the pending
|
|
632
|
+
* end takes an edge in the accent's border role rather than the solid the
|
|
633
|
+
* chosen end takes: a proposal is drawn as an outline, a choice as a fill.
|
|
634
|
+
*/
|
|
635
|
+
[data-ui='calendar'] [data-part='cell'][data-preview]::before {
|
|
636
|
+
background-color: var(
|
|
637
|
+
--slotted-calendar-preview-background,
|
|
638
|
+
color-mix(
|
|
639
|
+
in oklab,
|
|
640
|
+
var(--slotted-tone-accent-text) calc(var(--slotted-highlight-tint-opacity, 0.075) * 50%),
|
|
641
|
+
transparent
|
|
642
|
+
)
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
[data-ui='calendar']
|
|
647
|
+
[data-part='cell'][data-preview]:is([data-range-start], [data-range-end]):not([data-selected])
|
|
648
|
+
[data-part='day'] {
|
|
649
|
+
box-shadow: inset 0 0 0 var(--slotted-border-width-thin, 1px)
|
|
650
|
+
var(--slotted-calendar-preview-border-color, var(--slotted-tone-accent-border));
|
|
651
|
+
color: var(--slotted-calendar-in-range-color, var(--slotted-tone-accent-text));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* ── States ───────────────────────────────────────────────────────────
|
|
655
|
+
*
|
|
656
|
+
* Written strongest last, and each weaker one guarded against every stronger
|
|
657
|
+
* one it can meet, which is the order `STATE_PRECEDENCE` sets.
|
|
658
|
+
*/
|
|
659
|
+
|
|
660
|
+
[data-ui='calendar'] [data-part='day']:hover:not([data-disabled]),
|
|
661
|
+
[data-ui='calendar'] [data-part='period']:hover:not(:disabled):not([data-disabled]),
|
|
662
|
+
[data-ui='calendar'] [data-part='heading']:hover:not(:disabled):not([data-disabled]),
|
|
663
|
+
[data-ui='calendar'] [data-part='previous']:hover:not(:disabled):not([data-disabled]),
|
|
664
|
+
[data-ui='calendar'] [data-part='next']:hover:not(:disabled):not([data-disabled]) {
|
|
665
|
+
background-color: var(
|
|
666
|
+
--slotted-calendar-hover-background,
|
|
667
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
[data-ui='calendar'] [data-part='day']:active:not([data-disabled]),
|
|
672
|
+
[data-ui='calendar'] [data-part='period']:active:not(:disabled):not([data-disabled]),
|
|
673
|
+
[data-ui='calendar'] [data-part='previous']:active:not(:disabled):not([data-disabled]),
|
|
674
|
+
[data-ui='calendar'] [data-part='next']:active:not(:disabled):not([data-disabled]) {
|
|
675
|
+
background-color: var(
|
|
676
|
+
--slotted-calendar-active-background,
|
|
677
|
+
var(--slotted-tone-secondary-subtle-active)
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Inside a run the pointer takes the accent one step up rather than the
|
|
683
|
+
* neutral hover every other day takes. A grey wash over a blue band read as
|
|
684
|
+
* a hole in the run, and the reader's own selection is what the band is
|
|
685
|
+
* about — the pointer should deepen it, not neutralise it.
|
|
686
|
+
*/
|
|
687
|
+
[data-ui='calendar']
|
|
688
|
+
[data-part='cell'][data-in-range]
|
|
689
|
+
[data-part='day']:hover:not([data-disabled]) {
|
|
690
|
+
background-color: var(
|
|
691
|
+
--slotted-calendar-range-hover-background,
|
|
692
|
+
var(--slotted-tone-accent-subtle-hover)
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
[data-ui='calendar']
|
|
697
|
+
[data-part='cell'][data-in-range]
|
|
698
|
+
[data-part='day']:active:not([data-disabled]) {
|
|
699
|
+
background-color: var(
|
|
700
|
+
--slotted-calendar-range-active-background,
|
|
701
|
+
var(--slotted-tone-accent-subtle-active)
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* A date the predicate refused answers neither the pointer nor the press,
|
|
707
|
+
* and it is written after the two rules above for that reason: the guards
|
|
708
|
+
* they carry are for `data-disabled`, and this state is the family's own.
|
|
709
|
+
*/
|
|
710
|
+
[data-ui='calendar']
|
|
711
|
+
[data-part='cell'][data-unavailable]
|
|
712
|
+
[data-part='day']:hover:not([data-disabled]),
|
|
713
|
+
[data-ui='calendar']
|
|
714
|
+
[data-part='cell'][data-unavailable]
|
|
715
|
+
[data-part='day']:active:not([data-disabled]) {
|
|
716
|
+
background-color: transparent;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* The chosen date. A fill rather than a ring, so it and today can be true at
|
|
721
|
+
* once and still read as two facts.
|
|
722
|
+
*/
|
|
723
|
+
[data-ui='calendar'] [data-part='cell'][data-selected] [data-part='day'],
|
|
724
|
+
[data-ui='calendar'] [data-part='period'][data-selected] {
|
|
725
|
+
background-color: var(--slotted-calendar-selected-background, var(--slotted-tone-accent-solid));
|
|
726
|
+
color: var(--slotted-calendar-selected-color, var(--slotted-tone-accent-on-solid));
|
|
727
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
[data-ui='calendar'] [data-part='day']:focus-visible,
|
|
731
|
+
[data-ui='calendar'] [data-part='period']:focus-visible,
|
|
732
|
+
[data-ui='calendar'] [data-part='heading']:focus-visible,
|
|
733
|
+
[data-ui='calendar'] [data-part='previous']:focus-visible,
|
|
734
|
+
[data-ui='calendar'] [data-part='next']:focus-visible {
|
|
735
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
736
|
+
/* Inside the cell rather than outside it: an offset ring on a grid of
|
|
737
|
+
touching cells would overlap the four neighbours and read as a smear. */
|
|
738
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
[data-ui='calendar'][data-part='root'][data-invalid] {
|
|
742
|
+
--slotted-focus-ring-color: var(
|
|
743
|
+
--slotted-calendar-invalid-focus-ring-color,
|
|
744
|
+
var(--slotted-focus-ring-color-invalid, Highlight)
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/*
|
|
749
|
+
* Width as well as colour (WCAG 1.4.1): in `high-contrast` the invalid ring
|
|
750
|
+
* is the valid ring's black. The calendar has no frame to edge, so the ring
|
|
751
|
+
* is where invalid shows, and it widens by the edge every invalid control
|
|
752
|
+
* adds — inward, like the ring it replaces, so no neighbouring cell is
|
|
753
|
+
* covered.
|
|
754
|
+
*/
|
|
755
|
+
[data-ui='calendar'][data-part='root'][data-invalid] [data-part='day']:focus-visible,
|
|
756
|
+
[data-ui='calendar'][data-part='root'][data-invalid] [data-part='period']:focus-visible,
|
|
757
|
+
[data-ui='calendar'][data-part='root'][data-invalid] [data-part='heading']:focus-visible,
|
|
758
|
+
[data-ui='calendar'][data-part='root'][data-invalid] [data-part='previous']:focus-visible,
|
|
759
|
+
[data-ui='calendar'][data-part='root'][data-invalid] [data-part='next']:focus-visible {
|
|
760
|
+
--_invalid-ring: calc(
|
|
761
|
+
var(--slotted-focus-ring-width, 2px) +
|
|
762
|
+
var(--slotted-calendar-invalid-edge-width, var(--slotted-control-invalid-edge-width, 1px))
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
outline-offset: calc(var(--_invalid-ring) * -1);
|
|
766
|
+
outline-width: var(--_invalid-ring);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/* A calendar the reader may read and not change: the days stop offering
|
|
770
|
+
themselves, and nothing is faded, because the value is still real. */
|
|
771
|
+
[data-ui='calendar'][data-part='root'][data-readonly] [data-part='day'],
|
|
772
|
+
[data-ui='calendar'][data-part='root'][data-readonly] [data-part='period'] {
|
|
773
|
+
cursor: default;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
[data-ui='calendar'][data-part='root'][data-disabled] {
|
|
777
|
+
color: var(--slotted-calendar-disabled-color, var(--slotted-disabled-foreground));
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* Nothing a person cannot reach is painted in the tone that says "act on
|
|
781
|
+
me": today keeps its dot, in the ink a disabled control is allowed. */
|
|
782
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='day'][data-today] {
|
|
783
|
+
color: var(--slotted-disabled-foreground);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
[data-ui='calendar'][data-part='root'][data-disabled]
|
|
787
|
+
[data-part='cell'][data-selected]
|
|
788
|
+
[data-part='day'] {
|
|
789
|
+
background-color: var(--slotted-disabled-background);
|
|
790
|
+
color: var(--slotted-disabled-foreground);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='day'],
|
|
794
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='period'],
|
|
795
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='previous'],
|
|
796
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='next'],
|
|
797
|
+
[data-ui='calendar'][data-part='root'][data-disabled] [data-part='heading'] {
|
|
798
|
+
cursor: not-allowed;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
[data-ui='calendar'] [data-part='previous']:disabled,
|
|
802
|
+
[data-ui='calendar'] [data-part='next']:disabled {
|
|
803
|
+
color: var(--slotted-disabled-foreground);
|
|
804
|
+
cursor: not-allowed;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/* ── Reflow ───────────────────────────────────────────────────────────
|
|
808
|
+
*
|
|
809
|
+
* The component answers its own inline size, not the viewport's: a calendar
|
|
810
|
+
* in a 320-pixel drawer and the same calendar on a 1400-pixel page are the
|
|
811
|
+
* same width problem, and only a container query sees both.
|
|
812
|
+
*
|
|
813
|
+
* The breakpoints are literal because CSS forbids a custom property in a
|
|
814
|
+
* container condition, which `docs/token-foundation.md` records.
|
|
815
|
+
*/
|
|
816
|
+
/**
|
|
817
|
+
* The months close up once there is no room to keep them a full step apart.
|
|
818
|
+
* It is the one query left in the sheet, and it is on a descendant rather
|
|
819
|
+
* than on the root because a container cannot query itself.
|
|
820
|
+
*/
|
|
821
|
+
@container slotted-calendar (max-width: 320px) {
|
|
822
|
+
[data-ui='calendar'] [data-part='grids'] {
|
|
823
|
+
--_month-gap: var(--slotted-space-4, 0.5rem);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
@media (forced-colors: active) {
|
|
828
|
+
/**
|
|
829
|
+
* A mixed tint has nothing to mix in a palette the system replaced, so the
|
|
830
|
+
* run takes an edge here and only here — two rules the operating system
|
|
831
|
+
* keeps, on a band that would otherwise be the page's own ground.
|
|
832
|
+
*/
|
|
833
|
+
[data-ui='calendar'] [data-part='cell'][data-in-range]::before,
|
|
834
|
+
[data-ui='calendar'] [data-part='cell'][data-range-start]::before,
|
|
835
|
+
[data-ui='calendar'] [data-part='cell'][data-range-end]::before {
|
|
836
|
+
border-block: var(--slotted-border-width-thin, 1px) solid
|
|
837
|
+
var(--slotted-calendar-range-border-color, var(--slotted-tone-accent-border));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/* A proposal keeps its own distance from a choice here too: the pending
|
|
841
|
+
band's edges are dashed where the chosen run's are solid. */
|
|
842
|
+
[data-ui='calendar'] [data-part='cell'][data-preview]::before {
|
|
843
|
+
border-block-style: dashed;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|