@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,1899 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* The outer frame of a route: the regions that do not change when the
|
|
4
|
+
* content does.
|
|
5
|
+
*
|
|
6
|
+
* div[data-ui=app-shell]
|
|
7
|
+
* ├─ skip ▸ a · a · a
|
|
8
|
+
* ├─ banner ▸ brand ▸ nav-toggle · … | centre ▸ … | actions ▸ …
|
|
9
|
+
* ├─ nav ▸ nav-header · nav-body · nav-footer
|
|
10
|
+
* ├─ scrim
|
|
11
|
+
* ├─ main ▸ page ▸ …
|
|
12
|
+
* ├─ aside ▸ aside-header ▸ … · aside-toggle | aside-body | aside-footer
|
|
13
|
+
* ├─ footer
|
|
14
|
+
* └─ layer
|
|
15
|
+
*
|
|
16
|
+
* ── One grid of measures, and every row in the frame stands on it ─────────
|
|
17
|
+
*
|
|
18
|
+
* `--_gutter` is the air around anything the frame holds. `--_row-inset` is
|
|
19
|
+
* how far a *row* sits inside the region that holds it, and `--_row-mark` is
|
|
20
|
+
* the box its mark occupies. Three numbers, published as tokens, and every
|
|
21
|
+
* left edge in the shell is one of two sums of them: the mark column at
|
|
22
|
+
* `gutter + inset`, and the text column at `gutter + inset + mark + gap`.
|
|
23
|
+
*
|
|
24
|
+
* The control in the bar, the workspace mark at the head of the navigation,
|
|
25
|
+
* every destination's mark and the account at its foot therefore share one
|
|
26
|
+
* edge; the wordmark, the labels, the group headings and the account's name
|
|
27
|
+
* share the other. Before the three numbers existed there were five edges
|
|
28
|
+
* within twenty pixels of each other, which is what a column of things that
|
|
29
|
+
* do not line up looks like.
|
|
30
|
+
*
|
|
31
|
+
* `side-navigation`, wave 8, reads the same three and needs no agreement
|
|
32
|
+
* with this family beyond them.
|
|
33
|
+
*
|
|
34
|
+
* ── The chrome recedes and the content advances ───────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* Under `canvas="inset"`, the default, the frame is painted the sunken
|
|
37
|
+
* ground and carries no surface of its own; the content is a raised card
|
|
38
|
+
* inset from it with a radius and an edge, and **no rule is drawn anywhere**
|
|
39
|
+
* — the gutter separates. `canvas="flush"` is the arrangement every surveyed
|
|
40
|
+
* implementation ships: regions on the surface, content on the ground, a
|
|
41
|
+
* hairline between. In the light scheme the two page surfaces are two per
|
|
42
|
+
* cent apart, which is why the hairline was doing all the work there and
|
|
43
|
+
* none of it here.
|
|
44
|
+
*
|
|
45
|
+
* ── The mode decides the geometry; the width only decides the mode ────────
|
|
46
|
+
*
|
|
47
|
+
* `data-nav-mode` is the single source of truth for where the navigation is
|
|
48
|
+
* drawn, and the binding writes it. `overlay` is a layer at *any* width,
|
|
49
|
+
* because a consumer may state the axis on a wide screen. Keying the layer's
|
|
50
|
+
* geometry on the container query instead was a defect: with the mode forced
|
|
51
|
+
* on a wide screen the script applied the veil, the focus scope and `inert`
|
|
52
|
+
* while the stylesheet left the region a static column underneath, so the
|
|
53
|
+
* screen went dark and nothing else happened.
|
|
54
|
+
*
|
|
55
|
+
* The container query is the *fallback*, and only that: before the binding
|
|
56
|
+
* has measured — a server render, the frame before hydration — there is no
|
|
57
|
+
* attribute, and a wide shell draws the column while a narrow one draws
|
|
58
|
+
* nothing. That is the safe half of the answer either way.
|
|
59
|
+
*
|
|
60
|
+
* `container-type: inline-size` makes an element a query container for its
|
|
61
|
+
* descendants and not for itself, so the root never rewrites its own
|
|
62
|
+
* template: it states its tracks once and each region places itself.
|
|
63
|
+
*
|
|
64
|
+
* ── One element in all three modes ────────────────────────────────────────
|
|
65
|
+
*
|
|
66
|
+
* The rail is the same `nav` at another inline size, which is what keeps the
|
|
67
|
+
* focus and the scroll position where the reader left them; Carbon remounts
|
|
68
|
+
* its side nav between the two and loses both.
|
|
69
|
+
*/
|
|
70
|
+
[data-ui='app-shell'] {
|
|
71
|
+
/**
|
|
72
|
+
* The air around anything the frame holds. Composed from the control gap
|
|
73
|
+
* rather than stated on the space scale, so it answers density: a dense
|
|
74
|
+
* screen tightens its frame along with its controls.
|
|
75
|
+
*/
|
|
76
|
+
--_gutter: var(
|
|
77
|
+
--slotted-app-shell-gutter,
|
|
78
|
+
calc(var(--slotted-control-gap-lg, 8px) + var(--slotted-space-2, 0.25rem))
|
|
79
|
+
);
|
|
80
|
+
/** How far a row sits inside its region, over and above the gutter. */
|
|
81
|
+
--_row-inset: var(--slotted-app-shell-row-inset-size, var(--slotted-space-4, 0.5rem));
|
|
82
|
+
/** The box a row's mark occupies: a control's own size, so the two agree. */
|
|
83
|
+
--_row-mark: var(--slotted-app-shell-row-mark-size, var(--slotted-control-height-md, 34px));
|
|
84
|
+
--_row-gap: var(--slotted-control-gap-md, 8px);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A row's type. A list of destinations is text and not a control, so it
|
|
88
|
+
* composes the type scale rather than the control scale — the two agree at
|
|
89
|
+
* thirteen pixels today and a theme may move either. `normal` is not a
|
|
90
|
+
* line height: it varies by font, and a two-line row needs the number.
|
|
91
|
+
*/
|
|
92
|
+
--_row-font-size: var(
|
|
93
|
+
--slotted-app-shell-row-text-size,
|
|
94
|
+
var(--slotted-font-size-sm, 0.8125rem)
|
|
95
|
+
);
|
|
96
|
+
--_row-line-height: var(
|
|
97
|
+
--slotted-app-shell-row-text-line-height,
|
|
98
|
+
var(--slotted-line-height-sm, 1.45)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Published, because the rows inside the regions are not this family's:
|
|
103
|
+
* `side-navigation` and `top-navigation` read them in wave 8, and a
|
|
104
|
+
* consumer who brings its own list has nothing to align to otherwise. Six
|
|
105
|
+
* numbers, and every left edge, row height and label in the frame is one.
|
|
106
|
+
*/
|
|
107
|
+
--slotted-app-shell-row-inset: var(--_row-inset);
|
|
108
|
+
--slotted-app-shell-row-mark: var(--_row-mark);
|
|
109
|
+
--slotted-app-shell-row-gap: var(--_row-gap);
|
|
110
|
+
--slotted-app-shell-row-height: var(--_row-mark);
|
|
111
|
+
--slotted-app-shell-row-font-size: var(--_row-font-size);
|
|
112
|
+
--slotted-app-shell-row-line-height: var(--_row-line-height);
|
|
113
|
+
|
|
114
|
+
--_nav-size: var(--slotted-app-shell-nav-size-md, 15rem);
|
|
115
|
+
/**
|
|
116
|
+
* The layer is wider than the column. A column shares the screen with the
|
|
117
|
+
* content and earns every pixel; a layer covers content the reader cannot
|
|
118
|
+
* reach, so its width costs nothing but the strip that says where to press
|
|
119
|
+
* to leave — one mark and the gutters, kept by the `min()` below.
|
|
120
|
+
*/
|
|
121
|
+
--_nav-overlay-size: var(--slotted-app-shell-nav-overlay-size, 20rem);
|
|
122
|
+
--_aside-size: var(--slotted-app-shell-aside-size-md, 20rem);
|
|
123
|
+
/**
|
|
124
|
+
* A rail is one mark column with a row's inset on each side, so a closed
|
|
125
|
+
* region is exactly as wide as the marks it holds and reads as a centred
|
|
126
|
+
* strip rather than as a column with its air on one side.
|
|
127
|
+
*/
|
|
128
|
+
--_rail-size: var(
|
|
129
|
+
--slotted-app-shell-rail-size,
|
|
130
|
+
calc(var(--_row-mark) + var(--_row-inset) * 2)
|
|
131
|
+
);
|
|
132
|
+
/**
|
|
133
|
+
* How far the frame's own edge is from the regions that run along it. It is
|
|
134
|
+
* the gutter on the inset plane, where every region is a card standing off
|
|
135
|
+
* the edge, and nothing on the flush plane, where they meet it. The bar and
|
|
136
|
+
* the footer read it so that their rows stand on the same left edge as the
|
|
137
|
+
* column's — measured, the control in the bar was twelve pixels to the
|
|
138
|
+
* right of the marks under `flush` and over them under `inset`.
|
|
139
|
+
*/
|
|
140
|
+
--_edge: var(--_gutter);
|
|
141
|
+
/**
|
|
142
|
+
* How far the navigation's rows stand inside the region's own box, and
|
|
143
|
+
* how much of the region's width its edges take. Both are nothing on the
|
|
144
|
+
* inset plane, where the column is the frame's ground and its box is the
|
|
145
|
+
* strip a reader sees; each other plane gives the region an edge — the
|
|
146
|
+
* screen's, a card's, a seam — and a row whose ground meets an edge reads
|
|
147
|
+
* as a column with no padding. The inset is on both sides, so the closed
|
|
148
|
+
* strip stays centred in itself, and the closed width is written once,
|
|
149
|
+
* from both, for every plane.
|
|
150
|
+
*/
|
|
151
|
+
--_nav-inset: 0px;
|
|
152
|
+
--_nav-edges: 0px;
|
|
153
|
+
|
|
154
|
+
--_frame: var(--slotted-app-shell-frame, var(--slotted-surface-sunken, Canvas));
|
|
155
|
+
--_canvas: var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas));
|
|
156
|
+
--_ink: var(--slotted-app-shell-color, var(--slotted-text, CanvasText));
|
|
157
|
+
/**
|
|
158
|
+
* The bar of a scrolling body: the thumb at the strong border step, which
|
|
159
|
+
* is the step `scroll-area` gives it and the one the foundation holds to
|
|
160
|
+
* 3:1 against every surface; the track the column's own ground, because a
|
|
161
|
+
* groove down the side of a navigation column is a second column.
|
|
162
|
+
*/
|
|
163
|
+
--_scrollbar-thumb: var(
|
|
164
|
+
--slotted-app-shell-scrollbar-thumb-color,
|
|
165
|
+
var(--slotted-border-strong, ButtonBorder)
|
|
166
|
+
);
|
|
167
|
+
--_scrollbar-track: var(--slotted-app-shell-scrollbar-track-color, transparent);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* ── The palette, region by region ──────────────────────────────────────
|
|
171
|
+
*
|
|
172
|
+
* Every region reads its own ground and its own ink, and every one of them
|
|
173
|
+
* falls back to the plane's. A shell whose column is dark over a light
|
|
174
|
+
* frame is one declaration, and it does not have to be paid for by every
|
|
175
|
+
* shell that wants none of it. `--_frame` and `--_canvas` stay as the two
|
|
176
|
+
* coarse knobs; these are what a product reaches for when the coarse pair
|
|
177
|
+
* is not enough.
|
|
178
|
+
*
|
|
179
|
+
* The defaults differ by plane and that is the whole of what `canvas`
|
|
180
|
+
* decides: under `inset` the regions are transparent and the frame shows
|
|
181
|
+
* through, under `flush` the regions carry the surface and the content
|
|
182
|
+
* carries the ground. Stating one of these properties overrules the plane
|
|
183
|
+
* for that region alone.
|
|
184
|
+
*
|
|
185
|
+
* Each chain is written *at the region* rather than resolved into a local
|
|
186
|
+
* on the frame, and that is not a style: a `var()` resolves where the
|
|
187
|
+
* declaration stands, so a default held on the root resolves in the root's
|
|
188
|
+
* scope and hands a region carrying `data-slotted-scheme="dark"` the light
|
|
189
|
+
* scheme's ink over its own dark ground. Written here, the last fallback is
|
|
190
|
+
* read inside the region, so a repainted column is one declaration and its
|
|
191
|
+
* ink follows the scheme it was given.
|
|
192
|
+
*
|
|
193
|
+
* A tint is a background. A *palette* — the tones the controls inside the
|
|
194
|
+
* region read, the borders, the focus ring — is `data-slotted-scheme` on
|
|
195
|
+
* the region itself, because a ground swapped without its tones leaves
|
|
196
|
+
* every control calibrated against a page it is no longer on.
|
|
197
|
+
*/
|
|
198
|
+
--_rule: var(--slotted-app-shell-border-color, var(--slotted-border-subtle, ButtonBorder));
|
|
199
|
+
--_rule-width: var(--slotted-border-width-thin, 1px);
|
|
200
|
+
--_radius: var(--slotted-app-shell-radius, var(--slotted-radius-lg, 8px));
|
|
201
|
+
--_elevation: var(--slotted-app-shell-elevation, var(--slotted-shadow-xs));
|
|
202
|
+
/**
|
|
203
|
+
* The line a seam draws, stated once. `separation="tone"` sets it to
|
|
204
|
+
* `none` and the forced-colours block below sets it back, so every seam
|
|
205
|
+
* rule is written once and reads the axis through this property.
|
|
206
|
+
*/
|
|
207
|
+
--_seam: var(--_rule-width) solid var(--_rule);
|
|
208
|
+
--_seam-width: var(--_rule-width);
|
|
209
|
+
/** The corner of a region whose corner is the card's: the radius inside the edge. */
|
|
210
|
+
--_inner-radius: calc(var(--_radius) - var(--_rule-width));
|
|
211
|
+
--_scrim: var(--slotted-app-shell-scrim, var(--slotted-scrim, Canvas));
|
|
212
|
+
--_banner-size: var(
|
|
213
|
+
--slotted-app-shell-banner-block-size,
|
|
214
|
+
calc(var(--slotted-control-height-lg, 38px) + var(--slotted-space-5, 0.75rem) * 2)
|
|
215
|
+
);
|
|
216
|
+
--_duration: var(--slotted-duration-medium);
|
|
217
|
+
--_easing: var(--slotted-easing-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
218
|
+
|
|
219
|
+
background-color: var(--_frame);
|
|
220
|
+
/**
|
|
221
|
+
* A wash or a photograph behind the frame is a theme's decision, not a
|
|
222
|
+
* prop: one layer over the frame's colour, sized to cover the frame and
|
|
223
|
+
* nothing when the token is unset.
|
|
224
|
+
*/
|
|
225
|
+
background-image: var(--slotted-app-shell-frame-image, none);
|
|
226
|
+
background-position: center;
|
|
227
|
+
background-size: cover;
|
|
228
|
+
color: var(--_ink);
|
|
229
|
+
container-name: slotted-app-shell;
|
|
230
|
+
container-type: inline-size;
|
|
231
|
+
display: grid;
|
|
232
|
+
font-family: var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif);
|
|
233
|
+
/**
|
|
234
|
+
* Four rows — banner, body, the row the secondary pane drops into when the
|
|
235
|
+
* shell is narrow, and footer — and three columns, each sized by what is
|
|
236
|
+
* in it. A track with nothing in it is nought, so the rows and columns a
|
|
237
|
+
* layout does not use cost it nothing.
|
|
238
|
+
*/
|
|
239
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
240
|
+
grid-template-rows: auto minmax(0, 1fr) auto auto;
|
|
241
|
+
min-block-size: 100%;
|
|
242
|
+
min-inline-size: 0;
|
|
243
|
+
/**
|
|
244
|
+
* ── The frame clips its inline axis ───────────────────────────────────
|
|
245
|
+
*
|
|
246
|
+
* A closed layer stands one whole width outside the frame, and a
|
|
247
|
+
* transformed box still counts towards the scrollable overflow of every
|
|
248
|
+
* scroll container above it — `visibility: hidden` takes it off the screen
|
|
249
|
+
* and not out of the geometry. Measured in the `navModes` story: the closed
|
|
250
|
+
* end-side panel gave the document 192 pixels of horizontal overflow once
|
|
251
|
+
* the demo frame's clip was lifted, and inside that frame the focus scope's
|
|
252
|
+
* first `focus()` scrolled the frame 240 pixels to the hidden panel, which
|
|
253
|
+
* the slide then clamped back frame by frame — the whole page jumped and
|
|
254
|
+
* returned (`tools/harness` reading, Chrome 150).
|
|
255
|
+
*
|
|
256
|
+
* `clip` and not `hidden`: `hidden` is a scroll container, which is the
|
|
257
|
+
* thing `focus()` would scroll; `clip` is not, and it does not establish a
|
|
258
|
+
* formatting context either, so nothing about the grid changes. Per axis
|
|
259
|
+
* rather than once, as `progress-indicator` states it: the block axis stays
|
|
260
|
+
* open because the frame grows with its content under `scroll='document'`,
|
|
261
|
+
* and a focus ring on the frame's last row has to be drawn past its edge.
|
|
262
|
+
* No `overflow-clip-margin`: it applies only when both axes clip, and the
|
|
263
|
+
* margin it leaves is a strip of the layer still counted — four pixels of
|
|
264
|
+
* the same jump, measured. The logical pair follows the physical one for
|
|
265
|
+
* the engines that read it, as `scroll-area` writes the same rule.
|
|
266
|
+
*/
|
|
267
|
+
overflow-x: clip;
|
|
268
|
+
overflow-y: visible;
|
|
269
|
+
overflow-inline: clip;
|
|
270
|
+
overflow-block: visible;
|
|
271
|
+
position: relative;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
[data-ui='app-shell'][data-nav-size='sm'] {
|
|
275
|
+
--_nav-size: var(--slotted-app-shell-nav-size-sm, 13rem);
|
|
276
|
+
}
|
|
277
|
+
[data-ui='app-shell'][data-nav-size='lg'] {
|
|
278
|
+
--_nav-size: var(--slotted-app-shell-nav-size-lg, 18rem);
|
|
279
|
+
}
|
|
280
|
+
[data-ui='app-shell'][data-aside-size='sm'] {
|
|
281
|
+
--_aside-size: var(--slotted-app-shell-aside-size-sm, 16rem);
|
|
282
|
+
}
|
|
283
|
+
[data-ui='app-shell'][data-aside-size='lg'] {
|
|
284
|
+
--_aside-size: var(--slotted-app-shell-aside-size-lg, 26rem);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* `content` is the shell that owns the viewport: it is the height it was
|
|
289
|
+
* given and only the content scrolls. `100%` chains to the viewport when the
|
|
290
|
+
* shell is the top of the document, and the maximum is what closes the gap
|
|
291
|
+
* every surveyed implementation leaves open — a mobile browser's collapsing
|
|
292
|
+
* toolbar makes `100vh` taller than the space there is, and `100dvh` is the
|
|
293
|
+
* space there is.
|
|
294
|
+
*/
|
|
295
|
+
[data-ui='app-shell'][data-scroll='content'] {
|
|
296
|
+
block-size: 100%;
|
|
297
|
+
/**
|
|
298
|
+
* Both axes, under `content` alone: the shell's height is the space it was
|
|
299
|
+
* given and never its content's, so containing it costs nothing — and it
|
|
300
|
+
* is what lets the pane, as a row under the content at a narrow width,
|
|
301
|
+
* take half the shell in `cqb` rather than the whole of it.
|
|
302
|
+
*/
|
|
303
|
+
container-type: size;
|
|
304
|
+
max-block-size: 100dvh;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ── Skip links ──────────────────────────────────────────────────────── */
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The first focusable thing in the document, and out of the flow until it is
|
|
311
|
+
* focused: the reader who does not need it never meets it, and the reader
|
|
312
|
+
* who does finds it before the chrome.
|
|
313
|
+
*/
|
|
314
|
+
[data-ui='app-shell'] > [data-part='skip'] {
|
|
315
|
+
inset-block-start: 0;
|
|
316
|
+
inset-inline-start: 0;
|
|
317
|
+
position: absolute;
|
|
318
|
+
z-index: var(--slotted-z-index-popover, 1100);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
[data-ui='app-shell'] > [data-part='skip'] > a {
|
|
322
|
+
background-color: var(--_canvas);
|
|
323
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
324
|
+
border-radius: var(--slotted-control-radius, 4px);
|
|
325
|
+
box-shadow: var(--slotted-shadow-md);
|
|
326
|
+
color: var(--slotted-text, CanvasText);
|
|
327
|
+
display: inline-block;
|
|
328
|
+
font-size: var(--slotted-control-font-size-md, 13px);
|
|
329
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
330
|
+
inset-block-start: var(--_gutter);
|
|
331
|
+
inset-inline-start: var(--_gutter);
|
|
332
|
+
padding-block: var(--slotted-space-3, 0.375rem);
|
|
333
|
+
padding-inline: var(--slotted-space-5, 0.75rem);
|
|
334
|
+
position: absolute;
|
|
335
|
+
text-decoration: none;
|
|
336
|
+
transition: translate var(--_duration) var(--_easing);
|
|
337
|
+
translate: 0 -200%;
|
|
338
|
+
white-space: nowrap;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
[data-ui='app-shell'] > [data-part='skip'] > a:focus-visible {
|
|
342
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
343
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
344
|
+
translate: 0 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* ── What each region is, before where it is ─────────────────────────── */
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Two clusters and the space between them, each starting on the row inset so
|
|
351
|
+
* the control in the bar stands over the marks in the column. The shell owns
|
|
352
|
+
* where a top bar's things go and owns none of what they are:
|
|
353
|
+
* `top-navigation` in wave 8 fills `actions`.
|
|
354
|
+
*/
|
|
355
|
+
/**
|
|
356
|
+
* The bar is a query container of its own, for the same reason the column is:
|
|
357
|
+
* under `primary='nav'` it is the frame's width less the navigation's, so
|
|
358
|
+
* what it holds has to answer *its* width rather than the frame's. Nothing
|
|
359
|
+
* in the family reads the query — the bar carries an application's own search
|
|
360
|
+
* and its own account, and those are the things that shed a label first.
|
|
361
|
+
*/
|
|
362
|
+
[data-ui='app-shell'] > [data-part='banner'] {
|
|
363
|
+
align-items: center;
|
|
364
|
+
background-color: var(--slotted-app-shell-banner-surface, transparent);
|
|
365
|
+
color: var(
|
|
366
|
+
--slotted-app-shell-banner-color,
|
|
367
|
+
var(--slotted-app-shell-color, var(--slotted-text, CanvasText))
|
|
368
|
+
);
|
|
369
|
+
container-name: slotted-app-shell-banner;
|
|
370
|
+
container-type: inline-size;
|
|
371
|
+
display: flex;
|
|
372
|
+
gap: var(--_row-gap);
|
|
373
|
+
grid-area: 1 / 1 / 2 / -1;
|
|
374
|
+
min-block-size: var(--_banner-size);
|
|
375
|
+
min-inline-size: 0;
|
|
376
|
+
padding-inline: var(--_edge);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
[data-ui='app-shell'] [data-part='brand'] {
|
|
380
|
+
align-items: center;
|
|
381
|
+
display: flex;
|
|
382
|
+
flex: 0 1 auto;
|
|
383
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
384
|
+
gap: var(--_row-gap);
|
|
385
|
+
min-inline-size: 0;
|
|
386
|
+
padding-inline-start: var(--_row-inset);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** Pushed to the end by the space it takes, so an empty cluster costs nothing. */
|
|
390
|
+
/**
|
|
391
|
+
* Nothing in the bar may push the frame wider than the space there is. A
|
|
392
|
+
* wordmark is one word and one word does not wrap, so under `primary='nav'`
|
|
393
|
+
* at 420 pixels the mark reported ninety-three pixels of content inside a
|
|
394
|
+
* sixty-five-pixel box and the whole frame grew with it. What the bar holds
|
|
395
|
+
* shortens instead; the control is exempt, because a control that shrinks
|
|
396
|
+
* below its target is a defect of a different kind.
|
|
397
|
+
*/
|
|
398
|
+
[data-ui='app-shell'] [data-part='brand'] > *:not([data-part='nav-toggle']) {
|
|
399
|
+
min-inline-size: 0;
|
|
400
|
+
overflow: var(--slotted-app-shell-brand-overflow, hidden);
|
|
401
|
+
text-overflow: ellipsis;
|
|
402
|
+
white-space: var(--slotted-app-shell-brand-wrap, nowrap);
|
|
403
|
+
}
|
|
404
|
+
[data-ui='app-shell'] > [data-part='banner'] > [data-part='actions'] {
|
|
405
|
+
align-items: center;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex: 0 1 auto;
|
|
408
|
+
min-inline-size: 0;
|
|
409
|
+
gap: var(--slotted-control-gap-sm, 6px);
|
|
410
|
+
margin-inline-start: auto;
|
|
411
|
+
padding-inline-end: var(--_row-inset);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* A control keeps its width. Left to shrink with the cluster, a button in a
|
|
415
|
+
* bar too narrow for it broke its own label one letter per line, which is a
|
|
416
|
+
* defect of a different kind from a bar that has run out of room. What runs
|
|
417
|
+
* out of room is the middle, which is what `centre` is for.
|
|
418
|
+
*/
|
|
419
|
+
[data-ui='app-shell'] > [data-part='banner'] > [data-part='actions'] > * {
|
|
420
|
+
flex-shrink: 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The middle of the bar, and the one cluster that grows: a search, a trail, a
|
|
425
|
+
* route title, tabs. Without it a bar is two clusters and a void — which is
|
|
426
|
+
* what the first reference page photographed, a control at one end and three
|
|
427
|
+
* at the other with twelve hundred pixels of nothing between. What it holds
|
|
428
|
+
* is the consumer's; that it may be narrower than what it holds is the
|
|
429
|
+
* shell's, for the reason `actions` carries `min-inline-size: 0`.
|
|
430
|
+
*/
|
|
431
|
+
[data-ui='app-shell'] > [data-part='banner'] > [data-part='centre'] {
|
|
432
|
+
align-items: center;
|
|
433
|
+
display: flex;
|
|
434
|
+
flex: 1 1 auto;
|
|
435
|
+
gap: var(--_row-gap);
|
|
436
|
+
min-inline-size: 0;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* A query container of its own, so the family inside it answers the rail
|
|
441
|
+
* without the shell telling it anything. The rail is a width, and what a
|
|
442
|
+
* destination shows at that width — a mark alone, a mark and a word — is
|
|
443
|
+
* `side-navigation`'s decision to take against the room it has.
|
|
444
|
+
*/
|
|
445
|
+
[data-ui='app-shell'] > [data-part='nav'] {
|
|
446
|
+
background-color: var(--slotted-app-shell-nav-surface, transparent);
|
|
447
|
+
box-sizing: border-box;
|
|
448
|
+
color: var(
|
|
449
|
+
--slotted-app-shell-nav-color,
|
|
450
|
+
var(--slotted-app-shell-color, var(--slotted-text, CanvasText))
|
|
451
|
+
);
|
|
452
|
+
container-name: slotted-app-shell-nav;
|
|
453
|
+
container-type: inline-size;
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
grid-area: 2 / 1 / 3 / 2;
|
|
457
|
+
min-block-size: 0;
|
|
458
|
+
/**
|
|
459
|
+
* A grid item's automatic minimum size is its content's, so a rail asked
|
|
460
|
+
* for fifty pixels stood at whatever the longest destination measured.
|
|
461
|
+
*/
|
|
462
|
+
min-inline-size: 0;
|
|
463
|
+
overflow: hidden;
|
|
464
|
+
transition:
|
|
465
|
+
inline-size var(--_duration) var(--_easing),
|
|
466
|
+
visibility var(--_duration) step-start;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* The column pins its ends and scrolls its middle. Ant and Mantine let the
|
|
471
|
+
* list overflow the region, which takes the account control off the screen
|
|
472
|
+
* the moment an application has more destinations than height.
|
|
473
|
+
*
|
|
474
|
+
* The ends hold a row, and a row here is often two lines — a workspace and
|
|
475
|
+
* its perimeter, a person and their role. Block padding is what gives a
|
|
476
|
+
* two-line block its own air; without it both were crushed against the group
|
|
477
|
+
* heading below and the frame's footer beneath.
|
|
478
|
+
*/
|
|
479
|
+
[data-ui='app-shell'] > [data-part='nav'] > [data-part='nav-header'],
|
|
480
|
+
[data-ui='app-shell'] > [data-part='nav'] > [data-part='nav-footer'] {
|
|
481
|
+
align-items: center;
|
|
482
|
+
display: flex;
|
|
483
|
+
flex: 0 0 auto;
|
|
484
|
+
gap: var(--_row-gap);
|
|
485
|
+
min-block-size: var(--_rail-size);
|
|
486
|
+
min-inline-size: 0;
|
|
487
|
+
padding-block: var(--_row-inset);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* The inset is the rows', not the region's: on the parts, a seam drawn by
|
|
491
|
+
* one of them — the rule over the foot — still runs the column's full width,
|
|
492
|
+
* and a strip of the rail's own beside the column needs none.
|
|
493
|
+
*/
|
|
494
|
+
[data-ui='app-shell']
|
|
495
|
+
> [data-part='nav']
|
|
496
|
+
> :is([data-part='nav-header'], [data-part='nav-body'], [data-part='nav-footer']) {
|
|
497
|
+
padding-inline: var(--_nav-inset);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* The body scrolls the way `scroll-area` scrolls, and for the reasons written
|
|
501
|
+
* there. `scrollbar-gutter: stable` reserves the bar's column before the
|
|
502
|
+
* list overflows, so a destination added at the bottom moves no row
|
|
503
|
+
* sideways. `scrollbar-width: thin` is still the platform's bar — the trough
|
|
504
|
+
* click and the context menu survive — and it is what makes the bar the
|
|
505
|
+
* family's to colour: an overlay bar drawn by the platform over the rows sat
|
|
506
|
+
* on the chevron of every parent, because a row ends one inset from the
|
|
507
|
+
* column's edge and the platform's bar is wider than that inset. A thin
|
|
508
|
+
* classic bar stands in the gutter instead, beside the rows.
|
|
509
|
+
*
|
|
510
|
+
* The inline axis is hidden, not `auto`: while the region's width moves
|
|
511
|
+
* between open and rail, the rows stand wider than the body, and an `auto`
|
|
512
|
+
* inline axis drew a horizontal bar for the length of the transition.
|
|
513
|
+
*/
|
|
514
|
+
[data-ui='app-shell'] > [data-part='nav'] > [data-part='nav-body'] {
|
|
515
|
+
align-items: stretch;
|
|
516
|
+
display: flex;
|
|
517
|
+
flex: 1 1 auto;
|
|
518
|
+
flex-direction: column;
|
|
519
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
520
|
+
min-block-size: 0;
|
|
521
|
+
overflow-x: hidden;
|
|
522
|
+
overflow-y: auto;
|
|
523
|
+
overflow-inline: hidden;
|
|
524
|
+
overflow-block: auto;
|
|
525
|
+
overscroll-behavior: contain;
|
|
526
|
+
padding-block: var(--slotted-space-4, 0.5rem);
|
|
527
|
+
scrollbar-color: var(--_scrollbar-thumb) var(--_scrollbar-track);
|
|
528
|
+
scrollbar-gutter: stable;
|
|
529
|
+
scrollbar-width: thin;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
[data-ui='app-shell'] > [data-part='main'] {
|
|
533
|
+
background-color: var(
|
|
534
|
+
--slotted-app-shell-main-surface,
|
|
535
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
536
|
+
);
|
|
537
|
+
color: var(
|
|
538
|
+
--slotted-app-shell-main-color,
|
|
539
|
+
var(--slotted-app-shell-color, var(--slotted-text, CanvasText))
|
|
540
|
+
);
|
|
541
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
542
|
+
/**
|
|
543
|
+
* Whatever the content stacks, it stacks inside the content: a sticky
|
|
544
|
+
* page-header raises itself three steps over the sticky layer, and
|
|
545
|
+
* without a context of its own here it would paint over the wait drawn
|
|
546
|
+
* on this region's edge.
|
|
547
|
+
*/
|
|
548
|
+
isolation: isolate;
|
|
549
|
+
min-block-size: 0;
|
|
550
|
+
min-inline-size: 0;
|
|
551
|
+
}
|
|
552
|
+
[data-ui='app-shell'][data-scroll='content'] > [data-part='main'] {
|
|
553
|
+
overflow: auto;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* The wait for the next page, on the line between the chrome and the
|
|
558
|
+
* content. The frame is the box of `main` — its grid area here, its margins
|
|
559
|
+
* and its corners wherever a canvas states them, because every such rule
|
|
560
|
+
* below names both parts — and it paints nothing: the one progress bar it
|
|
561
|
+
* holds stands on its block-start edge. It is a frame rather than a strip
|
|
562
|
+
* so the card's corners clip the bar; a strip two pixels tall given the
|
|
563
|
+
* card's radius has its radii scaled down to half its height, and its ends
|
|
564
|
+
* would be square against a rounded card.
|
|
565
|
+
*
|
|
566
|
+
* Strict containment keeps the travelling bar's per-frame layout inside
|
|
567
|
+
* this box, and its paint inside the corners.
|
|
568
|
+
*/
|
|
569
|
+
[data-ui='app-shell'] > [data-part='progress'] {
|
|
570
|
+
contain: strict;
|
|
571
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
572
|
+
inset-block-start: calc(-1 * var(--_progress-lift, 0px));
|
|
573
|
+
min-block-size: 0;
|
|
574
|
+
min-inline-size: 0;
|
|
575
|
+
overflow: clip;
|
|
576
|
+
pointer-events: none;
|
|
577
|
+
position: relative;
|
|
578
|
+
z-index: 1;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The content scrolls with the document under `document`, and the frame
|
|
582
|
+
* with it; the bar holds the top of the viewport once the banner, which is
|
|
583
|
+
* not sticky there, has gone.
|
|
584
|
+
*/
|
|
585
|
+
[data-ui='app-shell'][data-scroll='document'] > [data-part='progress'] {
|
|
586
|
+
--slotted-progress-indicator-position: sticky;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** A skip link's destination takes focus; it draws no ring for a pointer. */
|
|
590
|
+
[data-ui='app-shell'] > [data-part='main']:focus {
|
|
591
|
+
outline: none;
|
|
592
|
+
}
|
|
593
|
+
[data-ui='app-shell'] > [data-part='main']:focus-visible {
|
|
594
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
595
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Below the content until the shell is wide enough to hold it beside: at a
|
|
600
|
+
* narrow width a column beside the content is two columns of nothing.
|
|
601
|
+
*
|
|
602
|
+
* The pane opens and closes the way the navigation does, and its closed
|
|
603
|
+
* state is the navigation's `hidden`: out of the layout, out of the tab
|
|
604
|
+
* order, out of the accessibility tree. A pane has no rail — its content is
|
|
605
|
+
* the thing it shows or nothing — and a region at nought pixels that still
|
|
606
|
+
* lays its children out puts content outside the box that owns it.
|
|
607
|
+
*/
|
|
608
|
+
[data-ui='app-shell'] > [data-part='aside'] {
|
|
609
|
+
background-color: var(
|
|
610
|
+
--slotted-app-shell-aside-surface,
|
|
611
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
612
|
+
);
|
|
613
|
+
box-sizing: border-box;
|
|
614
|
+
color: var(
|
|
615
|
+
--slotted-app-shell-aside-color,
|
|
616
|
+
var(--slotted-app-shell-color, var(--slotted-text, CanvasText))
|
|
617
|
+
);
|
|
618
|
+
display: flex;
|
|
619
|
+
flex-direction: column;
|
|
620
|
+
grid-area: 3 / 1 / 4 / -1;
|
|
621
|
+
min-block-size: 0;
|
|
622
|
+
min-inline-size: 0;
|
|
623
|
+
}
|
|
624
|
+
[data-ui='app-shell'][data-scroll='content'] > [data-part='aside'] {
|
|
625
|
+
overflow: auto;
|
|
626
|
+
}
|
|
627
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']) > [data-part='aside']:not([data-open]) {
|
|
628
|
+
display: none;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* The pane pins its ends and scrolls its middle, the discipline the column
|
|
633
|
+
* already has: a pane with more content than height took its actions off the
|
|
634
|
+
* screen. The head holds the title and the control that closes the pane; the
|
|
635
|
+
* foot holds the actions that close its task. Both stand on the row inset
|
|
636
|
+
* plus the edge, so the title's left edge is the content card's.
|
|
637
|
+
*/
|
|
638
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-header'],
|
|
639
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-footer'] {
|
|
640
|
+
align-items: center;
|
|
641
|
+
display: flex;
|
|
642
|
+
flex: 0 0 auto;
|
|
643
|
+
gap: var(--_row-gap);
|
|
644
|
+
min-block-size: var(--_rail-size);
|
|
645
|
+
min-inline-size: 0;
|
|
646
|
+
padding-block: var(--_row-inset);
|
|
647
|
+
padding-inline: calc(var(--_row-inset) + var(--_row-gap));
|
|
648
|
+
}
|
|
649
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-header'] {
|
|
650
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
651
|
+
}
|
|
652
|
+
/** The title takes the room and the control keeps its own. */
|
|
653
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-header'] > :not([data-part]) {
|
|
654
|
+
flex: 1 1 auto;
|
|
655
|
+
min-inline-size: 0;
|
|
656
|
+
overflow: hidden;
|
|
657
|
+
text-overflow: ellipsis;
|
|
658
|
+
white-space: nowrap;
|
|
659
|
+
}
|
|
660
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-footer'] {
|
|
661
|
+
justify-content: flex-end;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* The pane's body scrolls as the column's does, bar and gutter included. It
|
|
665
|
+
* stands on the inset the head and the foot stand on, so what it holds lines
|
|
666
|
+
* up with the pane's title and no consumer pads it by hand.
|
|
667
|
+
*/
|
|
668
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-body'] {
|
|
669
|
+
flex: 1 1 auto;
|
|
670
|
+
min-block-size: 0;
|
|
671
|
+
min-inline-size: 0;
|
|
672
|
+
overflow-x: hidden;
|
|
673
|
+
overflow-y: auto;
|
|
674
|
+
overflow-inline: hidden;
|
|
675
|
+
overflow-block: auto;
|
|
676
|
+
overscroll-behavior: contain;
|
|
677
|
+
padding-block: var(--slotted-space-5, 0.75rem);
|
|
678
|
+
padding-inline: calc(var(--_row-inset) + var(--_row-gap));
|
|
679
|
+
scrollbar-color: var(--_scrollbar-thumb) var(--_scrollbar-track);
|
|
680
|
+
scrollbar-gutter: stable;
|
|
681
|
+
scrollbar-width: thin;
|
|
682
|
+
}
|
|
683
|
+
/* Content that brings its own inset — a page, a detail panel, a list to the edges. */
|
|
684
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-body'][data-inset='none'] {
|
|
685
|
+
padding: 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* The pane's inner edge, which the reader moves: a focusable `separator`
|
|
690
|
+
* from the WAI-ARIA Window Splitter pattern. Rendering the part is the
|
|
691
|
+
* capability, and the column is the only arrangement it moves — a layer keeps
|
|
692
|
+
* its size step and a row under the content has no inline edge — so it is
|
|
693
|
+
* drawn nowhere here and shown by the wide query alone.
|
|
694
|
+
*
|
|
695
|
+
* A strip lying over the pane's inner edge, inside the box: the pane scrolls
|
|
696
|
+
* under `scroll="content"`, and a scroll container clips what stands outside
|
|
697
|
+
* it. The strip is the rows' own inset wide, so it covers padding and no
|
|
698
|
+
* content. `::before` is the reach, transparent and the WCAG 2.2 floor wide.
|
|
699
|
+
* At rest it draws nothing; under the hand, the drag and the keyboard its
|
|
700
|
+
* outer border becomes the line, and the keyboard alone adds the ring.
|
|
701
|
+
*/
|
|
702
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-resizer'] {
|
|
703
|
+
--_resizer-thickness: var(--slotted-space-6, 1rem);
|
|
704
|
+
--_resizer-reach: var(--slotted-target-min, 24px);
|
|
705
|
+
background: transparent;
|
|
706
|
+
border: 0 solid transparent;
|
|
707
|
+
border-inline-start-width: var(--slotted-border-width-thick, 2px);
|
|
708
|
+
box-sizing: border-box;
|
|
709
|
+
cursor: col-resize;
|
|
710
|
+
display: none;
|
|
711
|
+
inline-size: var(--_resizer-thickness);
|
|
712
|
+
inset-block: 0;
|
|
713
|
+
inset-inline-start: 0;
|
|
714
|
+
position: absolute;
|
|
715
|
+
touch-action: none;
|
|
716
|
+
transition: border-color var(--_duration) var(--_easing);
|
|
717
|
+
z-index: 1;
|
|
718
|
+
}
|
|
719
|
+
[data-ui='app-shell'][data-nav-side='end'] > [data-part='aside'] > [data-part='aside-resizer'] {
|
|
720
|
+
border-inline-end-width: var(--slotted-border-width-thick, 2px);
|
|
721
|
+
border-inline-start-width: 0;
|
|
722
|
+
inset-inline-end: 0;
|
|
723
|
+
inset-inline-start: auto;
|
|
724
|
+
}
|
|
725
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-resizer']::before {
|
|
726
|
+
content: '';
|
|
727
|
+
inset-block: 0;
|
|
728
|
+
inset-inline: calc((var(--_resizer-reach) - var(--_resizer-thickness)) / -2);
|
|
729
|
+
position: absolute;
|
|
730
|
+
}
|
|
731
|
+
[data-ui='app-shell']
|
|
732
|
+
> [data-part='aside']
|
|
733
|
+
> [data-part='aside-resizer']:not([data-disabled]):hover,
|
|
734
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-resizer']:focus-visible,
|
|
735
|
+
[data-ui='app-shell'][data-dragging] > [data-part='aside'] > [data-part='aside-resizer'] {
|
|
736
|
+
border-color: var(
|
|
737
|
+
--slotted-app-shell-resizer-color,
|
|
738
|
+
var(--slotted-border-strong, ButtonBorder)
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
[data-ui='app-shell'] > [data-part='aside'] > [data-part='aside-resizer']:focus-visible {
|
|
742
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
743
|
+
outline-offset: calc(-1 * var(--slotted-focus-ring-offset, 2px));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* The application's own footer, and the one region a product most often does
|
|
748
|
+
* not have: a bar across the bottom of every route costs a row of the screen
|
|
749
|
+
* for a version string. It exists because `contentinfo` has to be reachable
|
|
750
|
+
* where a product does have one — a licence, a status, an environment — and
|
|
751
|
+
* it takes only the height of its own line.
|
|
752
|
+
*/
|
|
753
|
+
[data-ui='app-shell'] > [data-part='footer'] {
|
|
754
|
+
align-items: center;
|
|
755
|
+
background-color: var(--slotted-app-shell-footer-surface, transparent);
|
|
756
|
+
color: var(
|
|
757
|
+
--slotted-app-shell-footer-color,
|
|
758
|
+
var(--slotted-app-shell-color, var(--slotted-text-muted, GrayText))
|
|
759
|
+
);
|
|
760
|
+
display: flex;
|
|
761
|
+
font-size: var(--slotted-control-font-size-sm, 12px);
|
|
762
|
+
gap: var(--_row-gap);
|
|
763
|
+
grid-area: 4 / 1 / 5 / -1;
|
|
764
|
+
min-inline-size: 0;
|
|
765
|
+
padding-block: var(--slotted-space-3, 0.375rem);
|
|
766
|
+
padding-inline: calc(var(--_edge) + var(--_row-inset));
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* The application's portal root. `display: contents` leaves no box at all,
|
|
771
|
+
* so a layer put here is laid out by its own rules and the grid never sees
|
|
772
|
+
* it. What it is a containing block for is the shell rather than the
|
|
773
|
+
* viewport, because `container-type` contains layout.
|
|
774
|
+
*/
|
|
775
|
+
[data-ui='app-shell'] > [data-part='layer'] {
|
|
776
|
+
display: contents;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/* ── The inset canvas ────────────────────────────────────────────────── */
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Everything the frame holds is a box inset from it by the gutter, and the
|
|
783
|
+
* gutter is a *margin* rather than padding on purpose: the navigation's own
|
|
784
|
+
* box is then the strip a reader sees, so a rail is centred in itself rather
|
|
785
|
+
* than being a column with its air on one side. That was the reviewer's
|
|
786
|
+
* finding — the closed strip had twelve pixels to its right, where the
|
|
787
|
+
* content card begins, and nothing to its left.
|
|
788
|
+
*/
|
|
789
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='nav'] {
|
|
790
|
+
/* Invisible on the transparent default; a product that paints the column
|
|
791
|
+
gets a region with the plane's corners rather than a square block. */
|
|
792
|
+
border-radius: var(--_radius);
|
|
793
|
+
margin-block: var(--_gutter);
|
|
794
|
+
margin-inline-start: var(--_gutter);
|
|
795
|
+
}
|
|
796
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='main'],
|
|
797
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='aside'],
|
|
798
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='main'],
|
|
799
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='aside'] {
|
|
800
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
801
|
+
border-radius: var(--_radius);
|
|
802
|
+
box-shadow: var(--_elevation);
|
|
803
|
+
margin-block: var(--_gutter);
|
|
804
|
+
margin-inline: var(--_gutter);
|
|
805
|
+
}
|
|
806
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='aside'],
|
|
807
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='aside'] {
|
|
808
|
+
margin-inline-start: 0;
|
|
809
|
+
}
|
|
810
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='footer'],
|
|
811
|
+
[data-ui='app-shell'][data-canvas='panel'] > [data-part='footer'],
|
|
812
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='footer'] {
|
|
813
|
+
padding-block-start: 0;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* The card is a wide screen's arrangement. On a phone, where the navigation
|
|
817
|
+
* has become a layer, a gutter on every side and rounded corners against the
|
|
818
|
+
* screen's own frame a column of content that has no room to spare. The
|
|
819
|
+
* content then meets the frame's edges and keeps one rule, under the banner,
|
|
820
|
+
* for the seam the gutter used to draw. Keyed on the mode, as every layer
|
|
821
|
+
* rule is; the width answers only for a shell not yet measured, below.
|
|
822
|
+
*/
|
|
823
|
+
[data-ui='app-shell'][data-canvas='floating'][data-nav-mode='overlay'] > [data-part='main'],
|
|
824
|
+
[data-ui='app-shell'][data-canvas='inset'][data-nav-mode='overlay'] > [data-part='main'] {
|
|
825
|
+
border-block-end: 0;
|
|
826
|
+
border-inline: 0;
|
|
827
|
+
border-radius: 0;
|
|
828
|
+
box-shadow: none;
|
|
829
|
+
margin: 0;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/* ── The flush canvas ────────────────────────────────────────────────── */
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* The plane turned inside out: the regions carry the surface and the content
|
|
836
|
+
* carries the ground. Stated as the same six properties, so a product that
|
|
837
|
+
* repaints one region keeps its choice under either plane.
|
|
838
|
+
*/
|
|
839
|
+
[data-ui='app-shell'][data-canvas='flush'] {
|
|
840
|
+
--_edge: var(--_row-inset);
|
|
841
|
+
--_nav-edges: var(--_seam-width);
|
|
842
|
+
--_nav-inset: var(--_row-inset);
|
|
843
|
+
}
|
|
844
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='banner'] {
|
|
845
|
+
background-color: var(
|
|
846
|
+
--slotted-app-shell-banner-surface,
|
|
847
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='nav'] {
|
|
851
|
+
background-color: var(
|
|
852
|
+
--slotted-app-shell-nav-surface,
|
|
853
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='main'] {
|
|
857
|
+
background-color: var(--slotted-app-shell-main-surface, transparent);
|
|
858
|
+
}
|
|
859
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='aside'] {
|
|
860
|
+
background-color: var(
|
|
861
|
+
--slotted-app-shell-aside-surface,
|
|
862
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='footer'] {
|
|
866
|
+
background-color: var(
|
|
867
|
+
--slotted-app-shell-footer-surface,
|
|
868
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Block air, the gutter; inline air, the row inset on both sides, from
|
|
873
|
+
* `--_nav-inset`. A padding on one side alone is what put the closed strip
|
|
874
|
+
* off-centre on this plane once; none at all put every row's ground against
|
|
875
|
+
* the screen's edge and against the seam.
|
|
876
|
+
*/
|
|
877
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='nav'] {
|
|
878
|
+
padding-block: var(--_gutter);
|
|
879
|
+
}
|
|
880
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='banner'] {
|
|
881
|
+
border-block-end: var(--_seam);
|
|
882
|
+
}
|
|
883
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='footer'] {
|
|
884
|
+
border-block-start: var(--_seam);
|
|
885
|
+
}
|
|
886
|
+
[data-ui='app-shell'][data-canvas='flush'] > [data-part='aside'] {
|
|
887
|
+
border-block-start: var(--_seam);
|
|
888
|
+
}
|
|
889
|
+
/* The seams inside a region: wherever regions touch without a gutter, which
|
|
890
|
+
is every seam of `flush` and every seam inside the `panel` card. */
|
|
891
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])
|
|
892
|
+
> [data-part='nav']
|
|
893
|
+
> [data-part='nav-footer'] {
|
|
894
|
+
border-block-start: var(--_seam);
|
|
895
|
+
}
|
|
896
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])
|
|
897
|
+
> [data-part='aside']
|
|
898
|
+
> [data-part='aside-header'] {
|
|
899
|
+
border-block-end: var(--_seam);
|
|
900
|
+
}
|
|
901
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])
|
|
902
|
+
> [data-part='aside']
|
|
903
|
+
> [data-part='aside-footer'] {
|
|
904
|
+
border-block-start: var(--_seam);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/* ── The floating canvas ─────────────────────────────────────────────── */
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* The inset plane with the navigation lifted onto a card of its own: two
|
|
911
|
+
* cards on the sunken ground, a gutter apart, each with the edge, the radius
|
|
912
|
+
* and the elevation the content card has. The margin is the inset plane's,
|
|
913
|
+
* so the strip a reader sees is still the region's own box. A rail is one
|
|
914
|
+
* mark column and a row's inset each side, and the card's edge is added to
|
|
915
|
+
* it rather than taken from the marks.
|
|
916
|
+
*/
|
|
917
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='nav'] {
|
|
918
|
+
background-color: var(
|
|
919
|
+
--slotted-app-shell-nav-surface,
|
|
920
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
921
|
+
);
|
|
922
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
923
|
+
border-radius: var(--_radius);
|
|
924
|
+
box-shadow: var(--_elevation);
|
|
925
|
+
margin-block: var(--_gutter);
|
|
926
|
+
margin-inline-start: var(--_gutter);
|
|
927
|
+
}
|
|
928
|
+
/* A card has an edge the column never had: the rows stand one inset in, as
|
|
929
|
+
they do on the overlay card, and the bar's rows stand over them. */
|
|
930
|
+
[data-ui='app-shell'][data-canvas='floating'] {
|
|
931
|
+
--_edge: calc(var(--_gutter) + var(--_rule-width) + var(--_row-inset));
|
|
932
|
+
--_nav-edges: calc(2 * var(--_rule-width));
|
|
933
|
+
--_nav-inset: var(--_row-inset);
|
|
934
|
+
}
|
|
935
|
+
[data-ui='app-shell'][data-canvas='floating'][data-nav-side='end'] > [data-part='nav'] {
|
|
936
|
+
margin-inline: 0 var(--_gutter);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/* ── A card over more than one region ────────────────────────────────── */
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* The card that holds the banner and the content under `primary="main"`,
|
|
943
|
+
* and every region under `panel`, is one box: the root's `::before`, placed
|
|
944
|
+
* on the grid area the regions share. Two boxes with half a card each drew
|
|
945
|
+
* the shadow of one across the other at the seam. The pseudo-element is the
|
|
946
|
+
* grid's first item, so it paints under every region, and the regions over
|
|
947
|
+
* it keep their own grounds — clipped at the card's corners where their
|
|
948
|
+
* corners are the card's, so a sticky header scrolling inside the content
|
|
949
|
+
* does not square off the card's rounded edge.
|
|
950
|
+
*
|
|
951
|
+
* A layer is a phone's arrangement, and a phone has no room for a frame: the
|
|
952
|
+
* card goes wherever the navigation is a layer, as the inset card does.
|
|
953
|
+
*/
|
|
954
|
+
[data-ui='app-shell']:is(
|
|
955
|
+
[data-canvas='floating'],
|
|
956
|
+
[data-canvas='inset']
|
|
957
|
+
)[data-primary='main']::before,
|
|
958
|
+
[data-ui='app-shell'][data-canvas='panel']::before {
|
|
959
|
+
background-color: var(
|
|
960
|
+
--slotted-app-shell-main-surface,
|
|
961
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface))
|
|
962
|
+
);
|
|
963
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
964
|
+
border-radius: var(--_radius);
|
|
965
|
+
box-shadow: var(--_elevation);
|
|
966
|
+
content: '';
|
|
967
|
+
grid-area: 1 / 2 / 3 / 3;
|
|
968
|
+
margin: var(--_gutter);
|
|
969
|
+
min-inline-size: 0;
|
|
970
|
+
}
|
|
971
|
+
[data-ui='app-shell'][data-canvas='panel']::before {
|
|
972
|
+
grid-area: 2 / 1 / 3 / -1;
|
|
973
|
+
margin: 0;
|
|
974
|
+
}
|
|
975
|
+
[data-ui='app-shell'][data-canvas='panel']:is(
|
|
976
|
+
[data-primary='nav'],
|
|
977
|
+
[data-primary='main']
|
|
978
|
+
)::before {
|
|
979
|
+
grid-area: 1 / 1 / 3 / -1;
|
|
980
|
+
}
|
|
981
|
+
[data-ui='app-shell'][data-nav-mode='overlay'][data-canvas]::before {
|
|
982
|
+
content: none;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/* ── The banner in the plane of the content ──────────────────────────── */
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* The banner and the content are one card: the banner takes the card's top
|
|
989
|
+
* edge and sides and draws no ground of its own, the content takes the rest
|
|
990
|
+
* and keeps its ground with the card's lower corners. Between them is a
|
|
991
|
+
* seam, and `separation` decides it.
|
|
992
|
+
*/
|
|
993
|
+
[data-ui='app-shell']:is([data-canvas='floating'], [data-canvas='inset'])[data-primary='main']
|
|
994
|
+
> [data-part='banner'] {
|
|
995
|
+
margin-block-start: calc(var(--_gutter) + var(--_rule-width));
|
|
996
|
+
margin-inline: calc(var(--_gutter) + var(--_rule-width));
|
|
997
|
+
padding-inline: 0;
|
|
998
|
+
}
|
|
999
|
+
[data-ui='app-shell']:is([data-canvas='floating'], [data-canvas='inset'])[data-primary='main']
|
|
1000
|
+
> [data-part='main'] {
|
|
1001
|
+
border: none;
|
|
1002
|
+
border-radius: 0 0 var(--_inner-radius) var(--_inner-radius);
|
|
1003
|
+
box-shadow: none;
|
|
1004
|
+
margin-block: 0 calc(var(--_gutter) + var(--_rule-width));
|
|
1005
|
+
margin-inline: calc(var(--_gutter) + var(--_rule-width));
|
|
1006
|
+
}
|
|
1007
|
+
[data-ui='app-shell'][data-primary='main'] > [data-part='banner'],
|
|
1008
|
+
[data-ui='app-shell'][data-primary='main'][data-nav-side='end'] > [data-part='banner'],
|
|
1009
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='nav'] > [data-part='banner'],
|
|
1010
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='nav'][data-nav-side='end']
|
|
1011
|
+
> [data-part='banner'] {
|
|
1012
|
+
border-block-end: var(--_seam);
|
|
1013
|
+
grid-column: 2 / 3;
|
|
1014
|
+
}
|
|
1015
|
+
[data-ui='app-shell'][data-primary='main'][data-nav-mode='overlay'] > [data-part='banner'] {
|
|
1016
|
+
margin: 0;
|
|
1017
|
+
padding-inline: var(--_edge);
|
|
1018
|
+
}
|
|
1019
|
+
[data-ui='app-shell']:is(
|
|
1020
|
+
[data-canvas='floating'],
|
|
1021
|
+
[data-canvas='inset']
|
|
1022
|
+
)[data-primary='main'][data-nav-mode='overlay']
|
|
1023
|
+
> [data-part='main'] {
|
|
1024
|
+
border-radius: 0;
|
|
1025
|
+
margin: 0;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* ── The panel canvas ────────────────────────────────────────────────── */
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* One card holds every region but the footer, and the banner too once the
|
|
1032
|
+
* column or the content is primary. The frame's air is the root's padding,
|
|
1033
|
+
* so the grid's tracks are the card's inside and no region has to repeat a
|
|
1034
|
+
* margin the card has: the regions meet each other at seams, and the seams
|
|
1035
|
+
* are `separation`'s. The banner above the card stands on the frame at the
|
|
1036
|
+
* card's own edge, which is why the edge is nought here as under `flush`.
|
|
1037
|
+
*
|
|
1038
|
+
* The padding is dropped where the navigation is a layer. A shell not yet
|
|
1039
|
+
* measured below the compact width keeps it for one frame: the root cannot
|
|
1040
|
+
* query its own width, and that frame is the cost.
|
|
1041
|
+
*/
|
|
1042
|
+
[data-ui='app-shell'][data-canvas='panel'] {
|
|
1043
|
+
--_edge: calc(var(--_rule-width) + var(--_row-inset));
|
|
1044
|
+
--_nav-edges: var(--_seam-width);
|
|
1045
|
+
--_nav-inset: var(--_row-inset);
|
|
1046
|
+
}
|
|
1047
|
+
/* Once the navigation is a layer the card beside the bar is gone, and the
|
|
1048
|
+
bar's rows stand where the inset plane's do. */
|
|
1049
|
+
[data-ui='app-shell'][data-canvas='floating'][data-nav-mode='overlay'],
|
|
1050
|
+
[data-ui='app-shell'][data-canvas='panel'][data-nav-mode='overlay'] {
|
|
1051
|
+
--_edge: var(--_gutter);
|
|
1052
|
+
}
|
|
1053
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']) {
|
|
1054
|
+
box-sizing: border-box;
|
|
1055
|
+
padding-block-end: var(--_gutter);
|
|
1056
|
+
padding-inline: var(--_gutter);
|
|
1057
|
+
}
|
|
1058
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):is(
|
|
1059
|
+
[data-primary='nav'],
|
|
1060
|
+
[data-primary='main']
|
|
1061
|
+
) {
|
|
1062
|
+
padding-block-start: var(--_gutter);
|
|
1063
|
+
}
|
|
1064
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']) > [data-part='nav'] {
|
|
1065
|
+
border-end-start-radius: var(--_inner-radius);
|
|
1066
|
+
border-start-start-radius: var(--_inner-radius);
|
|
1067
|
+
margin: var(--_rule-width) 0 var(--_rule-width) var(--_rule-width);
|
|
1068
|
+
}
|
|
1069
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay'])[data-nav-side='end']
|
|
1070
|
+
> [data-part='nav'] {
|
|
1071
|
+
border-radius: 0 var(--_inner-radius) var(--_inner-radius) 0;
|
|
1072
|
+
margin: var(--_rule-width) var(--_rule-width) var(--_rule-width) 0;
|
|
1073
|
+
}
|
|
1074
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']) > [data-part='main'] {
|
|
1075
|
+
border-end-end-radius: var(--_inner-radius);
|
|
1076
|
+
margin-block-end: var(--_rule-width);
|
|
1077
|
+
margin-inline-end: var(--_rule-width);
|
|
1078
|
+
}
|
|
1079
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='banner']:not([data-nav-mode='overlay'])
|
|
1080
|
+
> [data-part='main'] {
|
|
1081
|
+
border-start-end-radius: var(--_inner-radius);
|
|
1082
|
+
margin-block-start: var(--_rule-width);
|
|
1083
|
+
}
|
|
1084
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):not(
|
|
1085
|
+
[data-primary='banner']
|
|
1086
|
+
)
|
|
1087
|
+
> [data-part='banner'] {
|
|
1088
|
+
margin-block-start: var(--_rule-width);
|
|
1089
|
+
margin-inline-end: var(--_rule-width);
|
|
1090
|
+
}
|
|
1091
|
+
/* The corner the content shares with the card is the pane's once the pane
|
|
1092
|
+
stands beside it. */
|
|
1093
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):has(
|
|
1094
|
+
> [data-part='aside'][data-open]
|
|
1095
|
+
)
|
|
1096
|
+
> [data-part='main'] {
|
|
1097
|
+
border-end-end-radius: 0;
|
|
1098
|
+
border-start-end-radius: 0;
|
|
1099
|
+
margin-inline-end: 0;
|
|
1100
|
+
}
|
|
1101
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):has(
|
|
1102
|
+
> [data-part='aside'][data-open]
|
|
1103
|
+
)
|
|
1104
|
+
> [data-part='banner'] {
|
|
1105
|
+
margin-inline-end: 0;
|
|
1106
|
+
}
|
|
1107
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):not(
|
|
1108
|
+
[data-aside-mode='overlay']
|
|
1109
|
+
)
|
|
1110
|
+
> [data-part='aside'] {
|
|
1111
|
+
border-end-end-radius: var(--_inner-radius);
|
|
1112
|
+
border-start-end-radius: var(--_inner-radius);
|
|
1113
|
+
margin: var(--_rule-width) var(--_rule-width) var(--_rule-width) 0;
|
|
1114
|
+
}
|
|
1115
|
+
[data-ui='app-shell'][data-canvas='panel'][data-nav-mode='overlay'] > [data-part='main'] {
|
|
1116
|
+
border-block-start: var(--_rule-width) solid var(--_rule);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/* ── The line the wait is drawn on ───────────────────────────────────── */
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Where the line between the chrome and the content is the banner's seam
|
|
1123
|
+
* rather than an edge of `main`, the frame of the wait stands that seam's
|
|
1124
|
+
* width higher, so the bar covers the line instead of sitting under it:
|
|
1125
|
+
* every seam of `flush`, the seam inside the card of `primary="main"`, and
|
|
1126
|
+
* the seam inside the `panel` card when the banner is in it. Above a
|
|
1127
|
+
* `panel` card it is the card's own top edge. Under `separation="tone"`
|
|
1128
|
+
* the seam is nought pixels wide and so is the lift. Where `main` keeps an
|
|
1129
|
+
* edge of its own — the inset and floating cards, and the top rule `panel`
|
|
1130
|
+
* gives it once the navigation is a layer — the frame is the box of `main`
|
|
1131
|
+
* and covers that edge already.
|
|
1132
|
+
*/
|
|
1133
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-primary='main']),
|
|
1134
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='nav'] {
|
|
1135
|
+
--_progress-lift: var(--_seam-width);
|
|
1136
|
+
}
|
|
1137
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='banner'] {
|
|
1138
|
+
--_progress-lift: var(--_rule-width);
|
|
1139
|
+
}
|
|
1140
|
+
[data-ui='app-shell'][data-canvas='panel'][data-nav-mode='overlay'] {
|
|
1141
|
+
--_progress-lift: 0px;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/* ── The frame of the wait, on the box of the content ────────────────── */
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Every margin and every corner `main` takes above, restated for the frame
|
|
1148
|
+
* in the same order and at the same specificity, so the frame is the box of
|
|
1149
|
+
* `main` in every arrangement and the bar on its edge is clipped by the
|
|
1150
|
+
* card's own corners. Only the geometry: the frame draws no edge, no ground
|
|
1151
|
+
* and no lift. The two twins of a shell not yet measured stand beside
|
|
1152
|
+
* their originals in the narrow query below. `app-shell.styles.test.mjs`
|
|
1153
|
+
* holds every rule that gives `main` a margin or a radius to its twin.
|
|
1154
|
+
*/
|
|
1155
|
+
[data-ui='app-shell'][data-canvas='floating'] > [data-part='progress'],
|
|
1156
|
+
[data-ui='app-shell'][data-canvas='inset'] > [data-part='progress'] {
|
|
1157
|
+
border-radius: var(--_radius);
|
|
1158
|
+
margin-block: var(--_gutter);
|
|
1159
|
+
margin-inline: var(--_gutter);
|
|
1160
|
+
}
|
|
1161
|
+
[data-ui='app-shell'][data-canvas='floating'][data-nav-mode='overlay'] > [data-part='progress'],
|
|
1162
|
+
[data-ui='app-shell'][data-canvas='inset'][data-nav-mode='overlay'] > [data-part='progress'] {
|
|
1163
|
+
border-radius: 0;
|
|
1164
|
+
margin: 0;
|
|
1165
|
+
}
|
|
1166
|
+
[data-ui='app-shell']:is([data-canvas='floating'], [data-canvas='inset'])[data-primary='main']
|
|
1167
|
+
> [data-part='progress'] {
|
|
1168
|
+
border-radius: 0 0 var(--_inner-radius) var(--_inner-radius);
|
|
1169
|
+
margin-block: 0 calc(var(--_gutter) + var(--_rule-width));
|
|
1170
|
+
margin-inline: calc(var(--_gutter) + var(--_rule-width));
|
|
1171
|
+
}
|
|
1172
|
+
[data-ui='app-shell']:is(
|
|
1173
|
+
[data-canvas='floating'],
|
|
1174
|
+
[data-canvas='inset']
|
|
1175
|
+
)[data-primary='main'][data-nav-mode='overlay']
|
|
1176
|
+
> [data-part='progress'] {
|
|
1177
|
+
border-radius: 0;
|
|
1178
|
+
margin: 0;
|
|
1179
|
+
}
|
|
1180
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay'])
|
|
1181
|
+
> [data-part='progress'] {
|
|
1182
|
+
border-end-end-radius: var(--_inner-radius);
|
|
1183
|
+
margin-block-end: var(--_rule-width);
|
|
1184
|
+
margin-inline-end: var(--_rule-width);
|
|
1185
|
+
}
|
|
1186
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='banner']:not([data-nav-mode='overlay'])
|
|
1187
|
+
> [data-part='progress'] {
|
|
1188
|
+
border-start-end-radius: var(--_inner-radius);
|
|
1189
|
+
margin-block-start: var(--_rule-width);
|
|
1190
|
+
}
|
|
1191
|
+
[data-ui='app-shell'][data-canvas='panel']:not([data-nav-mode='overlay']):has(
|
|
1192
|
+
> [data-part='aside'][data-open]
|
|
1193
|
+
)
|
|
1194
|
+
> [data-part='progress'] {
|
|
1195
|
+
border-end-end-radius: 0;
|
|
1196
|
+
border-start-end-radius: 0;
|
|
1197
|
+
margin-inline-end: 0;
|
|
1198
|
+
}
|
|
1199
|
+
/* ── Separation by tone ──────────────────────────────────────────────── */
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* No line: the chrome regions take a ground of their own and the content
|
|
1203
|
+
* keeps the surface. The tone is 4.5 per cent of the ink over the surface,
|
|
1204
|
+
* 0.036 OKLab L from it in light and in dark; the two page surfaces this
|
|
1205
|
+
* arrangement used to rely on are 0.02 apart, which read as nothing.
|
|
1206
|
+
*
|
|
1207
|
+
* A ground swapped without its tones leaves every control calibrated against
|
|
1208
|
+
* a page it is not on: the subtle hover a destination and the disclosure
|
|
1209
|
+
* control draw was the tone itself, and pressing a row changed nothing.
|
|
1210
|
+
* Inside a toned region the pair is recomputed from the region's ground, and
|
|
1211
|
+
* every control in it reads the pair it now stands on. The chain is written
|
|
1212
|
+
* at the region, for the reason the palette above is: a `var()` resolves
|
|
1213
|
+
* where it is declared, and a region carrying a scheme of its own must mix
|
|
1214
|
+
* its own ink.
|
|
1215
|
+
*/
|
|
1216
|
+
[data-ui='app-shell'][data-separation='tone'] {
|
|
1217
|
+
--_seam: none;
|
|
1218
|
+
--_seam-width: 0px;
|
|
1219
|
+
}
|
|
1220
|
+
[data-ui='app-shell'][data-canvas='flush'][data-separation='tone'] > [data-part='footer'] {
|
|
1221
|
+
--_ground: var(
|
|
1222
|
+
--slotted-app-shell-footer-surface,
|
|
1223
|
+
var(
|
|
1224
|
+
--slotted-app-shell-tone-surface,
|
|
1225
|
+
color-mix(in oklab, var(--slotted-surface), var(--slotted-text) 4.5%)
|
|
1226
|
+
)
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
[data-ui='app-shell'][data-canvas='flush'][data-separation='tone']:not([data-primary='main'])
|
|
1230
|
+
> [data-part='banner'] {
|
|
1231
|
+
--_ground: var(
|
|
1232
|
+
--slotted-app-shell-banner-surface,
|
|
1233
|
+
var(
|
|
1234
|
+
--slotted-app-shell-tone-surface,
|
|
1235
|
+
color-mix(in oklab, var(--slotted-surface), var(--slotted-text) 4.5%)
|
|
1236
|
+
)
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])[data-separation='tone']
|
|
1240
|
+
> [data-part='nav'] {
|
|
1241
|
+
--_ground: var(
|
|
1242
|
+
--slotted-app-shell-nav-surface,
|
|
1243
|
+
var(
|
|
1244
|
+
--slotted-app-shell-tone-surface,
|
|
1245
|
+
color-mix(in oklab, var(--slotted-surface), var(--slotted-text) 4.5%)
|
|
1246
|
+
)
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])[data-separation='tone']
|
|
1250
|
+
> [data-part='aside'] {
|
|
1251
|
+
--_ground: var(
|
|
1252
|
+
--slotted-app-shell-aside-surface,
|
|
1253
|
+
var(
|
|
1254
|
+
--slotted-app-shell-tone-surface,
|
|
1255
|
+
color-mix(in oklab, var(--slotted-surface), var(--slotted-text) 4.5%)
|
|
1256
|
+
)
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
[data-ui='app-shell'][data-separation='tone'] > [data-part='nav'] > [data-part='nav-rail'] {
|
|
1260
|
+
--_ground: var(
|
|
1261
|
+
--slotted-app-shell-tone-surface,
|
|
1262
|
+
color-mix(in oklab, var(--slotted-surface), var(--slotted-text) 4.5%)
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
[data-ui='app-shell'][data-canvas='flush'][data-separation='tone']:not([data-primary='main'])
|
|
1266
|
+
> [data-part='banner'],
|
|
1267
|
+
[data-ui='app-shell'][data-canvas='flush'][data-separation='tone'] > [data-part='footer'],
|
|
1268
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])[data-separation='tone']
|
|
1269
|
+
> :is([data-part='nav'], [data-part='aside']),
|
|
1270
|
+
[data-ui='app-shell'][data-separation='tone'] > [data-part='nav'] > [data-part='nav-rail'] {
|
|
1271
|
+
--slotted-tone-secondary-subtle-hover: color-mix(
|
|
1272
|
+
in oklab,
|
|
1273
|
+
var(--_ground),
|
|
1274
|
+
var(--slotted-text) 4%
|
|
1275
|
+
);
|
|
1276
|
+
--slotted-tone-secondary-subtle-active: color-mix(
|
|
1277
|
+
in oklab,
|
|
1278
|
+
var(--_ground),
|
|
1279
|
+
var(--slotted-text) 7%
|
|
1280
|
+
);
|
|
1281
|
+
|
|
1282
|
+
background-color: var(--_ground);
|
|
1283
|
+
}
|
|
1284
|
+
/* The content keeps the surface under a toned frame: on the flush plane it
|
|
1285
|
+
carried the sunken ground, which is 0.014 from the tone. */
|
|
1286
|
+
[data-ui='app-shell'][data-canvas='flush'][data-separation='tone'] > [data-part='main'] {
|
|
1287
|
+
background-color: var(
|
|
1288
|
+
--slotted-app-shell-main-surface,
|
|
1289
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/* ── A rail of its own ───────────────────────────────────────────────── */
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* A strip of marks before the column: the first level of a two-level
|
|
1297
|
+
* navigation, inside the one navigation region and under its one open state.
|
|
1298
|
+
* The region becomes two columns, the strip at the rail's width and the
|
|
1299
|
+
* column at the navigation's, and closing to `rail` narrows the region to
|
|
1300
|
+
* the strip: the column is cut by the region's own overflow while the width
|
|
1301
|
+
* moves, and leaves the tab order and the accessibility tree at the end of
|
|
1302
|
+
* the move — `visibility` steps at the end on the way out and at the start
|
|
1303
|
+
* on the way in. `:has()` recognises the part, so no script writes an
|
|
1304
|
+
* attribute and a server render has the grid on its first paint.
|
|
1305
|
+
*/
|
|
1306
|
+
[data-ui='app-shell'] > [data-part='nav']:has(> [data-part='nav-rail']) {
|
|
1307
|
+
display: grid;
|
|
1308
|
+
grid-template-columns: var(--_rail-size) minmax(var(--_nav-size), 1fr);
|
|
1309
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
1310
|
+
}
|
|
1311
|
+
[data-ui='app-shell'] > [data-part='nav'] > [data-part='nav-rail'] {
|
|
1312
|
+
align-items: center;
|
|
1313
|
+
border-inline-end: var(--_seam);
|
|
1314
|
+
box-sizing: border-box;
|
|
1315
|
+
display: flex;
|
|
1316
|
+
flex-direction: column;
|
|
1317
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
1318
|
+
grid-area: 1 / 1 / -1 / 2;
|
|
1319
|
+
min-block-size: 0;
|
|
1320
|
+
overflow: auto;
|
|
1321
|
+
overscroll-behavior: contain;
|
|
1322
|
+
padding-block: var(--_row-inset);
|
|
1323
|
+
scrollbar-width: none;
|
|
1324
|
+
}
|
|
1325
|
+
[data-ui='app-shell']
|
|
1326
|
+
> [data-part='nav']:has(> [data-part='nav-rail'])
|
|
1327
|
+
> [data-part='nav-header'] {
|
|
1328
|
+
grid-area: 1 / 2 / 2 / 3;
|
|
1329
|
+
}
|
|
1330
|
+
[data-ui='app-shell'] > [data-part='nav']:has(> [data-part='nav-rail']) > [data-part='nav-body'] {
|
|
1331
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
1332
|
+
}
|
|
1333
|
+
[data-ui='app-shell']
|
|
1334
|
+
> [data-part='nav']:has(> [data-part='nav-rail'])
|
|
1335
|
+
> [data-part='nav-footer'] {
|
|
1336
|
+
grid-area: 3 / 2 / 4 / 3;
|
|
1337
|
+
}
|
|
1338
|
+
/* The strip's rule is an edge, and a row whose ground meets it reads as a
|
|
1339
|
+
column with no padding. */
|
|
1340
|
+
[data-ui='app-shell']
|
|
1341
|
+
> [data-part='nav']:has(> [data-part='nav-rail'])
|
|
1342
|
+
> :is([data-part='nav-header'], [data-part='nav-body'], [data-part='nav-footer']) {
|
|
1343
|
+
padding-inline-start: var(--_row-inset);
|
|
1344
|
+
transition: visibility var(--_duration) step-start;
|
|
1345
|
+
}
|
|
1346
|
+
[data-ui='app-shell'][data-nav-mode='inline'][data-open]
|
|
1347
|
+
> [data-part='nav']:has(> [data-part='nav-rail']) {
|
|
1348
|
+
inline-size: calc(var(--_rail-size) + var(--_nav-size) + var(--_nav-edges));
|
|
1349
|
+
}
|
|
1350
|
+
/* Closed, the region is the strip and its edges: the strip holds a row's
|
|
1351
|
+
inset on each side of its marks already. */
|
|
1352
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1353
|
+
> [data-part='nav']:has(> [data-part='nav-rail']) {
|
|
1354
|
+
inline-size: calc(var(--_rail-size) + var(--_nav-edges));
|
|
1355
|
+
}
|
|
1356
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1357
|
+
> [data-part='nav']:has(> [data-part='nav-rail'])
|
|
1358
|
+
> :is([data-part='nav-header'], [data-part='nav-body'], [data-part='nav-footer']) {
|
|
1359
|
+
transition: visibility var(--_duration) step-end;
|
|
1360
|
+
visibility: hidden;
|
|
1361
|
+
}
|
|
1362
|
+
/* A closed strip is the region's edge, and the edge is the plane's to draw. */
|
|
1363
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1364
|
+
> [data-part='nav']
|
|
1365
|
+
> [data-part='nav-rail'] {
|
|
1366
|
+
border-inline-end: none;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/* ── Forced colours ──────────────────────────────────────────────────── */
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Every ground is `Canvas` here, so a tone separates nothing: the seams are
|
|
1373
|
+
* drawn whatever `separation` says. The theme cannot reach a boundary the
|
|
1374
|
+
* family draws with a ground, which is the case the briefing allows a family
|
|
1375
|
+
* a forced-colours rule of its own for.
|
|
1376
|
+
*/
|
|
1377
|
+
@media (forced-colors: active) {
|
|
1378
|
+
[data-ui='app-shell'][data-separation='tone'] {
|
|
1379
|
+
--_seam: var(--_rule-width) solid CanvasText;
|
|
1380
|
+
--_seam-width: var(--_rule-width);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/* ── The disclosure controls ─────────────────────────────────────────── */
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* The mark draws the region it toggles: a frame with a bar down one side,
|
|
1388
|
+
* and a chevron saying which way pressing sends it. Three bars would say
|
|
1389
|
+
* "a menu is here" and say nothing about which menu or where it goes; a
|
|
1390
|
+
* double chevron says "all the way to the end", which is what `pagination`
|
|
1391
|
+
* draws it for. Both were on this control before this one was.
|
|
1392
|
+
*
|
|
1393
|
+
* Its box is the row's mark column, so the control in the bar stands over
|
|
1394
|
+
* the marks in the navigation rather than three pixels off them.
|
|
1395
|
+
*
|
|
1396
|
+
* The pane's control is the same control: the same box, the same ink, the
|
|
1397
|
+
* same pair of marks. What differs is which way the artwork faces, below.
|
|
1398
|
+
*/
|
|
1399
|
+
[data-ui='app-shell'] [data-part='nav-toggle'],
|
|
1400
|
+
[data-ui='app-shell'] [data-part='aside-toggle'] {
|
|
1401
|
+
--slotted-icon-size: var(--slotted-control-icon-size-md, 16px);
|
|
1402
|
+
|
|
1403
|
+
background-color: transparent;
|
|
1404
|
+
block-size: var(--_row-mark);
|
|
1405
|
+
border: none;
|
|
1406
|
+
border-radius: var(--slotted-control-radius, 4px);
|
|
1407
|
+
/**
|
|
1408
|
+
* Quieter than the region it stands in, and *of* it: the control appears
|
|
1409
|
+
* in the bar and in the column's foot, and either may have been repainted.
|
|
1410
|
+
* Read from a token instead, it kept the light scheme's ink over a dark
|
|
1411
|
+
* column and the mark disappeared.
|
|
1412
|
+
*/
|
|
1413
|
+
color: color-mix(in oklab, currentcolor 70%, transparent);
|
|
1414
|
+
cursor: pointer;
|
|
1415
|
+
display: inline-grid;
|
|
1416
|
+
flex: 0 0 auto;
|
|
1417
|
+
inline-size: var(--_row-mark);
|
|
1418
|
+
padding: 0;
|
|
1419
|
+
place-items: center;
|
|
1420
|
+
transition:
|
|
1421
|
+
background-color var(--slotted-control-transition-duration)
|
|
1422
|
+
var(--slotted-control-transition-easing, cubic-bezier(0.2, 0, 0, 1)),
|
|
1423
|
+
color var(--slotted-control-transition-duration)
|
|
1424
|
+
var(--slotted-control-transition-easing, cubic-bezier(0.2, 0, 0, 1));
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* The artwork is the `panel-open` or `panel-close` Glyph element, chosen by
|
|
1429
|
+
* the binding from the region's state. It is direction-sensitive — the bar
|
|
1430
|
+
* is on one side of the frame and the chevron points along the line — so
|
|
1431
|
+
* the family turns it whole wherever the region is not at the start of the
|
|
1432
|
+
* line. The pane is on the end the navigation is not, so its control is the
|
|
1433
|
+
* navigation's turned, and turns back under the same conditions the
|
|
1434
|
+
* navigation's turns. The box is not `directional`: which side the region
|
|
1435
|
+
* stands on is this family's fact, not the reading direction's alone.
|
|
1436
|
+
*/
|
|
1437
|
+
[data-ui='app-shell'][data-nav-side='end'] [data-part='nav-toggle'] > [data-ui='icon'],
|
|
1438
|
+
[dir='rtl'] [data-ui='app-shell'] [data-part='nav-toggle'] > [data-ui='icon'],
|
|
1439
|
+
[data-ui='app-shell']:not([data-nav-side='end']) [data-part='aside-toggle'] > [data-ui='icon'],
|
|
1440
|
+
[dir='rtl']
|
|
1441
|
+
[data-ui='app-shell'][data-nav-side='end']
|
|
1442
|
+
[data-part='aside-toggle']
|
|
1443
|
+
> [data-ui='icon'] {
|
|
1444
|
+
scale: -1 1;
|
|
1445
|
+
}
|
|
1446
|
+
[dir='rtl']
|
|
1447
|
+
[data-ui='app-shell'][data-nav-side='end']
|
|
1448
|
+
[data-part='nav-toggle']
|
|
1449
|
+
> [data-ui='icon'],
|
|
1450
|
+
[dir='rtl']
|
|
1451
|
+
[data-ui='app-shell']:not([data-nav-side='end'])
|
|
1452
|
+
[data-part='aside-toggle']
|
|
1453
|
+
> [data-ui='icon'] {
|
|
1454
|
+
scale: 1 1;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
[data-ui='app-shell'] [data-part='nav-toggle']:enabled:hover,
|
|
1458
|
+
[data-ui='app-shell'] [data-part='aside-toggle']:enabled:hover {
|
|
1459
|
+
background-color: var(--slotted-tone-secondary-subtle-hover, ButtonFace);
|
|
1460
|
+
color: currentcolor;
|
|
1461
|
+
}
|
|
1462
|
+
[data-ui='app-shell'] [data-part='nav-toggle']:enabled:active,
|
|
1463
|
+
[data-ui='app-shell'] [data-part='aside-toggle']:enabled:active {
|
|
1464
|
+
background-color: var(--slotted-tone-secondary-subtle-active, ButtonFace);
|
|
1465
|
+
}
|
|
1466
|
+
[data-ui='app-shell'] [data-part='nav-toggle']:focus-visible,
|
|
1467
|
+
[data-ui='app-shell'] [data-part='aside-toggle']:focus-visible {
|
|
1468
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
1469
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/* ── The navigation as a column ──────────────────────────────────────── */
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* Keyed on the mode and not on the width, because a consumer may state the
|
|
1476
|
+
* mode at any width. The container query further down is the fallback for
|
|
1477
|
+
* the frame before the binding has measured, and nothing else.
|
|
1478
|
+
*/
|
|
1479
|
+
[data-ui='app-shell'][data-nav-mode='inline'] > [data-part='nav'] {
|
|
1480
|
+
inline-size: calc(var(--_rail-size) + 2 * var(--_nav-inset) + var(--_nav-edges));
|
|
1481
|
+
}
|
|
1482
|
+
[data-ui='app-shell'][data-nav-mode='inline'][data-open] > [data-part='nav'] {
|
|
1483
|
+
inline-size: var(--_nav-size);
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* The rail centres its marks, all three rows together: a head centred over a
|
|
1487
|
+
* body that is not reads as two columns in one strip.
|
|
1488
|
+
*/
|
|
1489
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1490
|
+
> [data-part='nav']
|
|
1491
|
+
> [data-part='nav-header'],
|
|
1492
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1493
|
+
> [data-part='nav']
|
|
1494
|
+
> [data-part='nav-footer'] {
|
|
1495
|
+
/* Two marks in one end of a rail — the control and the account — stand
|
|
1496
|
+
one over the other: the rail is one mark column wide, so a row of two
|
|
1497
|
+
is a row that overflows it. */
|
|
1498
|
+
flex-wrap: wrap;
|
|
1499
|
+
justify-content: center;
|
|
1500
|
+
}
|
|
1501
|
+
[data-ui='app-shell'][data-nav-mode='inline']:not([data-open])
|
|
1502
|
+
> [data-part='nav']
|
|
1503
|
+
> [data-part='nav-body'] {
|
|
1504
|
+
align-items: center;
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Nothing left behind: the region keeps no measure and no tab stop. Mantine
|
|
1508
|
+
* and Ant leave a zero-width navbar in the flow and `Tab` still crosses every
|
|
1509
|
+
* destination in it. `display: none` rather than a width of nought, because a
|
|
1510
|
+
* region at nought pixels still lays its children out and puts content
|
|
1511
|
+
* outside the box that owns it — `content-visibility: hidden` was tried and
|
|
1512
|
+
* preserves the last measured size instead. The cost is written: closing to
|
|
1513
|
+
* `hidden` does not animate, because it is a removal rather than a change of
|
|
1514
|
+
* width.
|
|
1515
|
+
*/
|
|
1516
|
+
[data-ui='app-shell'][data-nav-mode='inline'][data-collapsed-to='hidden']:not([data-open])
|
|
1517
|
+
> [data-part='nav'] {
|
|
1518
|
+
display: none;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
[data-ui='app-shell'][data-nav-side='end'] > [data-part='nav'] {
|
|
1522
|
+
grid-area: 2 / 3 / 3 / 4;
|
|
1523
|
+
}
|
|
1524
|
+
[data-ui='app-shell'][data-canvas='inset'][data-nav-side='end'] > [data-part='nav'] {
|
|
1525
|
+
margin-inline: 0 var(--_gutter);
|
|
1526
|
+
}
|
|
1527
|
+
[data-ui='app-shell'][data-canvas='flush'][data-nav-side='end'] > [data-part='nav'] {
|
|
1528
|
+
padding-inline: 0 var(--_gutter);
|
|
1529
|
+
}
|
|
1530
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel']) > [data-part='nav'] {
|
|
1531
|
+
border-inline-end: var(--_seam);
|
|
1532
|
+
}
|
|
1533
|
+
[data-ui='app-shell']:is([data-canvas='flush'], [data-canvas='panel'])[data-nav-side='end']
|
|
1534
|
+
> [data-part='nav'] {
|
|
1535
|
+
border-inline-end: none;
|
|
1536
|
+
border-inline-start: var(--_seam);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/** Reaching the corner: the navigation spans every row instead. */
|
|
1540
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main']) > [data-part='nav'] {
|
|
1541
|
+
grid-area: 1 / 1 / -1 / 2;
|
|
1542
|
+
}
|
|
1543
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main'])[data-nav-side='end']
|
|
1544
|
+
> [data-part='nav'] {
|
|
1545
|
+
grid-area: 1 / 3 / -1 / 4;
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* The navigation's own head takes the banner's height, so the workspace mark
|
|
1549
|
+
* and whatever the bar carries sit on one line rather than being two headers
|
|
1550
|
+
* of different sizes in the same row of the screen.
|
|
1551
|
+
*/
|
|
1552
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main'])
|
|
1553
|
+
> [data-part='nav']
|
|
1554
|
+
> [data-part='nav-header'] {
|
|
1555
|
+
/* The head's own padding is inside the banner's height, or the mark stands
|
|
1556
|
+
its padding lower than the bar beside it. */
|
|
1557
|
+
box-sizing: border-box;
|
|
1558
|
+
min-block-size: var(--_banner-size);
|
|
1559
|
+
}
|
|
1560
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main']) > [data-part='banner'],
|
|
1561
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main']) > [data-part='footer'] {
|
|
1562
|
+
grid-column: 2 / -1;
|
|
1563
|
+
}
|
|
1564
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main'])[data-nav-side='end']
|
|
1565
|
+
> [data-part='banner'],
|
|
1566
|
+
[data-ui='app-shell']:is([data-primary='nav'], [data-primary='main'])[data-nav-side='end']
|
|
1567
|
+
> [data-part='footer'] {
|
|
1568
|
+
grid-column: 1 / -2;
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* The banner in the plane of the content stands over the content's column
|
|
1572
|
+
* alone: the pane beside it, as a column, spans the banner's row. Under
|
|
1573
|
+
* `panel` the banner is inside the card whether the column or the content
|
|
1574
|
+
* is primary, so `nav` draws what `main` draws there.
|
|
1575
|
+
*/
|
|
1576
|
+
[data-ui='app-shell'][data-primary='main'] > [data-part='banner'],
|
|
1577
|
+
[data-ui='app-shell'][data-canvas='panel'][data-primary='nav'] > [data-part='banner'] {
|
|
1578
|
+
grid-column: 2 / 3;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/* ── The navigation as a layer ───────────────────────────────────────── */
|
|
1582
|
+
|
|
1583
|
+
/**
|
|
1584
|
+
* Out of flow, so the track it was in collapses on its own and the root's
|
|
1585
|
+
* template is never rewritten.
|
|
1586
|
+
*
|
|
1587
|
+
* `grid-area: 2 / 1 / -1 / -1` is what makes the panel stop under the banner
|
|
1588
|
+
* and be the height of everything below it. An absolutely positioned grid
|
|
1589
|
+
* child placed into a definite area takes *that area* as its containing
|
|
1590
|
+
* block, not the grid's padding box — so the rows say where the panel ends
|
|
1591
|
+
* without anyone measuring the banner, and a banner that wraps to two lines
|
|
1592
|
+
* moves the panel with it.
|
|
1593
|
+
*
|
|
1594
|
+
* The alternative is a sheet over everything, which Material draws and which
|
|
1595
|
+
* puts the veil over the bar as well: the reader then has no fixed point on
|
|
1596
|
+
* the screen at all.
|
|
1597
|
+
*/
|
|
1598
|
+
[data-ui='app-shell'][data-nav-mode='overlay'] > [data-part='nav'] {
|
|
1599
|
+
/* A layer has an edge on every plane, so its rows stand one inset in. The
|
|
1600
|
+
inset is read by the parts, which take it from here. */
|
|
1601
|
+
--_nav-inset: var(--_row-inset);
|
|
1602
|
+
|
|
1603
|
+
/* A layer over the content is opaque or it is unreadable, so the plane's
|
|
1604
|
+
transparent default does not reach it — the product's own choice still
|
|
1605
|
+
does, which is why this is the property and not the colour. */
|
|
1606
|
+
background-color: var(
|
|
1607
|
+
--slotted-app-shell-nav-surface,
|
|
1608
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
1609
|
+
);
|
|
1610
|
+
border-block-start: var(--_rule-width) solid var(--_rule);
|
|
1611
|
+
box-shadow: var(--slotted-shadow-lg);
|
|
1612
|
+
display: flex;
|
|
1613
|
+
grid-area: 2 / 1 / -1 / -1;
|
|
1614
|
+
inline-size: min(var(--_nav-overlay-size), calc(100% - var(--_row-mark) - 2 * var(--_gutter)));
|
|
1615
|
+
inset-block: 0;
|
|
1616
|
+
inset-inline-start: 0;
|
|
1617
|
+
margin: 0;
|
|
1618
|
+
padding-inline: 0;
|
|
1619
|
+
position: absolute;
|
|
1620
|
+
transition:
|
|
1621
|
+
translate var(--_duration) var(--slotted-easing-exit, cubic-bezier(0.3, 0, 0.8, 0.15)),
|
|
1622
|
+
visibility var(--_duration) step-end;
|
|
1623
|
+
translate: -100% 0;
|
|
1624
|
+
visibility: hidden;
|
|
1625
|
+
z-index: var(--slotted-z-index-overlay, 1000);
|
|
1626
|
+
}
|
|
1627
|
+
[data-ui='app-shell'][data-nav-mode='overlay'][data-open] > [data-part='nav'] {
|
|
1628
|
+
transition:
|
|
1629
|
+
translate var(--_duration) var(--slotted-easing-enter, cubic-bezier(0.05, 0.7, 0.1, 1)),
|
|
1630
|
+
visibility var(--_duration) step-start;
|
|
1631
|
+
translate: 0 0;
|
|
1632
|
+
visibility: visible;
|
|
1633
|
+
}
|
|
1634
|
+
[data-ui='app-shell'][data-nav-mode='overlay'][data-nav-side='end'] > [data-part='nav'] {
|
|
1635
|
+
inset-inline-end: 0;
|
|
1636
|
+
inset-inline-start: auto;
|
|
1637
|
+
translate: 100% 0;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* On the inset plane the panel is a card like every other region, so it takes
|
|
1642
|
+
* the same gutter, the same radius and the same elevation rather than being
|
|
1643
|
+
* a sheet welded to the frame's edge — the one region that ignored the plane
|
|
1644
|
+
* it slid onto. The closed position then has to clear the gutter as well, or
|
|
1645
|
+
* the panel leaves a strip of itself against the edge.
|
|
1646
|
+
*
|
|
1647
|
+
* A card has an edge the column never had, and rows whose ground meets that
|
|
1648
|
+
* edge read as a panel with no padding. One row inset on the card, for all
|
|
1649
|
+
* three parts: the marks keep one column, and a consumer's header row that
|
|
1650
|
+
* writes the same inset stays over them.
|
|
1651
|
+
*/
|
|
1652
|
+
[data-ui='app-shell'][data-canvas='floating'][data-nav-mode='overlay'] > [data-part='nav'],
|
|
1653
|
+
[data-ui='app-shell'][data-canvas='panel'][data-nav-mode='overlay'] > [data-part='nav'],
|
|
1654
|
+
[data-ui='app-shell'][data-canvas='inset'][data-nav-mode='overlay'] > [data-part='nav'] {
|
|
1655
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
1656
|
+
border-radius: var(--_radius);
|
|
1657
|
+
box-shadow: var(--slotted-shadow-lg);
|
|
1658
|
+
margin-block: var(--_gutter);
|
|
1659
|
+
margin-inline-start: var(--_gutter);
|
|
1660
|
+
translate: calc(-100% - var(--_gutter)) 0;
|
|
1661
|
+
}
|
|
1662
|
+
[data-ui='app-shell']:not([data-canvas='flush'])[data-nav-mode='overlay'][data-nav-side='end']
|
|
1663
|
+
> [data-part='nav'] {
|
|
1664
|
+
margin-inline-end: var(--_gutter);
|
|
1665
|
+
margin-inline-start: 0;
|
|
1666
|
+
translate: calc(100% + var(--_gutter)) 0;
|
|
1667
|
+
}
|
|
1668
|
+
[data-ui='app-shell']:not([data-canvas='flush'])[data-nav-mode='overlay'][data-open]
|
|
1669
|
+
> [data-part='nav'] {
|
|
1670
|
+
translate: 0 0;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/* ── The pane as a layer ─────────────────────────────────────────────── */
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* The navigation's layer, from the other end of the line: the same rows of
|
|
1677
|
+
* the grid, the same elevation, the same pair of easings, and the mirror of
|
|
1678
|
+
* every inline distance. It slides in from the end the navigation is not on,
|
|
1679
|
+
* which is the end the pane stands at as a column.
|
|
1680
|
+
*/
|
|
1681
|
+
[data-ui='app-shell'][data-aside-mode='overlay'] > [data-part='aside'] {
|
|
1682
|
+
background-color: var(
|
|
1683
|
+
--slotted-app-shell-aside-surface,
|
|
1684
|
+
var(--slotted-app-shell-canvas, var(--slotted-surface, Canvas))
|
|
1685
|
+
);
|
|
1686
|
+
border-block-start: var(--_rule-width) solid var(--_rule);
|
|
1687
|
+
box-shadow: var(--slotted-shadow-lg);
|
|
1688
|
+
display: flex;
|
|
1689
|
+
grid-area: 2 / 1 / -1 / -1;
|
|
1690
|
+
inline-size: var(--_aside-size);
|
|
1691
|
+
inset-block: 0;
|
|
1692
|
+
inset-inline-end: 0;
|
|
1693
|
+
inset-inline-start: auto;
|
|
1694
|
+
margin: 0;
|
|
1695
|
+
max-inline-size: 80%;
|
|
1696
|
+
position: absolute;
|
|
1697
|
+
transition:
|
|
1698
|
+
translate var(--_duration) var(--slotted-easing-exit, cubic-bezier(0.3, 0, 0.8, 0.15)),
|
|
1699
|
+
visibility var(--_duration) step-end;
|
|
1700
|
+
translate: 100% 0;
|
|
1701
|
+
visibility: hidden;
|
|
1702
|
+
z-index: var(--slotted-z-index-overlay, 1000);
|
|
1703
|
+
}
|
|
1704
|
+
[data-ui='app-shell'][data-aside-mode='overlay'] > [data-part='aside'][data-open] {
|
|
1705
|
+
transition:
|
|
1706
|
+
translate var(--_duration) var(--slotted-easing-enter, cubic-bezier(0.05, 0.7, 0.1, 1)),
|
|
1707
|
+
visibility var(--_duration) step-start;
|
|
1708
|
+
translate: 0 0;
|
|
1709
|
+
visibility: visible;
|
|
1710
|
+
}
|
|
1711
|
+
[data-ui='app-shell'][data-aside-mode='overlay'][data-nav-side='end'] > [data-part='aside'] {
|
|
1712
|
+
inset-inline-end: auto;
|
|
1713
|
+
inset-inline-start: 0;
|
|
1714
|
+
translate: -100% 0;
|
|
1715
|
+
}
|
|
1716
|
+
[data-ui='app-shell'][data-canvas='floating'][data-aside-mode='overlay'] > [data-part='aside'],
|
|
1717
|
+
[data-ui='app-shell'][data-canvas='panel'][data-aside-mode='overlay'] > [data-part='aside'],
|
|
1718
|
+
[data-ui='app-shell'][data-canvas='inset'][data-aside-mode='overlay'] > [data-part='aside'] {
|
|
1719
|
+
border: var(--_rule-width) solid var(--_rule);
|
|
1720
|
+
border-radius: var(--_radius);
|
|
1721
|
+
box-shadow: var(--slotted-shadow-lg);
|
|
1722
|
+
margin-block: var(--_gutter);
|
|
1723
|
+
margin-inline-end: var(--_gutter);
|
|
1724
|
+
translate: calc(100% + var(--_gutter)) 0;
|
|
1725
|
+
}
|
|
1726
|
+
[data-ui='app-shell']:not([data-canvas='flush'])[data-aside-mode='overlay'][data-nav-side='end']
|
|
1727
|
+
> [data-part='aside'] {
|
|
1728
|
+
margin-inline-end: 0;
|
|
1729
|
+
margin-inline-start: var(--_gutter);
|
|
1730
|
+
translate: calc(-100% - var(--_gutter)) 0;
|
|
1731
|
+
}
|
|
1732
|
+
[data-ui='app-shell']:not([data-canvas='flush'])[data-aside-mode='overlay']
|
|
1733
|
+
> [data-part='aside'][data-open] {
|
|
1734
|
+
translate: 0 0;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
/* ── Before the binding has measured ─────────────────────────────────── */
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* The breakpoint is a literal because `@container (inline-size: var(--x))` is
|
|
1741
|
+
* invalid CSS. It is `COMPACT_BELOW` of `@slotted/core/measure`, the limit
|
|
1742
|
+
* of the compact size class; `specs/components/app-shell/contract.json`
|
|
1743
|
+
* states the same number under `overlayAt`, and `app-shell.styles.test.mjs`
|
|
1744
|
+
* compares every condition here against it.
|
|
1745
|
+
*
|
|
1746
|
+
* The first two blocks answer one question only: what a shell whose mode has
|
|
1747
|
+
* not been decided yet draws. A server render and the frame before hydration
|
|
1748
|
+
* carry no `data-nav-mode`, and a wide one is then a column while a narrow
|
|
1749
|
+
* one is nothing. Once the binding writes the attribute, the rules above own
|
|
1750
|
+
* the geometry at every width.
|
|
1751
|
+
*
|
|
1752
|
+
* The pane is the other way round: where it stands is a question of room
|
|
1753
|
+
* rather than of mode, so the width answers it and the width alone.
|
|
1754
|
+
*/
|
|
1755
|
+
@container slotted-app-shell (inline-size >= 600px) {
|
|
1756
|
+
[data-ui='app-shell']:not([data-nav-mode]) > [data-part='nav'] {
|
|
1757
|
+
inline-size: calc(var(--_rail-size) + 2 * var(--_nav-inset) + var(--_nav-edges));
|
|
1758
|
+
}
|
|
1759
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-open] > [data-part='nav'] {
|
|
1760
|
+
inline-size: var(--_nav-size);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/* The pane as a column, where there is room. `:not(overlay)`: a layer is
|
|
1764
|
+
placed by its mode and not by the width, exactly as the navigation's is,
|
|
1765
|
+
and these rules are for the column and the row alone. */
|
|
1766
|
+
/* The extent the reader set, when there is one, and the size step when
|
|
1767
|
+
not. The binding writes it in `cqi`: the root is the query container, so
|
|
1768
|
+
the unit resolves against the shell's own inline size from inside this
|
|
1769
|
+
`auto` track, where a percentage would resolve against the track the
|
|
1770
|
+
pane itself sizes. */
|
|
1771
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']) > [data-part='aside'] {
|
|
1772
|
+
grid-area: 2 / 3 / 3 / 4;
|
|
1773
|
+
inline-size: var(--slotted-app-shell-aside-extent, var(--_aside-size));
|
|
1774
|
+
position: relative;
|
|
1775
|
+
}
|
|
1776
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay'])
|
|
1777
|
+
> [data-part='aside']
|
|
1778
|
+
> [data-part='aside-resizer']:not([hidden]) {
|
|
1779
|
+
display: block;
|
|
1780
|
+
}
|
|
1781
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay'])[data-nav-side='end']
|
|
1782
|
+
> [data-part='aside'] {
|
|
1783
|
+
grid-area: 2 / 1 / 3 / 2;
|
|
1784
|
+
}
|
|
1785
|
+
/* Under `primary="main"` the pane, as a column, stands beside the card
|
|
1786
|
+
that holds the banner and the content, so it spans the banner's row. */
|
|
1787
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay'])[data-primary='main']
|
|
1788
|
+
> [data-part='aside'],
|
|
1789
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay'])[data-canvas='panel'][data-primary='nav']
|
|
1790
|
+
> [data-part='aside'] {
|
|
1791
|
+
grid-row: 1 / 3;
|
|
1792
|
+
}
|
|
1793
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']):is(
|
|
1794
|
+
[data-canvas='floating'],
|
|
1795
|
+
[data-canvas='inset']
|
|
1796
|
+
)[data-nav-side='end']
|
|
1797
|
+
> [data-part='aside'] {
|
|
1798
|
+
margin-inline: var(--_gutter) 0;
|
|
1799
|
+
}
|
|
1800
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay'])[data-canvas='flush']
|
|
1801
|
+
> [data-part='aside'] {
|
|
1802
|
+
border-block-start: none;
|
|
1803
|
+
}
|
|
1804
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']):is(
|
|
1805
|
+
[data-canvas='flush'],
|
|
1806
|
+
[data-canvas='panel']
|
|
1807
|
+
)
|
|
1808
|
+
> [data-part='aside'] {
|
|
1809
|
+
border-inline-start: var(--_seam);
|
|
1810
|
+
}
|
|
1811
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']):is(
|
|
1812
|
+
[data-canvas='flush'],
|
|
1813
|
+
[data-canvas='panel']
|
|
1814
|
+
)[data-nav-side='end']
|
|
1815
|
+
> [data-part='aside'] {
|
|
1816
|
+
border-inline-end: var(--_seam);
|
|
1817
|
+
border-inline-start: none;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
@container slotted-app-shell (inline-size < 600px) {
|
|
1822
|
+
[data-ui='app-shell']:not([data-nav-mode]) > [data-part='nav'] {
|
|
1823
|
+
display: none;
|
|
1824
|
+
}
|
|
1825
|
+
/* The content flush to the frame, as it is once the mode says overlay. */
|
|
1826
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='floating'] > [data-part='main'],
|
|
1827
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='inset'] > [data-part='main'] {
|
|
1828
|
+
border-block-end: 0;
|
|
1829
|
+
border-inline: 0;
|
|
1830
|
+
border-radius: 0;
|
|
1831
|
+
box-shadow: none;
|
|
1832
|
+
margin: 0;
|
|
1833
|
+
}
|
|
1834
|
+
/* The card of `primary="main"` goes with the gutter. The root cannot
|
|
1835
|
+
query its own width, but its pseudo-element is a child of the query
|
|
1836
|
+
container and reads it. */
|
|
1837
|
+
[data-ui='app-shell']:not([data-nav-mode]):is(
|
|
1838
|
+
[data-canvas='floating'],
|
|
1839
|
+
[data-canvas='inset']
|
|
1840
|
+
)[data-primary='main']::before {
|
|
1841
|
+
content: none;
|
|
1842
|
+
}
|
|
1843
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-primary='main'] > [data-part='banner'] {
|
|
1844
|
+
margin: 0;
|
|
1845
|
+
padding-inline: var(--_edge);
|
|
1846
|
+
}
|
|
1847
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='panel'] > [data-part='main'] {
|
|
1848
|
+
border-block-start: var(--_rule-width) solid var(--_rule);
|
|
1849
|
+
border-radius: 0;
|
|
1850
|
+
}
|
|
1851
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='floating'] > [data-part='progress'],
|
|
1852
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='inset'] > [data-part='progress'] {
|
|
1853
|
+
border-radius: 0;
|
|
1854
|
+
margin: 0;
|
|
1855
|
+
}
|
|
1856
|
+
[data-ui='app-shell']:not([data-nav-mode])[data-canvas='panel'] > [data-part='progress'] {
|
|
1857
|
+
border-radius: 0;
|
|
1858
|
+
}
|
|
1859
|
+
[data-ui='app-shell']:not([data-aside-mode='overlay']):is(
|
|
1860
|
+
[data-canvas='floating'],
|
|
1861
|
+
[data-canvas='inset']
|
|
1862
|
+
)
|
|
1863
|
+
> [data-part='aside'] {
|
|
1864
|
+
margin-inline: var(--_gutter);
|
|
1865
|
+
}
|
|
1866
|
+
/* A row under the content takes at most half the shell, so the content
|
|
1867
|
+
keeps the other half: a pane whose record is taller than the screen
|
|
1868
|
+
left the content a line high, with the pane scrolling and the content
|
|
1869
|
+
gone. Only where the shell owns its height; under `document` the page
|
|
1870
|
+
scrolls and the row takes its content's height. */
|
|
1871
|
+
[data-ui='app-shell'][data-scroll='content']:not([data-aside-mode='overlay'])
|
|
1872
|
+
> [data-part='aside'] {
|
|
1873
|
+
max-block-size: 50cqb;
|
|
1874
|
+
}
|
|
1875
|
+
/* The pane as a row under the content, in the inline mode alone: a pane
|
|
1876
|
+
whose mode is not yet decided draws nothing at a narrow width, the safe
|
|
1877
|
+
half of the answer, for the reason the navigation draws nothing. */
|
|
1878
|
+
[data-ui='app-shell']:not([data-aside-mode]) > [data-part='aside'] {
|
|
1879
|
+
display: none;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
/* ── The veil ────────────────────────────────────────────────────────── */
|
|
1884
|
+
|
|
1885
|
+
/** It stops where the panel stops, so the bar above stays a fixed point. */
|
|
1886
|
+
[data-ui='app-shell'] > [data-part='scrim'] {
|
|
1887
|
+
background-color: var(--_scrim);
|
|
1888
|
+
grid-area: 2 / 1 / -1 / -1;
|
|
1889
|
+
inset: 0;
|
|
1890
|
+
opacity: 1;
|
|
1891
|
+
position: absolute;
|
|
1892
|
+
transition: opacity var(--_duration) var(--_easing);
|
|
1893
|
+
z-index: calc(var(--slotted-z-index-overlay, 1000) - 1);
|
|
1894
|
+
}
|
|
1895
|
+
[data-ui='app-shell'] > [data-part='scrim'][data-status='entering'],
|
|
1896
|
+
[data-ui='app-shell'] > [data-part='scrim'][data-status='exiting'] {
|
|
1897
|
+
opacity: 0;
|
|
1898
|
+
}
|
|
1899
|
+
}
|