@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,764 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── The message ────────────────────────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* A block of text about the page, a section, or the thing beside it, with a
|
|
6
|
+
* mark saying how serious it is and — sometimes — a control that puts it
|
|
7
|
+
* away and a row of controls that act on it.
|
|
8
|
+
*
|
|
9
|
+
* The box is a `section`, not a live region. MDN's `alert` role reference
|
|
10
|
+
* says the role "should only be used for text content, not interactive
|
|
11
|
+
* elements such as links or buttons", and every alert worth shipping has a
|
|
12
|
+
* dismiss control. Four of the ten surveyed implementations write a live
|
|
13
|
+
* role on the box anyway and put buttons inside it; two write no live region
|
|
14
|
+
* at all and announce nothing. Here the region is `content`, which holds the
|
|
15
|
+
* title and the description and nothing else, and the actions and the
|
|
16
|
+
* dismiss control are its siblings outside it.
|
|
17
|
+
*
|
|
18
|
+
* The family owns its own container, because two of its layout decisions
|
|
19
|
+
* answer the width of the box rather than the width of the window: a run of
|
|
20
|
+
* actions stacks and stretches when the box is narrow, and a page banner
|
|
21
|
+
* puts its actions back on the message's own line when the box is wide.
|
|
22
|
+
*/
|
|
23
|
+
[data-ui='alert'][data-part='root'] {
|
|
24
|
+
/**
|
|
25
|
+
* The tone's five roles, defaulted to `accent` so an alert with no stated
|
|
26
|
+
* tone is an informational one. Each tone below restates the same five;
|
|
27
|
+
* every one of them is a role the theme's contrast test measures, so the
|
|
28
|
+
* family never invents a colour and never mixes a ground the theme holds
|
|
29
|
+
* with a foreground it does not.
|
|
30
|
+
*/
|
|
31
|
+
--_solid: var(--slotted-tone-accent-solid);
|
|
32
|
+
--_on-solid: var(--slotted-tone-accent-on-solid);
|
|
33
|
+
--_surface: var(--slotted-tone-accent-surface);
|
|
34
|
+
--_border: var(--slotted-tone-accent-border);
|
|
35
|
+
--_text: var(--slotted-tone-accent-text);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* How the dismiss control answers a pointer, and it is one mechanism for
|
|
39
|
+
* all three variants rather than three.
|
|
40
|
+
*
|
|
41
|
+
* `tag` measured why the tone's own ramp cannot do it: in the light scheme
|
|
42
|
+
* `--slotted-tone-accent-subtle-hover` is #eff6ff against a #dbeafe ground
|
|
43
|
+
* and `subtle-active` is that ground exactly, so a control on a tinted
|
|
44
|
+
* alert either lightened on hover or did nothing at all on press. On a
|
|
45
|
+
* `solid` alert the same roles are calibrated against the page rather than
|
|
46
|
+
* against the tone's fill, and on `outline` the ground is not the family's
|
|
47
|
+
* to know.
|
|
48
|
+
*
|
|
49
|
+
* A state layer answers all three: the control's own text colour, at a low
|
|
50
|
+
* strength, over whatever ground it already has. `currentcolor` is legible
|
|
51
|
+
* on that ground by construction — it is the colour the words are read in —
|
|
52
|
+
* so a wash of it is always perceptible and never depends on a pair the
|
|
53
|
+
* theme does not measure. The cost is that a theme retunes it by strength
|
|
54
|
+
* rather than by colour, which is what the two component tokens are for.
|
|
55
|
+
*
|
|
56
|
+
* Ten and sixteen per cent, not eight and fourteen: white at eight per cent
|
|
57
|
+
* over the `danger` fill moved OKLab lightness by 0.019 in the light and
|
|
58
|
+
* dark schemes, under the 0.02 a surface step has to reach. Ten moves it by
|
|
59
|
+
* 0.025 there and by at least 0.044 on every soft and outline ground
|
|
60
|
+
* (`docs/history/designs/2026-09-25-alert-reui-dossier.md` § 4).
|
|
61
|
+
*/
|
|
62
|
+
--_state-hover: color-mix(
|
|
63
|
+
in oklab,
|
|
64
|
+
currentcolor var(--slotted-alert-state-layer-hover, 10%),
|
|
65
|
+
transparent
|
|
66
|
+
);
|
|
67
|
+
--_state-active: color-mix(
|
|
68
|
+
in oklab,
|
|
69
|
+
currentcolor var(--slotted-alert-state-layer-active, 16%),
|
|
70
|
+
transparent
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Type and the dismiss glyph come from the control scales, the only ones
|
|
75
|
+
* the theme restates per density, so an alert inside a dense table shrinks
|
|
76
|
+
* with the rows around it. The box's inset comes from the space scale,
|
|
77
|
+
* which does not vary by density — the cost `card` accepted for the same
|
|
78
|
+
* reason, and recorded in
|
|
79
|
+
* `docs/history/designs/2026-09-02-alert-family-design.md`: at `dense`
|
|
80
|
+
* the box keeps its inset while the type inside it shrinks.
|
|
81
|
+
*
|
|
82
|
+
* `--_font-size` is the *title's* size and the root's own, and the
|
|
83
|
+
* description steps down from it. That is what lets the mark be sized in
|
|
84
|
+
* `em` and land on the message's first line at every reach without a
|
|
85
|
+
* number saying how.
|
|
86
|
+
*
|
|
87
|
+
* The status mark is a filled disc a step above the title's `1em`, rounded
|
|
88
|
+
* to an even number of pixels so its centre falls on a whole pixel: 16 at
|
|
89
|
+
* 13-pixel type, 14 at 12, 16 at 14. A filled mark carries more weight per
|
|
90
|
+
* pixel than an outline, so it stays within a few pixels of the capitals
|
|
91
|
+
* and still reads as the loudest thing on the line.
|
|
92
|
+
*/
|
|
93
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
94
|
+
--_description-font-size: var(--slotted-control-font-size-md, 13px);
|
|
95
|
+
--_glyph-size: var(--slotted-control-icon-size-md, 16px);
|
|
96
|
+
--_inner-size: var(--slotted-control-inner-size-md, 24px);
|
|
97
|
+
--_mark-size: round(nearest, 1.2em, 2px);
|
|
98
|
+
--_line-height: var(--slotted-line-height-md, 1.5);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Each measure is named once, and a reach moves only the value it falls
|
|
102
|
+
* back to. An application that retunes `--slotted-alert-padding-block`
|
|
103
|
+
* therefore retunes all three reaches, rather than discovering that the
|
|
104
|
+
* name it set is the one the default reach happens to read.
|
|
105
|
+
*/
|
|
106
|
+
--_inset-block: var(--slotted-alert-padding-block, var(--slotted-space-5, 0.75rem));
|
|
107
|
+
--_inset-inline: var(--slotted-alert-padding-inline, var(--slotted-space-6, 1rem));
|
|
108
|
+
/**
|
|
109
|
+
* The distance from the mark to the words it belongs to: eight pixels at
|
|
110
|
+
* every reach (R5). One step below the box's own inset on purpose: read at
|
|
111
|
+
* 420 pixels with the inset and the gap equal, the mark sat as far from its
|
|
112
|
+
* own sentence as it did from the edge of the box, and belonged to neither.
|
|
113
|
+
* It does not step with the reach — the mark already does, through `em`,
|
|
114
|
+
* and a gap that shrank beside a smaller mark pulled the two apart twice.
|
|
115
|
+
*/
|
|
116
|
+
--_column-gap: var(--slotted-alert-column-gap, var(--slotted-space-4, 0.5rem));
|
|
117
|
+
--_row-gap: var(--slotted-alert-row-gap, var(--slotted-space-5, 0.75rem));
|
|
118
|
+
--_text-gap: var(--slotted-alert-text-gap, var(--slotted-space-2, 0.25rem));
|
|
119
|
+
--_radius: var(--slotted-alert-radius, var(--slotted-radius-md, 6px));
|
|
120
|
+
--_border-width: var(--slotted-alert-border-width, var(--slotted-border-width-thin, 1px));
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The dismiss control is an inner control, not a button (R2): its box is
|
|
124
|
+
* the glyph plus a step of air on each side, floored at WCAG 2.2 2.5.8's
|
|
125
|
+
* twenty-four pixels so the pointer target holds at every density. It
|
|
126
|
+
* used to take the smallest control height, which put a thirty-pixel
|
|
127
|
+
* square beside a sixteen-pixel mark and a line of text twenty tall.
|
|
128
|
+
*/
|
|
129
|
+
--_dismiss-size: var(
|
|
130
|
+
--slotted-alert-dismiss-size,
|
|
131
|
+
max(
|
|
132
|
+
var(--slotted-target-min, 24px),
|
|
133
|
+
min(calc(var(--_glyph-size) + 2 * var(--slotted-space-2, 0.25rem)), var(--_inner-size))
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Two composition changes that answer the box rather than the window.
|
|
139
|
+
* A container name rather than the anonymous form, because an alert inside
|
|
140
|
+
* a card inside a page would otherwise resolve against whichever ancestor
|
|
141
|
+
* happened to be a container first.
|
|
142
|
+
*/
|
|
143
|
+
container-name: slotted-alert;
|
|
144
|
+
container-type: inline-size;
|
|
145
|
+
/**
|
|
146
|
+
* `container-type: inline-size` applies `contain: inline-size`, which makes
|
|
147
|
+
* the box's intrinsic width zero: it is the whole point of the containment,
|
|
148
|
+
* and it is why the width has to be stated rather than left to `auto`.
|
|
149
|
+
*
|
|
150
|
+
* Found in the rendered reference page rather than in a test. In normal
|
|
151
|
+
* flow the box filled its parent and nothing was wrong; placed in a column
|
|
152
|
+
* that shrink-wraps its children — a flex column aligned to `start`, which
|
|
153
|
+
* is what the workbench's own stage is — every alert collapsed to the width
|
|
154
|
+
* of one character and the message broke one letter per line. A consumer
|
|
155
|
+
* writing `<Stack>` around three alerts would have found the same thing.
|
|
156
|
+
*/
|
|
157
|
+
inline-size: 100%;
|
|
158
|
+
|
|
159
|
+
background-color: transparent;
|
|
160
|
+
border: var(--_border-width) solid transparent;
|
|
161
|
+
border-radius: var(--_radius);
|
|
162
|
+
box-sizing: border-box;
|
|
163
|
+
color: var(--_text);
|
|
164
|
+
/**
|
|
165
|
+
* The distance between the mark and the words belongs to the mark, not to
|
|
166
|
+
* the grid. A grid `column-gap` is drawn between two tracks whether or not
|
|
167
|
+
* either holds anything, so an alert with no mark was indented by a gap in
|
|
168
|
+
* front of an empty column — the words started one gap further in than the
|
|
169
|
+
* inset, which is visible the moment two alerts sit one above the other.
|
|
170
|
+
*/
|
|
171
|
+
column-gap: 0;
|
|
172
|
+
display: grid;
|
|
173
|
+
font-family: var(--slotted-alert-font-family, var(--slotted-control-font-family, sans-serif));
|
|
174
|
+
font-size: var(--_font-size);
|
|
175
|
+
/**
|
|
176
|
+
* Three columns: the mark, the words, and the control that puts the
|
|
177
|
+
* message away. The row of actions goes under the words rather than beside
|
|
178
|
+
* them, so a second row exists only when there are actions to put in it —
|
|
179
|
+
* a fixed two-row template leaves a row gap under every alert that has
|
|
180
|
+
* none, which is the defect a `grid-template-areas` version of this had.
|
|
181
|
+
*/
|
|
182
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
183
|
+
/**
|
|
184
|
+
* The line box is rounded to an even number of pixels. At 13-pixel type
|
|
185
|
+
* the scale's 1.5 gave 19.5, so a message of one line was 45.5 pixels
|
|
186
|
+
* tall and every control centred on the line landed on a half pixel. An
|
|
187
|
+
* even line also centres the 24- and 26-pixel dismiss control and the
|
|
188
|
+
* 30-pixel actions on whole pixels: 20 at 13, 22 at 14, 18 at 12.
|
|
189
|
+
*/
|
|
190
|
+
line-height: round(nearest, calc(var(--_line-height) * 1em), 2px);
|
|
191
|
+
padding-block: var(--_inset-block);
|
|
192
|
+
padding-inline: var(--_inset-inline);
|
|
193
|
+
row-gap: var(--_row-gap);
|
|
194
|
+
/**
|
|
195
|
+
* The entry, which runs only where an arrival is the message (see the
|
|
196
|
+
* `@starting-style` rule below). A transition is read from the state being
|
|
197
|
+
* entered, so the open box carries the entering curve and the closed box
|
|
198
|
+
* below carries the leaving one.
|
|
199
|
+
*/
|
|
200
|
+
transition:
|
|
201
|
+
opacity var(--slotted-duration-short) var(--slotted-easing-enter),
|
|
202
|
+
translate var(--slotted-duration-short) var(--slotted-easing-enter),
|
|
203
|
+
display var(--slotted-duration-short) var(--slotted-easing-enter);
|
|
204
|
+
transition-behavior: allow-discrete;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A closed alert keeps its element: a live region has to be in the document
|
|
209
|
+
* before its text changes, so an alert that unmounted could not announce the
|
|
210
|
+
* second time the message arrived. `display: none` is restated because the
|
|
211
|
+
* root's own `display: grid` would otherwise win over the user agent's rule
|
|
212
|
+
* for `[hidden]`.
|
|
213
|
+
*
|
|
214
|
+
* The exit. Something the reader was reading is going away, which is a
|
|
215
|
+
* change the eye has to follow; `display` is carried with it so the box
|
|
216
|
+
* holds its place until the fade is over rather than snapping out from
|
|
217
|
+
* under the content below.
|
|
218
|
+
*/
|
|
219
|
+
[data-ui='alert'][data-part='root'][hidden] {
|
|
220
|
+
display: none;
|
|
221
|
+
opacity: 0;
|
|
222
|
+
transition:
|
|
223
|
+
opacity var(--slotted-duration-short) var(--slotted-easing-exit),
|
|
224
|
+
translate var(--slotted-duration-short) var(--slotted-easing-exit),
|
|
225
|
+
display var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
226
|
+
translate: 0 calc(-1 * var(--slotted-space-2, 0.25rem));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The entry belongs to a live alert only. `@starting-style` also applies on
|
|
231
|
+
* first paint, so an unconditional entry would fade in every alert already
|
|
232
|
+
* on a settings page at once — motion that carries no information. A
|
|
233
|
+
* `polite` or `assertive` alert is the one whose arrival is itself the
|
|
234
|
+
* message: it is closed or empty until something happens, and the motion
|
|
235
|
+
* marks the moment it does. It travels the path the exit takes, in reverse.
|
|
236
|
+
*/
|
|
237
|
+
@starting-style {
|
|
238
|
+
[data-ui='alert'][data-part='root']:is([data-live='polite'], [data-live='assertive']) {
|
|
239
|
+
opacity: 0;
|
|
240
|
+
translate: 0 calc(-1 * var(--slotted-space-2, 0.25rem));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* ── Tone: the colour, and the mark that says the same thing ───────────
|
|
245
|
+
*
|
|
246
|
+
* Five tones and five marks, and the map is
|
|
247
|
+
* `specs/components/alert/contract.json` rather than a decision taken here:
|
|
248
|
+
* both bindings draw the glyph from it and the contract test reads it, and
|
|
249
|
+
* this sheet paints only the colour. It is
|
|
250
|
+
* the map `status-indicator` declares, so a status light and an alert about
|
|
251
|
+
* the same thing draw the same silhouette.
|
|
252
|
+
*
|
|
253
|
+
* The mark is what satisfies WCAG 2.2 1.4.1. Under forced colours every tone
|
|
254
|
+
* ground becomes one system colour and the five boxes are identical; the
|
|
255
|
+
* circle, the triangle and the crossed circle are what is left.
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
[data-ui='alert'][data-part='root'][data-tone='accent'] {
|
|
259
|
+
--_solid: var(--slotted-tone-accent-solid);
|
|
260
|
+
--_on-solid: var(--slotted-tone-accent-on-solid);
|
|
261
|
+
--_surface: var(--slotted-tone-accent-surface);
|
|
262
|
+
--_border: var(--slotted-tone-accent-border);
|
|
263
|
+
--_text: var(--slotted-tone-accent-text);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[data-ui='alert'][data-part='root'][data-tone='secondary'] {
|
|
267
|
+
--_solid: var(--slotted-tone-secondary-solid);
|
|
268
|
+
--_on-solid: var(--slotted-tone-secondary-on-solid);
|
|
269
|
+
--_surface: var(--slotted-tone-secondary-surface);
|
|
270
|
+
--_border: var(--slotted-tone-secondary-border);
|
|
271
|
+
--_text: var(--slotted-tone-secondary-text);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
[data-ui='alert'][data-part='root'][data-tone='success'] {
|
|
275
|
+
--_solid: var(--slotted-tone-success-solid);
|
|
276
|
+
--_on-solid: var(--slotted-tone-success-on-solid);
|
|
277
|
+
--_surface: var(--slotted-tone-success-surface);
|
|
278
|
+
--_border: var(--slotted-tone-success-border);
|
|
279
|
+
--_text: var(--slotted-tone-success-text);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
[data-ui='alert'][data-part='root'][data-tone='warning'] {
|
|
283
|
+
--_solid: var(--slotted-tone-warning-solid);
|
|
284
|
+
--_on-solid: var(--slotted-tone-warning-on-solid);
|
|
285
|
+
--_surface: var(--slotted-tone-warning-surface);
|
|
286
|
+
--_border: var(--slotted-tone-warning-border);
|
|
287
|
+
--_text: var(--slotted-tone-warning-text);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
[data-ui='alert'][data-part='root'][data-tone='danger'] {
|
|
291
|
+
--_solid: var(--slotted-tone-danger-solid);
|
|
292
|
+
--_on-solid: var(--slotted-tone-danger-on-solid);
|
|
293
|
+
--_surface: var(--slotted-tone-danger-surface);
|
|
294
|
+
--_border: var(--slotted-tone-danger-border);
|
|
295
|
+
--_text: var(--slotted-tone-danger-text);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* ── Variant: the three ways one tone is painted ───────────────────────
|
|
299
|
+
*
|
|
300
|
+
* The words take the tone's own `text` role in all three, because that is
|
|
301
|
+
* the one pair `packages/themes/test/default-theme.test.mjs` measures
|
|
302
|
+
* against these grounds: the tone's text on the tone's surface, and the
|
|
303
|
+
* tone's text on each of the three neutral surfaces. Painting a tinted
|
|
304
|
+
* ground with `--slotted-text` would be an unheld ratio, and the theme's own
|
|
305
|
+
* comment records that the first family to depend on one is the family that
|
|
306
|
+
* discovers the shortfall in a screenshot.
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The default, and the shape seven of the ten surveyed implementations draw.
|
|
311
|
+
* It carries the tone's border as well as its surface: in `high-contrast`
|
|
312
|
+
* the tone surfaces are the page's own white, so a soft alert with no edge
|
|
313
|
+
* is a run of coloured text with no box around it — the finding `kbd`
|
|
314
|
+
* recorded about its own soft variant.
|
|
315
|
+
*/
|
|
316
|
+
[data-ui='alert'][data-part='root'][data-variant='soft'] {
|
|
317
|
+
background-color: var(--_surface);
|
|
318
|
+
border-color: var(--_border);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* The quiet painting, for the three grounds the theme measures a tone's text
|
|
323
|
+
* against: the page's surface, a raised one, a sunken one. It carries no
|
|
324
|
+
* ground of its own, which is what lets it sit inside a card without drawing
|
|
325
|
+
* a second rectangle around the message.
|
|
326
|
+
*
|
|
327
|
+
* It is not the variant for an unknown ground, and the harness fragment shows
|
|
328
|
+
* why: read on `--slotted-surface-inverse`, the tone's text is a colour the
|
|
329
|
+
* theme never measured against that ground and the words go quiet. A dark
|
|
330
|
+
* region is a scheme rather than a surface — an application states
|
|
331
|
+
* `data-slotted-scheme` on it, every tone role is restated, and `outline`
|
|
332
|
+
* reads again. `soft` and `solid` survive the inverted surface only because
|
|
333
|
+
* they repaint the ground before they write on it.
|
|
334
|
+
*/
|
|
335
|
+
[data-ui='alert'][data-part='root'][data-variant='outline'] {
|
|
336
|
+
background-color: transparent;
|
|
337
|
+
border-color: var(--_border);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* The message that must not be missed. `on-solid` is the foreground the
|
|
342
|
+
* theme's contrast test holds against `solid`, `solid-hover` and
|
|
343
|
+
* `solid-active` in every scheme, so the whole box — words, mark and dismiss
|
|
344
|
+
* control — takes it.
|
|
345
|
+
*/
|
|
346
|
+
[data-ui='alert'][data-part='root'][data-variant='solid'] {
|
|
347
|
+
background-color: var(--_solid);
|
|
348
|
+
border-color: var(--_solid);
|
|
349
|
+
color: var(--_on-solid);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* ── Reach: the same message at three scopes ───────────────────────────
|
|
353
|
+
*
|
|
354
|
+
* The guide's three recipes as one axis. It is not a size: a size would say
|
|
355
|
+
* the same message can be drawn large or small, and this says which scope
|
|
356
|
+
* the message applies to, with the geometry following from that.
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
/** Beside the thing it is about — a field group, a table row, a card. */
|
|
360
|
+
[data-ui='alert'][data-part='root'][data-reach='inline'] {
|
|
361
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
362
|
+
--_description-font-size: var(--slotted-control-font-size-sm, 12px);
|
|
363
|
+
--_glyph-size: var(--slotted-control-icon-size-sm, 14px);
|
|
364
|
+
--_inner-size: var(--slotted-control-inner-size-sm, 22px);
|
|
365
|
+
--_inset-block: var(--slotted-alert-padding-block, var(--slotted-space-4, 0.5rem));
|
|
366
|
+
--_inset-inline: var(--slotted-alert-padding-inline, var(--slotted-space-5, 0.75rem));
|
|
367
|
+
--_row-gap: var(--slotted-alert-row-gap, var(--slotted-space-4, 0.5rem));
|
|
368
|
+
--_radius: var(--slotted-alert-radius, var(--slotted-radius-sm, 4px));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Across a page or an application shell. It runs to both edges of whatever
|
|
373
|
+
* holds it, so it draws no corner and no inline edge — there is nothing
|
|
374
|
+
* there for an edge to separate — and keeps the one edge that does separate
|
|
375
|
+
* it from the page below.
|
|
376
|
+
*
|
|
377
|
+
* The title steps up while the description stays where it was, which is the
|
|
378
|
+
* one place in the family where the two type sizes differ.
|
|
379
|
+
*/
|
|
380
|
+
[data-ui='alert'][data-part='root'][data-reach='page'] {
|
|
381
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
382
|
+
--_glyph-size: var(--slotted-control-icon-size-lg, 18px);
|
|
383
|
+
--_inner-size: var(--slotted-control-inner-size-lg, 28px);
|
|
384
|
+
/**
|
|
385
|
+
* A banner is much wider than it is tall, so its two insets are not equal:
|
|
386
|
+
* the inline one is a step wider than a section's because it has a page's
|
|
387
|
+
* width to sit inside, and the block one is a step deeper because twelve
|
|
388
|
+
* pixels over a title this size read as a strip rather than as a band.
|
|
389
|
+
*/
|
|
390
|
+
--_inset-block: var(--slotted-alert-padding-block, var(--slotted-space-6, 1rem));
|
|
391
|
+
--_inset-inline: var(--slotted-alert-padding-inline, var(--slotted-space-7, 1.25rem));
|
|
392
|
+
--_radius: var(--slotted-alert-radius, 0);
|
|
393
|
+
|
|
394
|
+
border-block-start-width: 0;
|
|
395
|
+
border-inline-width: 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** A solid page banner has no edge to lose, so it keeps all four. */
|
|
399
|
+
[data-ui='alert'][data-part='root'][data-reach='page'][data-variant='solid'] {
|
|
400
|
+
border-block-start-width: var(--_border-width);
|
|
401
|
+
border-inline-width: var(--_border-width);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ── The mark ─────────────────────────────────────────────────────────
|
|
405
|
+
*
|
|
406
|
+
* A filled disc on the title's first line, with the tone's symbol cut out
|
|
407
|
+
* of it: the binding's `Glyph` in its `solid` form, which fills the glyph's
|
|
408
|
+
* ring and cuts the symbol out of the fill with an SVG mask, so the ground
|
|
409
|
+
* shows through the symbol on every variant. Every tone's glyph is a
|
|
410
|
+
* circle, so every mark is the same round silhouette on the same centre;
|
|
411
|
+
* the symbol inside is what tells the five apart when colour is gone. A
|
|
412
|
+
* triangle was the one mark whose visual centre is not its box's centre,
|
|
413
|
+
* and it did not sit on the line the way the four circles did.
|
|
414
|
+
*
|
|
415
|
+
* The glyph's filled ring reaches radius 11 of its 24-unit box, so the box
|
|
416
|
+
* is 12/11 of the mark and the disc is the mark's own size; the box
|
|
417
|
+
* overhangs the slot by half a unit on each side, centred.
|
|
418
|
+
*
|
|
419
|
+
* The disc is painted in `currentcolor`: the tone's text role on `soft` and
|
|
420
|
+
* `outline`, which the theme measures against those grounds, and `on-solid`
|
|
421
|
+
* on `solid`, where the cut-out shows the fill beneath.
|
|
422
|
+
*
|
|
423
|
+
* The mark hangs from the first line's baseline, not from the top of the
|
|
424
|
+
* row. Grid baseline alignment puts the mark's bottom edge on the words'
|
|
425
|
+
* baseline, and a relative offset lowers it by half of what the mark exceeds
|
|
426
|
+
* the capitals by, so its centre is the centre of the capitals in whatever
|
|
427
|
+
* face the title is set in. A margin cannot do this: a box with no baseline
|
|
428
|
+
* of its own is aligned by its border edge, so a margin moves nothing. The
|
|
429
|
+
* offset is rounded to a whole pixel, and the mark is a whole number of
|
|
430
|
+
* pixels, so the distance from the baseline to the mark's edge is an
|
|
431
|
+
* integer: the browser snaps the glyphs and the drawing by the same amount
|
|
432
|
+
* and they cannot land a pixel apart. The negative block-start margin takes
|
|
433
|
+
* the mark's own height out of the baseline group, so the words sit where
|
|
434
|
+
* they sit with no mark at all. Layout containment keeps the glyph's own
|
|
435
|
+
* baseline out of the box's, so the box is aligned by its border edge.
|
|
436
|
+
*
|
|
437
|
+
* A mark the page supplies instead of the tone's takes the same box and its
|
|
438
|
+
* place on the first line, centred in it.
|
|
439
|
+
*/
|
|
440
|
+
[data-ui='alert'] > [data-part='icon'] {
|
|
441
|
+
--slotted-icon-size: calc(var(--_mark-size) * 12 / 11);
|
|
442
|
+
|
|
443
|
+
align-items: center;
|
|
444
|
+
align-self: baseline;
|
|
445
|
+
block-size: var(--_mark-size);
|
|
446
|
+
contain: layout;
|
|
447
|
+
display: flex;
|
|
448
|
+
grid-column: 1;
|
|
449
|
+
grid-row: 1;
|
|
450
|
+
inline-size: var(--_mark-size);
|
|
451
|
+
inset-block-start: round(nearest, (var(--_mark-size) - 1cap) / 2, 1px);
|
|
452
|
+
justify-content: center;
|
|
453
|
+
margin-block-start: calc(-1 * var(--_mark-size));
|
|
454
|
+
margin-inline-end: var(--_column-gap);
|
|
455
|
+
position: relative;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* ── The words, which are the live region ─────────────────────────────
|
|
459
|
+
*
|
|
460
|
+
* `min-inline-size: 0` is what lets the column shrink below its content
|
|
461
|
+
* inside the root's grid, and it is the half of the pair that
|
|
462
|
+
* `minmax(0, 1fr)` on the track cannot supply on its own. The same pair is
|
|
463
|
+
* restated inside: the words are a grid of their own, and its one track has
|
|
464
|
+
* to be clamped too, or an unbreakable word in the title sizes the track to
|
|
465
|
+
* itself and the words run past the box that holds them.
|
|
466
|
+
*/
|
|
467
|
+
[data-ui='alert'] > [data-part='content'] {
|
|
468
|
+
/* The other half of the mark's baseline group: see the mark above. */
|
|
469
|
+
align-self: baseline;
|
|
470
|
+
display: grid;
|
|
471
|
+
grid-column: 2;
|
|
472
|
+
grid-row: 1;
|
|
473
|
+
grid-template-columns: minmax(0, 1fr);
|
|
474
|
+
min-inline-size: 0;
|
|
475
|
+
row-gap: var(--_text-gap);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* `anywhere` breaks the one string a message is likeliest to carry that no
|
|
480
|
+
* language can break — a deployment id, a container digest, a compound noun,
|
|
481
|
+
* a host name without a hyphen — and, unlike `break-word`, lowers the
|
|
482
|
+
* min-content size the grid reads, so the track never grows past the box to
|
|
483
|
+
* fit the word. Nothing is removed: the whole string stays in the DOM, in the
|
|
484
|
+
* accessibility tree, and findable by a page search.
|
|
485
|
+
*
|
|
486
|
+
* The 240-pixel capture is what asked for it: with `break-word` the title
|
|
487
|
+
* `Zahlungsbestätigung` set the track to its own width and the words stood
|
|
488
|
+
* outside the box by a few pixels in every scheme; the digest under it broke
|
|
489
|
+
* at the track's edge rather than at the box's. An ordinary word breaks only
|
|
490
|
+
* in a column narrower than the word, which at this type size is a column
|
|
491
|
+
* too narrow for the message anyway.
|
|
492
|
+
*/
|
|
493
|
+
[data-ui='alert'] [data-part='title'] {
|
|
494
|
+
/* A title rendered as a heading keeps the root's type rather than the
|
|
495
|
+
user agent's `1.17em`: the mark is `1em` of the root, and a 15-pixel
|
|
496
|
+
title beside a 13-pixel mark put the mark a pixel above its capitals. */
|
|
497
|
+
font-family: var(
|
|
498
|
+
--slotted-alert-title-font-family,
|
|
499
|
+
var(
|
|
500
|
+
--slotted-font-family-heading,
|
|
501
|
+
var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif)
|
|
502
|
+
)
|
|
503
|
+
);
|
|
504
|
+
font-size: inherit;
|
|
505
|
+
font-weight: var(--slotted-alert-title-font-weight, var(--slotted-font-weight-semibold, 600));
|
|
506
|
+
line-height: inherit;
|
|
507
|
+
margin: 0;
|
|
508
|
+
min-inline-size: 0;
|
|
509
|
+
overflow-wrap: anywhere;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
[data-ui='alert'] [data-part='description'] {
|
|
513
|
+
font-size: var(--_description-font-size);
|
|
514
|
+
/* Its own line, rounded against its own size: a page banner's description
|
|
515
|
+
is set a step below its title, and inheriting the title's 22 pixels
|
|
516
|
+
would open its lines by two. */
|
|
517
|
+
line-height: round(nearest, calc(var(--_line-height) * 1em), 2px);
|
|
518
|
+
margin: 0;
|
|
519
|
+
min-inline-size: 0;
|
|
520
|
+
overflow-wrap: anywhere;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/* ── The actions ──────────────────────────────────────────────────────
|
|
524
|
+
*
|
|
525
|
+
* Under the words rather than beside them, which is where Primer, Polaris,
|
|
526
|
+
* Carbon and MUI all put them, and the only place they can wrap without
|
|
527
|
+
* taking the message's own column with them. They are outside `content`, so
|
|
528
|
+
* they are outside the live region: a reader hearing the message is not read
|
|
529
|
+
* the labels of two buttons in the middle of it.
|
|
530
|
+
*/
|
|
531
|
+
[data-ui='alert'] > [data-part='actions'] {
|
|
532
|
+
align-items: center;
|
|
533
|
+
display: flex;
|
|
534
|
+
flex-wrap: wrap;
|
|
535
|
+
gap: var(--slotted-alert-actions-gap, var(--slotted-space-3, 0.375rem));
|
|
536
|
+
grid-column: 2;
|
|
537
|
+
grid-row: 2;
|
|
538
|
+
min-inline-size: 0;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* ── The dismiss control ──────────────────────────────────────────────
|
|
542
|
+
*
|
|
543
|
+
* A real button with a real name, sized from the smallest control height so
|
|
544
|
+
* it clears the WCAG 2.2 2.5.8 minimum at every density.
|
|
545
|
+
*
|
|
546
|
+
* The negative margins are optical rather than decorative. The button's box
|
|
547
|
+
* is taller than the line it sits beside, so aligning the two boxes puts the
|
|
548
|
+
* glyph below the title's centre; pulling the box up by half the difference
|
|
549
|
+
* puts the glyph on the title's own line. The same distance is taken off the
|
|
550
|
+
* inline end, so the glyph — rather than the invisible hit area around it —
|
|
551
|
+
* sits at the inset the rest of the box uses.
|
|
552
|
+
*
|
|
553
|
+
* The block end takes it too, which is the half that was missing. A grid row
|
|
554
|
+
* is as tall as its tallest item, so a control pulled up by half its overhang
|
|
555
|
+
* and left to hang by the other half made the row taller than the line it sat
|
|
556
|
+
* on — and a message of one line with no description ended up with more room
|
|
557
|
+
* under it than over it. With both margins the control's margin box is
|
|
558
|
+
* exactly one line, and the inset above the message equals the inset below.
|
|
559
|
+
*/
|
|
560
|
+
[data-ui='alert'] > [data-part='dismiss'] {
|
|
561
|
+
/* The cross is the binding's `Glyph` in an `icon` box at the glyph size
|
|
562
|
+
the formula for this box reads. */
|
|
563
|
+
--slotted-icon-size: var(--_glyph-size);
|
|
564
|
+
|
|
565
|
+
align-items: center;
|
|
566
|
+
align-self: start;
|
|
567
|
+
appearance: none;
|
|
568
|
+
background-color: transparent;
|
|
569
|
+
block-size: var(--_dismiss-size);
|
|
570
|
+
border: 0;
|
|
571
|
+
border-radius: var(--slotted-alert-dismiss-radius, var(--slotted-control-radius, 4px));
|
|
572
|
+
color: inherit;
|
|
573
|
+
cursor: pointer;
|
|
574
|
+
display: inline-flex;
|
|
575
|
+
flex: none;
|
|
576
|
+
/* The user agent gives every button a font of its own, and `1lh` below is
|
|
577
|
+
measured against whatever that font resolves to. Inheriting is what puts
|
|
578
|
+
the control's optical correction on the same line box as the title. */
|
|
579
|
+
font: inherit;
|
|
580
|
+
grid-column: 3;
|
|
581
|
+
grid-row: 1;
|
|
582
|
+
inline-size: var(--_dismiss-size);
|
|
583
|
+
justify-content: center;
|
|
584
|
+
margin-block-end: calc((1lh - var(--_dismiss-size)) / 2);
|
|
585
|
+
margin-block-start: calc((1lh - var(--_dismiss-size)) / 2);
|
|
586
|
+
margin-inline-end: calc((1lh - var(--_dismiss-size)) / 2);
|
|
587
|
+
margin-inline-start: var(--_column-gap);
|
|
588
|
+
padding: 0;
|
|
589
|
+
transition:
|
|
590
|
+
background-color var(--slotted-control-transition-duration)
|
|
591
|
+
var(--slotted-control-transition-easing),
|
|
592
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* `:enabled` is the guard `packages/styles/src/states.test.mjs` requires, and
|
|
597
|
+
* the reason it exists: a control the browser has already refused must not
|
|
598
|
+
* answer a pointer.
|
|
599
|
+
*/
|
|
600
|
+
[data-ui='alert'] > [data-part='dismiss']:enabled:hover {
|
|
601
|
+
background-color: var(--_state-hover);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
[data-ui='alert'] > [data-part='dismiss']:enabled:active {
|
|
605
|
+
background-color: var(--_state-active);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
[data-ui='alert'] > [data-part='dismiss']:disabled {
|
|
609
|
+
color: var(--slotted-disabled-foreground);
|
|
610
|
+
cursor: default;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/* On a finger the target is the comfortable size in both axes and the box
|
|
614
|
+
is the one a mouse sees: a transparent box centred on the control reaches
|
|
615
|
+
44 pixels, and the ground and the ring stay square. */
|
|
616
|
+
@media (pointer: coarse) {
|
|
617
|
+
[data-ui='alert'] > [data-part='dismiss'] {
|
|
618
|
+
position: relative;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
[data-ui='alert'] > [data-part='dismiss']::after {
|
|
622
|
+
content: '';
|
|
623
|
+
inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
|
|
624
|
+
position: absolute;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/* ── Focus ────────────────────────────────────────────────────────────
|
|
629
|
+
*
|
|
630
|
+
* The ring on the dismiss control is inset, because the control sits at the
|
|
631
|
+
* corner of the box and a ring outside it would be drawn over the border or
|
|
632
|
+
* clipped by it. The ring on the box itself is outside, and it exists
|
|
633
|
+
* because the root carries `tabindex="-1"`: the family never moves focus
|
|
634
|
+
* there on its own — the APG is explicit that an alert must not affect
|
|
635
|
+
* keyboard focus — but a page that deliberately sends focus to a message
|
|
636
|
+
* must not send it somewhere invisible.
|
|
637
|
+
*/
|
|
638
|
+
[data-ui='alert'] > [data-part='dismiss']:focus-visible {
|
|
639
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
640
|
+
outline-offset: calc(-1 * var(--slotted-focus-ring-width, 2px));
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Measured rather than assumed: `--slotted-focus-ring-color` reads 1.30 : 1
|
|
645
|
+
* against `--slotted-tone-accent-solid` in the light scheme, 1.03 against
|
|
646
|
+
* `success` and 1.07 against `danger`. The ring the theme calibrated against
|
|
647
|
+
* the page's surface disappears on the tone's own fill. The ring keeps the
|
|
648
|
+
* theme's colour in every scheme (R3); what a solid alert adds is a halo one
|
|
649
|
+
* ring-width wide just inside it, in the ink its own words are written in,
|
|
650
|
+
* so the ring is read against that halo rather than against the fill.
|
|
651
|
+
*/
|
|
652
|
+
[data-ui='alert'][data-variant='solid'] > [data-part='dismiss']:focus-visible {
|
|
653
|
+
box-shadow: inset 0 0 0 calc(2 * var(--slotted-focus-ring-width, 2px)) var(--_on-solid);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
[data-ui='alert'][data-part='root']:focus-visible {
|
|
657
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
658
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* ── Composition that answers the box, not the window ──────────────────
|
|
662
|
+
*
|
|
663
|
+
* Two container queries, and both change what the box is made of rather than
|
|
664
|
+
* how large it is. The breakpoints are literal because CSS forbids a custom
|
|
665
|
+
* property inside a container condition, which is the rule
|
|
666
|
+
* `docs/token-foundation.md` records for every reflowing family.
|
|
667
|
+
*/
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* A narrow box. Two buttons wrapping into two ragged lines is worse than two
|
|
671
|
+
* buttons stacked, and at this width a control that fills the column is the
|
|
672
|
+
* one a thumb can hit. This is the composition change the 240-pixel capture
|
|
673
|
+
* exists to check.
|
|
674
|
+
*/
|
|
675
|
+
@container slotted-alert (max-width: 22rem) {
|
|
676
|
+
[data-ui='alert'] > [data-part='actions'] {
|
|
677
|
+
/**
|
|
678
|
+
* Stacked, and each control still its own width. Stretching them is what
|
|
679
|
+
* the mobile banners in the field do, and it takes a decision that is not
|
|
680
|
+
* the family's: how wide a control is belongs to the control, and
|
|
681
|
+
* `button` already ships `fullWidth` for a consumer who wants it.
|
|
682
|
+
*/
|
|
683
|
+
align-items: flex-start;
|
|
684
|
+
flex-direction: column;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* A wide page banner. A system message is usually one line with one action,
|
|
690
|
+
* and stacking that action under it wastes the whole width the banner took;
|
|
691
|
+
* past this point the action rejoins the message's own line and the dismiss
|
|
692
|
+
* control moves out to a column of its own. Only `page` reflows: an alert
|
|
693
|
+
* beside a field group is narrow because the thing it is about is narrow,
|
|
694
|
+
* however wide the window is.
|
|
695
|
+
*/
|
|
696
|
+
@container slotted-alert (min-width: 40rem) {
|
|
697
|
+
[data-ui='alert'][data-reach='page'] > [data-part='actions'] {
|
|
698
|
+
align-self: center;
|
|
699
|
+
grid-column: 3;
|
|
700
|
+
grid-row: 1;
|
|
701
|
+
margin-inline-start: var(--_column-gap);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Centred with the action beside it rather than pulled onto the title's
|
|
706
|
+
* line. On its own the control belongs at the message's first line; next to
|
|
707
|
+
* a control that is centred on the whole box, two marks at two heights read
|
|
708
|
+
* as an alignment mistake.
|
|
709
|
+
*
|
|
710
|
+
* Both block margins go, not only the top one: centring aligns the margin
|
|
711
|
+
* box, and the negative bottom margin left behind pulled the control's
|
|
712
|
+
* centre below the action's by half its overhang.
|
|
713
|
+
*/
|
|
714
|
+
[data-ui='alert'][data-reach='page'] > [data-part='dismiss'] {
|
|
715
|
+
align-self: center;
|
|
716
|
+
grid-column: 4;
|
|
717
|
+
margin-block: 0;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/* ── Forced colours ───────────────────────────────────────────────────
|
|
722
|
+
*
|
|
723
|
+
* The operating system replaces every author colour, so all five tones
|
|
724
|
+
* become one box and all three variants become one painting. What is left to
|
|
725
|
+
* decide is whether the box still has an edge, and the theme cannot answer
|
|
726
|
+
* it: `solid` draws its border in its own fill, and a transparent border is
|
|
727
|
+
* not a colour the system remaps.
|
|
728
|
+
*
|
|
729
|
+
* The mark needs no rule: its glyph fills with `currentcolor`, which the
|
|
730
|
+
* system has already forced, and it keeps its silhouette, which is the whole
|
|
731
|
+
* reason a glyph is drawn per tone — a triangle and a crossed circle are
|
|
732
|
+
* still different from each other when neither of them is coloured.
|
|
733
|
+
*/
|
|
734
|
+
@media (forced-colors: active) {
|
|
735
|
+
/**
|
|
736
|
+
* Stated per variant, because each variant's own rule outranks a rule on
|
|
737
|
+
* the root alone. The theme maps the accent fill to `Highlight` and its ink
|
|
738
|
+
* to `HighlightText`, and a system colour is one the browser keeps: the
|
|
739
|
+
* solid box stayed `Highlight` while the backplate the browser draws
|
|
740
|
+
* behind text stayed `Canvas`, and the words were white on white. The
|
|
741
|
+
* danger fill, not a system colour, was forced to `Canvas` and took its
|
|
742
|
+
* edge with it.
|
|
743
|
+
*/
|
|
744
|
+
[data-ui='alert'][data-part='root'][data-variant] {
|
|
745
|
+
background-color: Canvas;
|
|
746
|
+
border-color: CanvasText;
|
|
747
|
+
color: CanvasText;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* The control's glyph is stroked in `currentcolor`, so giving the control a
|
|
752
|
+
* system colour repaints the mark with it.
|
|
753
|
+
*/
|
|
754
|
+
[data-ui='alert'] > [data-part='dismiss'] {
|
|
755
|
+
color: CanvasText;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
[data-ui='alert'] > [data-part='dismiss']:enabled:hover,
|
|
759
|
+
[data-ui='alert'] > [data-part='dismiss']:enabled:active {
|
|
760
|
+
background-color: Highlight;
|
|
761
|
+
color: HighlightText;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|