@slotted/styles 0.0.0-stage → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,1181 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* The shell of a screen's top: a title area that never leaves, a header
|
|
4
|
+
* area that scrolls away under it and comes back, a pill of controls on the
|
|
5
|
+
* chrome's edge, and a slot for tabs. SAP Fiori's Dynamic Page, rebuilt on
|
|
6
|
+
* one sticky root.
|
|
7
|
+
*
|
|
8
|
+
* ┌──────────────────────────────────────────────┐
|
|
9
|
+
* │ title-area (sticky at 0 inside the root) │
|
|
10
|
+
* │ nav ▸ back · breadcrumbs │
|
|
11
|
+
* │ title-row ▸ visual · title · actions · nav │
|
|
12
|
+
* ├──────────────────────────────────────────────┤
|
|
13
|
+
* │ header-area (scrolls under the title area) │
|
|
14
|
+
* │ figure · content ▸ description … │
|
|
15
|
+
* ├─────────────────── controls ─────────────────┤ ← zero-height row, the rule, the pill
|
|
16
|
+
* │ tabs │
|
|
17
|
+
* └──────────────────────────────────────────────┘
|
|
18
|
+
*
|
|
19
|
+
* ── How it collapses ─────────────────────────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* The root is `position: sticky` with a negative offset the size of the
|
|
22
|
+
* header area, measured by the binding into `--slotted-page-header-collapse`.
|
|
23
|
+
* Scrolling, the root rises until its top is H pixels above the scrollport
|
|
24
|
+
* and stops there; the title area, sticky at 0 inside the root, stays at the
|
|
25
|
+
* top; the header area passes under it, hidden by the title area's opaque
|
|
26
|
+
* ground and higher stacking; the controls and the tabs, last in the root,
|
|
27
|
+
* are what remains under the title. Pinned, or revealed in place after a
|
|
28
|
+
* scroll, the offset is 0 and the whole root stays.
|
|
29
|
+
*
|
|
30
|
+
* Sirius reached the same picture with `display: contents` on the host, and
|
|
31
|
+
* lost the host's box with it. This root keeps a background, a shadow, a
|
|
32
|
+
* container query and the geometry a ref returns.
|
|
33
|
+
*
|
|
34
|
+
* Every part is reached from the part that owns it with a child combinator,
|
|
35
|
+
* because the content the consumer projects is other families — a
|
|
36
|
+
* description-list has a `description`, a card has `actions` and a `header`
|
|
37
|
+
* — and a descendant rule would restyle theirs.
|
|
38
|
+
*/
|
|
39
|
+
[data-ui='page-header'] {
|
|
40
|
+
--_surface: var(--slotted-page-header-background, var(--slotted-surface, Canvas));
|
|
41
|
+
/* What the root and the title area paint; `plain` takes it away at rest. */
|
|
42
|
+
--_ground: var(--_surface);
|
|
43
|
+
/* How far below the scroller's edge the chrome holds; `floating` sets it. */
|
|
44
|
+
--_sticky-inset: 0px;
|
|
45
|
+
--_rule: var(--slotted-page-header-border, var(--slotted-border-subtle, ButtonBorder));
|
|
46
|
+
--_text: var(--slotted-text, CanvasText);
|
|
47
|
+
--_muted: var(--slotted-text-muted, GrayText);
|
|
48
|
+
--_gap: var(--slotted-page-header-gap, var(--slotted-space-4, 0.5rem));
|
|
49
|
+
--_padding-block: var(--slotted-page-header-padding-block, var(--slotted-space-5, 0.75rem));
|
|
50
|
+
/**
|
|
51
|
+
* The title is set on the type scale, not the control scale: it is the
|
|
52
|
+
* largest line on the screen, and the control scale tops out at fourteen
|
|
53
|
+
* pixels. The snapped title takes the step below its expanded size, so a
|
|
54
|
+
* stuck bar is a bar and not a poster.
|
|
55
|
+
*/
|
|
56
|
+
--_heading-size: var(
|
|
57
|
+
--slotted-page-header-heading-size-md,
|
|
58
|
+
var(--slotted-font-size-2xl, 1.25rem)
|
|
59
|
+
);
|
|
60
|
+
--_heading-line-height: var(--slotted-line-height-2xl, 1.4);
|
|
61
|
+
--_snapped-heading-size: var(
|
|
62
|
+
--slotted-page-header-snapped-heading-size-md,
|
|
63
|
+
var(--slotted-font-size-lg, 1rem)
|
|
64
|
+
);
|
|
65
|
+
--_snapped-heading-line-height: var(--slotted-line-height-lg, 1.5);
|
|
66
|
+
--_subheading-size: var(--slotted-font-size-md, 0.875rem);
|
|
67
|
+
--_description-measure: var(--slotted-page-header-description-measure, 70ch);
|
|
68
|
+
/**
|
|
69
|
+
* The pill's buttons are the small control step, so the pill is one row
|
|
70
|
+
* of the density it sits in; the glyph is the small control glyph.
|
|
71
|
+
*/
|
|
72
|
+
--_control-size: var(
|
|
73
|
+
--slotted-page-header-control-size,
|
|
74
|
+
var(--slotted-control-height-sm, 30px)
|
|
75
|
+
);
|
|
76
|
+
--_icon-size: var(--slotted-control-icon-size-sm, 14px);
|
|
77
|
+
--_rule-size: var(--slotted-border-width-thin, 1px);
|
|
78
|
+
--_duration: var(--slotted-duration-medium);
|
|
79
|
+
--_easing: var(--slotted-easing-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
80
|
+
|
|
81
|
+
background-color: var(--_ground);
|
|
82
|
+
color: var(--_text);
|
|
83
|
+
container-name: page-header;
|
|
84
|
+
container-type: inline-size;
|
|
85
|
+
font-family: var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif);
|
|
86
|
+
inline-size: 100%;
|
|
87
|
+
inset-block-start: calc(var(--_sticky-inset) - var(--slotted-page-header-collapse, 0px));
|
|
88
|
+
min-inline-size: 0;
|
|
89
|
+
/**
|
|
90
|
+
* Scroll anchoring would move the scroll position to keep the content
|
|
91
|
+
* under the reader still when the header area changes height, which is
|
|
92
|
+
* exactly the jump Sirius absorbed with a timer of two hundred milliseconds after each reveal.
|
|
93
|
+
*/
|
|
94
|
+
overflow-anchor: none;
|
|
95
|
+
position: sticky;
|
|
96
|
+
/**
|
|
97
|
+
* The offset moves only when a reveal begins or ends, so the transition
|
|
98
|
+
* is the slide of the header area under the title and never a fight with
|
|
99
|
+
* the scroll, during which the offset is constant.
|
|
100
|
+
*/
|
|
101
|
+
transition: inset-block-start var(--_duration) var(--_easing);
|
|
102
|
+
/**
|
|
103
|
+
* Above every other sticky thing on the page — a section's header, a
|
|
104
|
+
* table's column header, a pinned corner — so what a short section pushes
|
|
105
|
+
* up at the end of the scroll goes under the chrome and never over it.
|
|
106
|
+
*/
|
|
107
|
+
z-index: calc(var(--slotted-z-index-sticky, 100) + 3);
|
|
108
|
+
/**
|
|
109
|
+
* The chrome's edge is one hairline, the whole width of the header, in
|
|
110
|
+
* every state: with tabs under it, with an area, with a title alone. It
|
|
111
|
+
* is the root's own border, not a rule inside the container, so it runs
|
|
112
|
+
* to the header's edges and not to the measure's. `overflow-x: clip` is
|
|
113
|
+
* for the rule the pill straddles, drawn wider than the container below;
|
|
114
|
+
* the block axis stays visible for the pill's lower half.
|
|
115
|
+
*/
|
|
116
|
+
border-block-end: var(--_rule-size) solid var(--_rule);
|
|
117
|
+
overflow-x: clip;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[data-ui='page-header'][data-size='sm'] {
|
|
121
|
+
--_heading-size: var(
|
|
122
|
+
--slotted-page-header-heading-size-sm,
|
|
123
|
+
var(--slotted-font-size-xl, 1.125rem)
|
|
124
|
+
);
|
|
125
|
+
--_heading-line-height: var(--slotted-line-height-xl, 1.45);
|
|
126
|
+
--_snapped-heading-size: var(
|
|
127
|
+
--slotted-page-header-snapped-heading-size-sm,
|
|
128
|
+
var(--slotted-font-size-md, 0.875rem)
|
|
129
|
+
);
|
|
130
|
+
--_snapped-heading-line-height: var(--slotted-line-height-md, 1.5);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
[data-ui='page-header'][data-size='lg'] {
|
|
134
|
+
--_heading-size: var(
|
|
135
|
+
--slotted-page-header-heading-size-lg,
|
|
136
|
+
var(--slotted-font-size-3xl, 1.5rem)
|
|
137
|
+
);
|
|
138
|
+
--_heading-line-height: var(--slotted-line-height-3xl, 1.3);
|
|
139
|
+
--_snapped-heading-size: var(
|
|
140
|
+
--slotted-page-header-snapped-heading-size-lg,
|
|
141
|
+
var(--slotted-font-size-xl, 1.125rem)
|
|
142
|
+
);
|
|
143
|
+
--_snapped-heading-line-height: var(--slotted-line-height-xl, 1.45);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Pinned, revealed in place, or never collapsing by scroll: the whole root
|
|
148
|
+
* stays. The pin's own pressed state is read here rather than restated on
|
|
149
|
+
* the root, so the vocabulary gains no `pinned` — a pin is a toggle button
|
|
150
|
+
* and `pressed` is its word. Under `collapse="manual"` the offset is what
|
|
151
|
+
* the scroll would use, so it is zero for good.
|
|
152
|
+
*/
|
|
153
|
+
[data-ui='page-header'][data-revealed],
|
|
154
|
+
[data-ui='page-header'][data-collapse='manual'],
|
|
155
|
+
[data-ui='page-header']:has([data-part='pin'][data-pressed]) {
|
|
156
|
+
inset-block-start: var(--_sticky-inset);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* ── Variant ─────────────────────────────────────────────────────────── */
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* `band`, the default, is the chrome as a strip of the surface with one
|
|
163
|
+
* hairline under it. `plain` paints no ground of its own at rest and takes
|
|
164
|
+
* the one it stands on; once content passes under the chrome — the binding
|
|
165
|
+
* writes `data-stuck` — the surface comes back, because a header with no
|
|
166
|
+
* ground would show the page through its title. The hairline stays in
|
|
167
|
+
* both: it is where the chrome ends.
|
|
168
|
+
*/
|
|
169
|
+
[data-ui='page-header'][data-variant='plain']:not([data-stuck]) {
|
|
170
|
+
--_ground: transparent;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* `floating` is a box of its own for a header that stands inside padding:
|
|
175
|
+
* the hairline goes all the way round, the corners are a card's step,
|
|
176
|
+
* and stuck it slides to an inset below the scroller's edge with a shadow,
|
|
177
|
+
* as a thing lying on the content and not a strip across it. The inset is
|
|
178
|
+
* the stuck state's alone: a header flush with its scroller's edge at rest
|
|
179
|
+
* sat an inset down over the content under it. The title area paints its
|
|
180
|
+
* own ground, so its top corners follow the root's, and it slides with it.
|
|
181
|
+
*/
|
|
182
|
+
[data-ui='page-header'][data-variant='floating'] {
|
|
183
|
+
--_radius: var(--slotted-page-header-radius, var(--slotted-radius-lg, 8px));
|
|
184
|
+
|
|
185
|
+
border: var(--_rule-size) solid var(--_rule);
|
|
186
|
+
border-radius: var(--_radius);
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
}
|
|
189
|
+
[data-ui='page-header'][data-variant='floating'] > * > [data-part='title-area'] {
|
|
190
|
+
border-start-end-radius: calc(var(--_radius) - var(--_rule-size));
|
|
191
|
+
border-start-start-radius: calc(var(--_radius) - var(--_rule-size));
|
|
192
|
+
transition: inset-block-start var(--_duration) var(--_easing);
|
|
193
|
+
}
|
|
194
|
+
[data-ui='page-header'][data-variant='floating'][data-stuck] {
|
|
195
|
+
--_sticky-inset: var(--slotted-page-header-inset, var(--slotted-space-4, 0.5rem));
|
|
196
|
+
|
|
197
|
+
box-shadow: var(--slotted-page-header-shadow, var(--slotted-shadow-sm));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Compact, below 600 pixels: nothing sticks. The root and the title area
|
|
202
|
+
* are in the flow and scroll away with the page, because on a phone a
|
|
203
|
+
* record's header and tabs held 40 per cent of the screen for the whole
|
|
204
|
+
* scroll. `relative` and not `static`, so the root keeps the stacking the
|
|
205
|
+
* pill straddling its edge relies on, at an offset of nought. The binding
|
|
206
|
+
* writes the attribute from the root's own width: a container query cannot
|
|
207
|
+
* select the container it measures.
|
|
208
|
+
*/
|
|
209
|
+
[data-ui='page-header'][data-compact] {
|
|
210
|
+
inset-block-start: 0;
|
|
211
|
+
position: relative;
|
|
212
|
+
}
|
|
213
|
+
[data-ui='page-header'][data-compact] > * > [data-part='title-area'] {
|
|
214
|
+
inset-block-start: 0;
|
|
215
|
+
position: relative;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ── The title area ──────────────────────────────────────────────────── */
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The one hop through the container: `> *` is the container the family
|
|
222
|
+
* renders to align its rows to the page's measure, and it is not named,
|
|
223
|
+
* because a family does not select another family's identity.
|
|
224
|
+
*/
|
|
225
|
+
[data-ui='page-header'] > * > [data-part='title-area'] {
|
|
226
|
+
background-color: var(--_ground);
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
gap: var(--slotted-space-3, 0.375rem);
|
|
230
|
+
inset-block-start: var(--_sticky-inset);
|
|
231
|
+
overflow-anchor: none;
|
|
232
|
+
padding-block: var(--_padding-block) var(--slotted-space-4, 0.5rem);
|
|
233
|
+
position: sticky;
|
|
234
|
+
z-index: 2;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The pill straddles the chrome's edge, so whatever ends at that edge
|
|
239
|
+
* keeps half a pill clear of it: the title area, because stuck the edge
|
|
240
|
+
* is its own, and the header area's columns, because at rest it is theirs.
|
|
241
|
+
* The title area reserves it in every state rather than only when stuck,
|
|
242
|
+
* because a padding that changed at the snap would move the tabs by that
|
|
243
|
+
* much at the moment the header area left.
|
|
244
|
+
*/
|
|
245
|
+
[data-ui='page-header']:has([data-part='controls'] > *) > * > [data-part='title-area'] {
|
|
246
|
+
padding-block-end: calc(var(--slotted-space-4, 0.5rem) + var(--_control-size) / 2);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
[data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav'] {
|
|
250
|
+
align-items: center;
|
|
251
|
+
color: var(--_muted);
|
|
252
|
+
display: flex;
|
|
253
|
+
font-size: var(--slotted-font-size-sm, 0.8125rem);
|
|
254
|
+
gap: var(--slotted-space-5, 0.75rem);
|
|
255
|
+
line-height: var(--slotted-line-height-sm, 1.45);
|
|
256
|
+
min-inline-size: 0;
|
|
257
|
+
}
|
|
258
|
+
[data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav']:empty {
|
|
259
|
+
display: none;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* One way back at a time. Beside breadcrumbs the arrow says what their
|
|
263
|
+
* first crumb already says, so it yields to them; below 600 pixels the
|
|
264
|
+
* breadcrumbs go and the arrow is what remains. A consumer writes both
|
|
265
|
+
* once and the width decides.
|
|
266
|
+
*/
|
|
267
|
+
[data-ui='page-header']
|
|
268
|
+
> *
|
|
269
|
+
> [data-part='title-area']
|
|
270
|
+
> [data-part='nav']
|
|
271
|
+
> [data-part='back']:has(~ [data-part='breadcrumbs']:not(:empty)) {
|
|
272
|
+
display: none;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The way back. A link or a button the consumer renders; the arrow is the
|
|
277
|
+
* family's, the `arrow-left` Glyph element the binding puts before the
|
|
278
|
+
* label, and it points along the reading direction, so its `directional`
|
|
279
|
+
* box mirrors it in a right-to-left document.
|
|
280
|
+
*/
|
|
281
|
+
[data-ui='page-header'] > * > [data-part='title-area'] > [data-part='nav'] > [data-part='back'] {
|
|
282
|
+
--slotted-icon-size: var(--_icon-size);
|
|
283
|
+
|
|
284
|
+
align-items: center;
|
|
285
|
+
background: none;
|
|
286
|
+
border: 0;
|
|
287
|
+
border-radius: var(--slotted-control-radius, var(--slotted-radius-sm, 4px));
|
|
288
|
+
color: var(--_muted);
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
display: inline-flex;
|
|
291
|
+
flex: none;
|
|
292
|
+
font: inherit;
|
|
293
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
294
|
+
gap: var(--slotted-control-gap-sm, 6px);
|
|
295
|
+
margin: 0;
|
|
296
|
+
min-block-size: var(--slotted-target-min, 24px);
|
|
297
|
+
padding: 0;
|
|
298
|
+
text-decoration: none;
|
|
299
|
+
transition: color var(--slotted-duration-short) var(--_easing);
|
|
300
|
+
}
|
|
301
|
+
/* On a finger the way back is a full target among 44-pixel controls: it
|
|
302
|
+
reaches the comfortable size by padding and gives it back as a negative
|
|
303
|
+
margin, so the nav row keeps its height and nothing under it moves. */
|
|
304
|
+
@media (pointer: coarse) {
|
|
305
|
+
[data-ui='page-header']
|
|
306
|
+
> *
|
|
307
|
+
> [data-part='title-area']
|
|
308
|
+
> [data-part='nav']
|
|
309
|
+
> [data-part='back'] {
|
|
310
|
+
--_reach: max(
|
|
311
|
+
0px,
|
|
312
|
+
calc((var(--slotted-target-comfortable, 44px) - var(--slotted-target-min, 24px)) / 2)
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
margin-block: calc(var(--_reach) * -1);
|
|
316
|
+
padding-block: var(--_reach);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
[data-ui='page-header']
|
|
320
|
+
> *
|
|
321
|
+
> [data-part='title-area']
|
|
322
|
+
> [data-part='nav']
|
|
323
|
+
> [data-part='back']:not([data-disabled]):not(:disabled):hover {
|
|
324
|
+
color: var(--_text);
|
|
325
|
+
}
|
|
326
|
+
[data-ui='page-header']
|
|
327
|
+
> *
|
|
328
|
+
> [data-part='title-area']
|
|
329
|
+
> [data-part='nav']
|
|
330
|
+
> [data-part='back']:focus-visible {
|
|
331
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
332
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
[data-ui='page-header']
|
|
336
|
+
> *
|
|
337
|
+
> [data-part='title-area']
|
|
338
|
+
> [data-part='nav']
|
|
339
|
+
> [data-part='breadcrumbs'] {
|
|
340
|
+
min-inline-size: 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* The row: the visual, the title column, and the actions and the record
|
|
345
|
+
* navigation pushed to the end. Everything after the title wraps under it
|
|
346
|
+
* when the width runs out, in the order it has in the DOM.
|
|
347
|
+
*/
|
|
348
|
+
[data-ui='page-header'] > * > [data-part='title-area'] > [data-part='title-row'] {
|
|
349
|
+
align-items: flex-start;
|
|
350
|
+
display: flex;
|
|
351
|
+
flex-wrap: wrap;
|
|
352
|
+
gap: var(--_gap) var(--slotted-space-6, 1rem);
|
|
353
|
+
min-inline-size: 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
[data-ui='page-header']
|
|
357
|
+
> *
|
|
358
|
+
> [data-part='title-area']
|
|
359
|
+
> [data-part='title-row']
|
|
360
|
+
> [data-part='visual'] {
|
|
361
|
+
align-items: center;
|
|
362
|
+
/* On the title's first line, as Primer's LeadingVisual: centred on the
|
|
363
|
+
whole column it sank beside the second line of a wrapped title. */
|
|
364
|
+
align-self: flex-start;
|
|
365
|
+
display: inline-flex;
|
|
366
|
+
flex: none;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The title column is a wrapping row so the marks sit on the heading's own
|
|
371
|
+
* line, on its baseline — which is what "a mark beside a line of text is
|
|
372
|
+
* aligned to the text" asks for — and the subheading, forced to a full
|
|
373
|
+
* row, takes the line under it.
|
|
374
|
+
*/
|
|
375
|
+
[data-ui='page-header']
|
|
376
|
+
> *
|
|
377
|
+
> [data-part='title-area']
|
|
378
|
+
> [data-part='title-row']
|
|
379
|
+
> [data-part='title'] {
|
|
380
|
+
align-items: center;
|
|
381
|
+
column-gap: var(--slotted-space-4, 0.5rem);
|
|
382
|
+
display: flex;
|
|
383
|
+
/**
|
|
384
|
+
* Twelve rems and not twenty: the basis is what decides whether the
|
|
385
|
+
* visual keeps the title beside it at 420 pixels, and the column grows to
|
|
386
|
+
* the line anyway.
|
|
387
|
+
*/
|
|
388
|
+
flex: 1 1 20rem;
|
|
389
|
+
flex-wrap: wrap;
|
|
390
|
+
/* At least a control tall, so a one-line heading stands on the axis of
|
|
391
|
+
the button beside it — sixteen pixels off it on a touch screen, where
|
|
392
|
+
the row aligns to the start for the sake of a heading that wraps. */
|
|
393
|
+
min-block-size: var(--slotted-control-height-md, 34px);
|
|
394
|
+
min-inline-size: 0;
|
|
395
|
+
row-gap: var(--slotted-space-1, 0.125rem);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Expanded, the heading wraps to two lines and then clamps; the binding
|
|
400
|
+
* repeats the full text in `title`, so nothing leaves the DOM. Snapped it is
|
|
401
|
+
* one line, a step smaller, and the clamp is the ellipsis a bar can afford
|
|
402
|
+
* — drawn as a block with `nowrap`, because a `-webkit-box` reports a
|
|
403
|
+
* width to the flex algorithm that is not the width it then paints, and the
|
|
404
|
+
* marks beside it ran past the container in the first capture.
|
|
405
|
+
*/
|
|
406
|
+
[data-ui='page-header']
|
|
407
|
+
> *
|
|
408
|
+
> [data-part='title-area']
|
|
409
|
+
> [data-part='title-row']
|
|
410
|
+
> [data-part='title']
|
|
411
|
+
> [data-part='heading'] {
|
|
412
|
+
-webkit-box-orient: vertical;
|
|
413
|
+
color: var(--_text);
|
|
414
|
+
display: -webkit-box;
|
|
415
|
+
font-family: var(
|
|
416
|
+
--slotted-font-family-heading,
|
|
417
|
+
var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif)
|
|
418
|
+
);
|
|
419
|
+
font-size: var(--_heading-size);
|
|
420
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
421
|
+
letter-spacing: -0.01em;
|
|
422
|
+
-webkit-line-clamp: 2;
|
|
423
|
+
line-height: var(--_heading-line-height);
|
|
424
|
+
margin: 0;
|
|
425
|
+
min-inline-size: 0;
|
|
426
|
+
overflow: hidden;
|
|
427
|
+
overflow-wrap: anywhere;
|
|
428
|
+
text-wrap: pretty;
|
|
429
|
+
/* No transition on the size: text re-rasterised on every frame of a
|
|
430
|
+
font-size change blurs and settles with a jolt, so the step is a cut. */
|
|
431
|
+
}
|
|
432
|
+
[data-ui='page-header']:not([data-expanded])
|
|
433
|
+
> *
|
|
434
|
+
> [data-part='title-area']
|
|
435
|
+
> [data-part='title-row']
|
|
436
|
+
> [data-part='title']
|
|
437
|
+
> [data-part='heading'] {
|
|
438
|
+
display: block;
|
|
439
|
+
flex: 0 1 auto;
|
|
440
|
+
font-size: var(--_snapped-heading-size);
|
|
441
|
+
line-height: var(--_snapped-heading-line-height);
|
|
442
|
+
text-overflow: ellipsis;
|
|
443
|
+
white-space: nowrap;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
[data-ui='page-header']
|
|
447
|
+
> *
|
|
448
|
+
> [data-part='title-area']
|
|
449
|
+
> [data-part='title-row']
|
|
450
|
+
> [data-part='title']
|
|
451
|
+
> [data-part='marks'] {
|
|
452
|
+
align-items: center;
|
|
453
|
+
display: inline-flex;
|
|
454
|
+
flex-wrap: wrap;
|
|
455
|
+
gap: var(--slotted-space-2, 0.25rem);
|
|
456
|
+
min-inline-size: 0;
|
|
457
|
+
}
|
|
458
|
+
[data-ui='page-header']
|
|
459
|
+
> *
|
|
460
|
+
> [data-part='title-area']
|
|
461
|
+
> [data-part='title-row']
|
|
462
|
+
> [data-part='title']
|
|
463
|
+
> [data-part='marks']:empty {
|
|
464
|
+
display: none;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
[data-ui='page-header']
|
|
468
|
+
> *
|
|
469
|
+
> [data-part='title-area']
|
|
470
|
+
> [data-part='title-row']
|
|
471
|
+
> [data-part='title']
|
|
472
|
+
> [data-part='subheading'] {
|
|
473
|
+
-webkit-box-orient: vertical;
|
|
474
|
+
color: var(--_muted);
|
|
475
|
+
display: -webkit-box;
|
|
476
|
+
flex-basis: 100%;
|
|
477
|
+
font-size: var(--_subheading-size);
|
|
478
|
+
/* Two lines, and the whole string in `title`: a header is a fixed cost
|
|
479
|
+
on every screen, so nothing in it grows without a bound. */
|
|
480
|
+
-webkit-line-clamp: 2;
|
|
481
|
+
line-height: var(--slotted-line-height-md, 1.5);
|
|
482
|
+
margin: 0;
|
|
483
|
+
min-inline-size: 0;
|
|
484
|
+
overflow: hidden;
|
|
485
|
+
text-wrap: pretty;
|
|
486
|
+
}
|
|
487
|
+
/* Snapped, the bar has one line to give it. */
|
|
488
|
+
[data-ui='page-header']:not([data-expanded])
|
|
489
|
+
> *
|
|
490
|
+
> [data-part='title-area']
|
|
491
|
+
> [data-part='title-row']
|
|
492
|
+
> [data-part='title']
|
|
493
|
+
> [data-part='subheading'] {
|
|
494
|
+
display: block;
|
|
495
|
+
overflow: hidden;
|
|
496
|
+
text-overflow: ellipsis;
|
|
497
|
+
white-space: nowrap;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
[data-ui='page-header']
|
|
501
|
+
> *
|
|
502
|
+
> [data-part='title-area']
|
|
503
|
+
> [data-part='title-row']
|
|
504
|
+
> [data-part='actions'] {
|
|
505
|
+
align-items: center;
|
|
506
|
+
display: flex;
|
|
507
|
+
flex: 0 1 auto;
|
|
508
|
+
/* Loose buttons wrap rather than breaking their own words; a toolbar in
|
|
509
|
+
here folds what does not fit into its menu before that happens. */
|
|
510
|
+
flex-wrap: wrap;
|
|
511
|
+
gap: var(--slotted-control-gap-md, 8px);
|
|
512
|
+
min-inline-size: 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* A toolbar pads its controls for the reach of their focus rings, which
|
|
517
|
+
* made it a control and two insets tall: beside a title that reserves one
|
|
518
|
+
* control, a record whose actions land with its answer grew its title row,
|
|
519
|
+
* and everything under it moved. The row gives the inset back, so the bar
|
|
520
|
+
* stands a control tall and its rings still draw inside its own padding.
|
|
521
|
+
* The pad is the toolbar's own — its inset, or the ring's reach for a bar
|
|
522
|
+
* that scrolls — written from the same public tokens; a framed bar keeps
|
|
523
|
+
* its box.
|
|
524
|
+
*/
|
|
525
|
+
[data-ui='page-header']
|
|
526
|
+
> *
|
|
527
|
+
> [data-part='title-area']
|
|
528
|
+
> [data-part='title-row']
|
|
529
|
+
> [data-part='actions']
|
|
530
|
+
> [data-ui='toolbar']:not([data-variant='outline']) {
|
|
531
|
+
margin-block: calc(-1 * var(--slotted-toolbar-inset, var(--slotted-space-2, 0.25rem)));
|
|
532
|
+
}
|
|
533
|
+
[data-ui='page-header']
|
|
534
|
+
> *
|
|
535
|
+
> [data-part='title-area']
|
|
536
|
+
> [data-part='title-row']
|
|
537
|
+
> [data-part='actions']
|
|
538
|
+
> [data-ui='toolbar']:not([data-variant='outline']):is(
|
|
539
|
+
[data-overflow='menu'],
|
|
540
|
+
[data-overflow='scroll']
|
|
541
|
+
) {
|
|
542
|
+
margin-block: calc(
|
|
543
|
+
-1 *
|
|
544
|
+
var(
|
|
545
|
+
--slotted-toolbar-inset,
|
|
546
|
+
max(
|
|
547
|
+
var(--slotted-space-2, 0.25rem),
|
|
548
|
+
calc(var(--slotted-focus-ring-offset, 2px) + var(--slotted-focus-ring-width, 2px))
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* A toolbar that folds into its menu keeps the actions on the title row at
|
|
556
|
+
* every width: the row stops wrapping, the title keeps its own width, and
|
|
557
|
+
* the cluster takes everything the title leaves, so the toolbar measures
|
|
558
|
+
* that room and folds the rest. Fiori's Dynamic Page. Loose actions have no
|
|
559
|
+
* menu to fold into and wrap.
|
|
560
|
+
*
|
|
561
|
+
* The cluster's width is the row's remainder and never its own content. A
|
|
562
|
+
* cluster sized by its content was the trigger alone once everything had
|
|
563
|
+
* folded, and it did not grow when the row did: the toolbar kept its width,
|
|
564
|
+
* its observer never fired, and the actions stayed folded after the window
|
|
565
|
+
* widened again, while a fresh load at the same width showed them all.
|
|
566
|
+
*/
|
|
567
|
+
[data-ui='page-header']
|
|
568
|
+
> *
|
|
569
|
+
> [data-part='title-area']
|
|
570
|
+
> [data-part='title-row']:has(
|
|
571
|
+
> [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
|
|
572
|
+
) {
|
|
573
|
+
flex-wrap: nowrap;
|
|
574
|
+
}
|
|
575
|
+
/* The title starts from its own width, not from the basis a wrapping row gives it, at every
|
|
576
|
+
width, and does not grow past it: the room it does not use is the cluster's. It shrinks only
|
|
577
|
+
once the cluster is down to its menu's trigger, because nothing else in the row can.
|
|
578
|
+
Its own width is its widest line. A wrapping flex row measures its items end to end, as if on
|
|
579
|
+
one line: a heading, its marks and the subheading under them came to 769 pixels for two lines
|
|
580
|
+
of 420, and the toolbar beside them folded every command into its menu next to empty room. As
|
|
581
|
+
a grid — the heading and its marks on the first line, the subheading across both columns
|
|
582
|
+
under them — the title is as wide as its widest line. */
|
|
583
|
+
[data-ui='page-header']
|
|
584
|
+
> *
|
|
585
|
+
> [data-part='title-area']
|
|
586
|
+
> [data-part='title-row']:has(
|
|
587
|
+
> [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
|
|
588
|
+
)
|
|
589
|
+
> [data-part='title'] {
|
|
590
|
+
align-items: center;
|
|
591
|
+
display: grid;
|
|
592
|
+
flex: 0 1 auto;
|
|
593
|
+
grid-template-columns: minmax(0, auto) auto;
|
|
594
|
+
justify-content: start;
|
|
595
|
+
}
|
|
596
|
+
[data-ui='page-header']
|
|
597
|
+
> *
|
|
598
|
+
> [data-part='title-area']
|
|
599
|
+
> [data-part='title-row']:has(
|
|
600
|
+
> [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
|
|
601
|
+
)
|
|
602
|
+
> [data-part='title']
|
|
603
|
+
> [data-part='heading'] {
|
|
604
|
+
grid-column: 1;
|
|
605
|
+
}
|
|
606
|
+
[data-ui='page-header']
|
|
607
|
+
> *
|
|
608
|
+
> [data-part='title-area']
|
|
609
|
+
> [data-part='title-row']:has(
|
|
610
|
+
> [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
|
|
611
|
+
)
|
|
612
|
+
> [data-part='title']
|
|
613
|
+
> [data-part='subheading'] {
|
|
614
|
+
grid-column: 1 / -1;
|
|
615
|
+
}
|
|
616
|
+
/* The cluster starts from nothing and grows into what the title leaves, so its width follows
|
|
617
|
+
the row's in both directions. It never gives way below its menu's trigger, a medium control,
|
|
618
|
+
and the toolbar's inset on either side: under it the trigger stood past the screen's edge on
|
|
619
|
+
a phone. */
|
|
620
|
+
[data-ui='page-header']
|
|
621
|
+
> *
|
|
622
|
+
> [data-part='title-area']
|
|
623
|
+
> [data-part='title-row']
|
|
624
|
+
> [data-part='actions']:has(> [data-ui='toolbar'][data-overflow='menu']) {
|
|
625
|
+
flex: 1 1 0%;
|
|
626
|
+
min-inline-size: calc(
|
|
627
|
+
var(--slotted-control-height-md, 34px) + 2 *
|
|
628
|
+
var(
|
|
629
|
+
--slotted-toolbar-inset,
|
|
630
|
+
max(
|
|
631
|
+
var(--slotted-space-2, 0.25rem),
|
|
632
|
+
calc(var(--slotted-focus-ring-offset, 2px) + var(--slotted-focus-ring-width, 2px))
|
|
633
|
+
)
|
|
634
|
+
)
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
/* The toolbar fills the cluster, so the room it measures is the cluster's, and its controls
|
|
638
|
+
stand at the end of the row as they did when the cluster was their own width. `safe`, because
|
|
639
|
+
a bar of pinned controls wider than the room scrolls, and overflow at the start of a scroll
|
|
640
|
+
container cannot be scrolled to. */
|
|
641
|
+
[data-ui='page-header']
|
|
642
|
+
> *
|
|
643
|
+
> [data-part='title-area']
|
|
644
|
+
> [data-part='title-row']
|
|
645
|
+
> [data-part='actions']
|
|
646
|
+
> [data-ui='toolbar'][data-overflow='menu'] {
|
|
647
|
+
flex: 1 1 0%;
|
|
648
|
+
justify-content: safe flex-end;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Previous and next record, after the actions and set apart from them by a
|
|
653
|
+
* rule, which is where sap.f puts navigationActions.
|
|
654
|
+
*/
|
|
655
|
+
[data-ui='page-header']
|
|
656
|
+
> *
|
|
657
|
+
> [data-part='title-area']
|
|
658
|
+
> [data-part='title-row']
|
|
659
|
+
> [data-part='navigation'] {
|
|
660
|
+
align-items: center;
|
|
661
|
+
border-inline-start: var(--_rule-size) solid var(--_rule);
|
|
662
|
+
display: flex;
|
|
663
|
+
flex: none;
|
|
664
|
+
gap: var(--slotted-control-gap-sm, 6px);
|
|
665
|
+
/* The far end of whichever row it lands on: alone on a wrapped row it
|
|
666
|
+
stayed at the start, under the title, in the dense capture. */
|
|
667
|
+
margin-inline-start: auto;
|
|
668
|
+
/* The toolbar beside it pads its controls by the same step; without it
|
|
669
|
+
the arrows sat four pixels above the actions' line. */
|
|
670
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
671
|
+
padding-inline-start: var(--slotted-space-4, 0.5rem);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Previous and next are directional: in a right-to-left document the
|
|
675
|
+
* previous record is to the right, and the consumer's chevrons are drawn
|
|
676
|
+
* for the other direction. The glyphs mirror; the order is already the
|
|
677
|
+
* row's.
|
|
678
|
+
*/
|
|
679
|
+
[data-ui='page-header']
|
|
680
|
+
> *
|
|
681
|
+
> [data-part='title-area']
|
|
682
|
+
> [data-part='title-row']
|
|
683
|
+
> [data-part='navigation']:dir(rtl)
|
|
684
|
+
:is(svg, [data-ui='icon']) {
|
|
685
|
+
scale: -1 1;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* ── The header area ─────────────────────────────────────────────────── */
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* A grid with one row, so that collapsing by the control is a track going
|
|
692
|
+
* to 0fr — the content's own height at every frame, nothing measured — as
|
|
693
|
+
* collapsible does. Figure and content share the row.
|
|
694
|
+
*/
|
|
695
|
+
[data-ui='page-header'] > * > [data-part='header-area'] {
|
|
696
|
+
display: grid;
|
|
697
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
698
|
+
grid-template-rows: 1fr;
|
|
699
|
+
min-inline-size: 0;
|
|
700
|
+
overflow-anchor: none;
|
|
701
|
+
transition:
|
|
702
|
+
grid-template-rows var(--_duration) var(--_easing),
|
|
703
|
+
opacity var(--_duration) var(--_easing);
|
|
704
|
+
}
|
|
705
|
+
[data-ui='page-header'] > * > [data-part='header-area']:not(:has([data-part='figure'])) {
|
|
706
|
+
grid-template-columns: minmax(0, 1fr);
|
|
707
|
+
}
|
|
708
|
+
[data-ui='page-header'] > * > [data-part='header-area'] > * {
|
|
709
|
+
min-block-size: 0;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Collapsed by the control: expanded is false and the scroll has not
|
|
714
|
+
* passed the area. Scrolled past, expanded is false too, and the sticky
|
|
715
|
+
* offset is what hides the area — a 0fr row there would shorten the root
|
|
716
|
+
* under the reader and jump the scroll by the area's height.
|
|
717
|
+
*/
|
|
718
|
+
[data-ui='page-header']:not([data-expanded]):not([data-scrolled])
|
|
719
|
+
> *
|
|
720
|
+
> [data-part='header-area'] {
|
|
721
|
+
grid-template-rows: 0fr;
|
|
722
|
+
opacity: 0;
|
|
723
|
+
}
|
|
724
|
+
[data-ui='page-header']:not([data-expanded]):not([data-scrolled])
|
|
725
|
+
> *
|
|
726
|
+
> [data-part='header-area']
|
|
727
|
+
> * {
|
|
728
|
+
overflow: clip;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
[data-ui='page-header'] > * > [data-part='header-area'] > [data-part='figure'] {
|
|
732
|
+
display: flex;
|
|
733
|
+
padding-block-end: var(--_padding-block);
|
|
734
|
+
padding-inline-end: var(--slotted-space-6, 1rem);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
[data-ui='page-header'] > * > [data-part='header-area'] > [data-part='content'] {
|
|
738
|
+
display: flex;
|
|
739
|
+
flex-direction: column;
|
|
740
|
+
gap: var(--_gap);
|
|
741
|
+
min-inline-size: 0;
|
|
742
|
+
padding-block-end: var(--_padding-block);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
[data-ui='page-header']
|
|
746
|
+
> *
|
|
747
|
+
> [data-part='header-area']
|
|
748
|
+
> [data-part='content']
|
|
749
|
+
> [data-part='description'] {
|
|
750
|
+
-webkit-box-orient: vertical;
|
|
751
|
+
color: var(--_muted);
|
|
752
|
+
display: -webkit-box;
|
|
753
|
+
font-size: var(--slotted-font-size-md, 0.875rem);
|
|
754
|
+
-webkit-line-clamp: 3;
|
|
755
|
+
line-height: var(--slotted-line-height-md, 1.5);
|
|
756
|
+
margin: 0;
|
|
757
|
+
max-inline-size: var(--_description-measure);
|
|
758
|
+
overflow: hidden;
|
|
759
|
+
text-wrap: pretty;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* The half pill the header area's columns keep clear of the edge; see the
|
|
763
|
+
title area's reservation above. After the columns' own rules, so it wins. */
|
|
764
|
+
[data-ui='page-header']:has([data-part='controls'] > *)
|
|
765
|
+
> *
|
|
766
|
+
> [data-part='header-area']
|
|
767
|
+
> [data-part='figure'],
|
|
768
|
+
[data-ui='page-header']:has([data-part='controls'] > *)
|
|
769
|
+
> *
|
|
770
|
+
> [data-part='header-area']
|
|
771
|
+
> [data-part='content'] {
|
|
772
|
+
padding-block-end: calc(var(--_padding-block) + var(--_control-size) / 2);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Collapsed by the control the track is 0fr, and the half pill the columns
|
|
777
|
+
* reserved would keep the area 27 pixels tall: the reservation goes with
|
|
778
|
+
* the content, and the pill straddles the title area's own reservation.
|
|
779
|
+
*/
|
|
780
|
+
[data-ui='page-header']:not([data-expanded]):not([data-scrolled])
|
|
781
|
+
> *
|
|
782
|
+
> [data-part='header-area']
|
|
783
|
+
> * {
|
|
784
|
+
padding-block: 0;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/* ── The controls, on the chrome's edge ──────────────────────────────── */
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* A zero-height row that carries the rule under the chrome and the pill
|
|
791
|
+
* that straddles it. It sits after the header area and before the tabs, so
|
|
792
|
+
* it is on the chrome's bottom edge in every state without moving in the
|
|
793
|
+
* DOM: at rest under the header area, stuck under the title area, because
|
|
794
|
+
* the header area is then under the title. Sirius moved the pill between
|
|
795
|
+
* two slots with `ngTemplateOutlet`.
|
|
796
|
+
*
|
|
797
|
+
* The row is `relative` and the pill `absolute`, so the tabs under it lay
|
|
798
|
+
* out as if it were not there; the pill overlaps their top edge.
|
|
799
|
+
*/
|
|
800
|
+
[data-ui='page-header'] > * > [data-part='controls'] {
|
|
801
|
+
block-size: 0;
|
|
802
|
+
position: relative;
|
|
803
|
+
/* Above the title area, and above the wait drawn on the same edge. */
|
|
804
|
+
z-index: 4;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* With tabs under the chrome the edge the pill straddles is above them, and
|
|
808
|
+
* it is drawn the whole width of the header — wider than the container and
|
|
809
|
+
* clipped by the root — so it is the same line as the root's own edge.
|
|
810
|
+
* Without tabs the root's edge is where the pill sits, and no second rule
|
|
811
|
+
* is drawn.
|
|
812
|
+
*/
|
|
813
|
+
[data-ui='page-header']:has(> * > [data-part='tabs']:not(:empty))
|
|
814
|
+
> *
|
|
815
|
+
> [data-part='controls']::before {
|
|
816
|
+
border-block-start: var(--_rule-size) solid var(--_rule);
|
|
817
|
+
content: '';
|
|
818
|
+
inset-block-start: 0;
|
|
819
|
+
inset-inline: -50vw;
|
|
820
|
+
position: absolute;
|
|
821
|
+
}
|
|
822
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * {
|
|
823
|
+
/* The pill is a control on the hairline: it keeps the surface in every variant. */
|
|
824
|
+
background-color: var(--_surface);
|
|
825
|
+
border: var(--_rule-size) solid var(--_rule);
|
|
826
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
827
|
+
display: inline-flex;
|
|
828
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
829
|
+
inline-size: max-content;
|
|
830
|
+
inset-block-start: 0;
|
|
831
|
+
inset-inline: 0;
|
|
832
|
+
margin-inline: auto;
|
|
833
|
+
padding: var(--slotted-space-1, 0.125rem);
|
|
834
|
+
position: absolute;
|
|
835
|
+
translate: 0 -50%;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Two inner controls, drawn by the family and not mounted from `button`
|
|
840
|
+
* (R2): a button carries a height, a padding, a frame and a shadow that do
|
|
841
|
+
* not fit inside a pill on a hairline. The ground moves under the pointer
|
|
842
|
+
* and nothing else does; press goes one step further; both hold back for a
|
|
843
|
+
* disabled control.
|
|
844
|
+
*/
|
|
845
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle'],
|
|
846
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'] {
|
|
847
|
+
/* One step above the nav row's arrow: a mark on a hairline pill reads
|
|
848
|
+
thin at the small step, and the reviewer asked for weight. The marks
|
|
849
|
+
are the Glyph elements the bindings render — `chevron-down` in the
|
|
850
|
+
toggle, `pin` or `pin-off` in the pin by its state. */
|
|
851
|
+
--slotted-icon-size: var(--slotted-control-icon-size-md, 16px);
|
|
852
|
+
|
|
853
|
+
align-items: center;
|
|
854
|
+
appearance: none;
|
|
855
|
+
background-color: transparent;
|
|
856
|
+
block-size: var(--_control-size);
|
|
857
|
+
border: 0;
|
|
858
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
859
|
+
color: var(--_muted);
|
|
860
|
+
cursor: pointer;
|
|
861
|
+
display: inline-flex;
|
|
862
|
+
inline-size: var(--_control-size);
|
|
863
|
+
justify-content: center;
|
|
864
|
+
margin: 0;
|
|
865
|
+
min-block-size: var(--slotted-target-min, 24px);
|
|
866
|
+
min-inline-size: var(--slotted-target-min, 24px);
|
|
867
|
+
padding: 0;
|
|
868
|
+
transition:
|
|
869
|
+
background-color var(--slotted-duration-short) var(--_easing),
|
|
870
|
+
color var(--slotted-duration-short) var(--_easing);
|
|
871
|
+
}
|
|
872
|
+
[data-ui='page-header']
|
|
873
|
+
> *
|
|
874
|
+
> [data-part='controls']
|
|
875
|
+
> *
|
|
876
|
+
> [data-part='toggle']
|
|
877
|
+
> [data-ui='icon'] {
|
|
878
|
+
transition: rotate var(--slotted-duration-short) var(--_easing);
|
|
879
|
+
}
|
|
880
|
+
[data-ui='page-header'][data-expanded]
|
|
881
|
+
> *
|
|
882
|
+
> [data-part='controls']
|
|
883
|
+
> *
|
|
884
|
+
> [data-part='toggle']
|
|
885
|
+
> [data-ui='icon'] {
|
|
886
|
+
rotate: 180deg;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Too much of the screen to hold: expanded, the header would take more
|
|
890
|
+
* than six tenths of the scrollport, or it is narrower than 600 pixels. On
|
|
891
|
+
* a phone a pinned header took nearly the whole height and left nothing to
|
|
892
|
+
* read, so the pin is not offered — the binding writes the fact and lets a
|
|
893
|
+
* held header go — and below 600 pixels the sheet hides it on its own.
|
|
894
|
+
*/
|
|
895
|
+
[data-ui='page-header'][data-tall] > * > [data-part='controls'] > * > [data-part='pin'] {
|
|
896
|
+
display: none;
|
|
897
|
+
}
|
|
898
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'][data-pressed] {
|
|
899
|
+
background-color: var(--slotted-tone-accent-surface, transparent);
|
|
900
|
+
color: var(--slotted-tone-accent-text, LinkText);
|
|
901
|
+
}
|
|
902
|
+
[data-ui='page-header']
|
|
903
|
+
> *
|
|
904
|
+
> [data-part='controls']
|
|
905
|
+
> *
|
|
906
|
+
> [data-part='pin'][data-pressed]:not(:disabled):hover {
|
|
907
|
+
background-color: var(--slotted-tone-accent-subtle-hover, ButtonFace);
|
|
908
|
+
color: var(--slotted-tone-accent-text, LinkText);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
[data-ui='page-header']
|
|
912
|
+
> *
|
|
913
|
+
> [data-part='controls']
|
|
914
|
+
> *
|
|
915
|
+
> [data-part='toggle']:not(:disabled):hover,
|
|
916
|
+
[data-ui='page-header']
|
|
917
|
+
> *
|
|
918
|
+
> [data-part='controls']
|
|
919
|
+
> *
|
|
920
|
+
> [data-part='pin']:not(:disabled):hover {
|
|
921
|
+
background-color: var(--slotted-tone-secondary-subtle-hover, ButtonFace);
|
|
922
|
+
color: var(--_text);
|
|
923
|
+
}
|
|
924
|
+
[data-ui='page-header']
|
|
925
|
+
> *
|
|
926
|
+
> [data-part='controls']
|
|
927
|
+
> *
|
|
928
|
+
> [data-part='toggle']:not(:disabled):active,
|
|
929
|
+
[data-ui='page-header']
|
|
930
|
+
> *
|
|
931
|
+
> [data-part='controls']
|
|
932
|
+
> *
|
|
933
|
+
> [data-part='pin']:not(:disabled):active {
|
|
934
|
+
background-color: var(--slotted-tone-secondary-subtle-active, ButtonFace);
|
|
935
|
+
}
|
|
936
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle']:focus-visible,
|
|
937
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin']:focus-visible {
|
|
938
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
939
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
940
|
+
}
|
|
941
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='toggle']:disabled,
|
|
942
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin']:disabled {
|
|
943
|
+
color: var(--slotted-disabled-foreground, GrayText);
|
|
944
|
+
cursor: default;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* ── The tabs ────────────────────────────────────────────────────────── */
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* The slot a tabs family sits in, and the last thing in the root, which is
|
|
951
|
+
* why it needs no sticky of its own. The pill above it needs the room its
|
|
952
|
+
* lower half takes, so the slot starts half a control down.
|
|
953
|
+
*/
|
|
954
|
+
[data-ui='page-header'] > * > [data-part='tabs'] {
|
|
955
|
+
/* The root's bottom edge is the tabs' track: one line, the whole width. */
|
|
956
|
+
--slotted-tabs-track-color: transparent;
|
|
957
|
+
|
|
958
|
+
min-inline-size: 0;
|
|
959
|
+
padding-block-start: calc(var(--_control-size) / 2 + var(--slotted-space-2, 0.25rem));
|
|
960
|
+
}
|
|
961
|
+
[data-ui='page-header'] > * > [data-part='tabs']:empty {
|
|
962
|
+
display: none;
|
|
963
|
+
}
|
|
964
|
+
/* Without a pill the tabs sit on the rule. */
|
|
965
|
+
[data-ui='page-header'] > * > [data-part='controls']:empty + [data-part='tabs'] {
|
|
966
|
+
padding-block-start: 0;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/* ── One state only ──────────────────────────────────────────────────── */
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* A child of the title area that exists in one state. Fiori has seven slots
|
|
973
|
+
* for this; one attribute does the same, and `display: none` keeps one
|
|
974
|
+
* heading in the accessibility tree at a time.
|
|
975
|
+
*
|
|
976
|
+
* The attribute is written twice, beside `data-part`, on purpose: the
|
|
977
|
+
* deepest part rule sets `display` with six attribute selectors, and this
|
|
978
|
+
* one has to outweigh it for any part at any depth, so it carries six as
|
|
979
|
+
* well and comes later. It is honoured on parts, which is where a consumer
|
|
980
|
+
* writes it.
|
|
981
|
+
*/
|
|
982
|
+
[data-ui='page-header'][data-expanded]
|
|
983
|
+
> *
|
|
984
|
+
> [data-part='title-area']
|
|
985
|
+
[data-part][data-when='snapped'][data-when],
|
|
986
|
+
[data-ui='page-header']:not([data-expanded])
|
|
987
|
+
> *
|
|
988
|
+
> [data-part='title-area']
|
|
989
|
+
[data-part][data-when='expanded'][data-when] {
|
|
990
|
+
display: none;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/* ── Loading ─────────────────────────────────────────────────────────── */
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* The placeholder is skeleton's — a family that waits paints none of its
|
|
997
|
+
* own — and this sheet says only how wide each one is. A line is one `lh`
|
|
998
|
+
* of the text it replaces, which is what keeps the height still when the
|
|
999
|
+
* title arrives.
|
|
1000
|
+
*/
|
|
1001
|
+
[data-ui='page-header'][data-loading]
|
|
1002
|
+
> *
|
|
1003
|
+
> [data-part='title-area']
|
|
1004
|
+
> [data-part='title-row']
|
|
1005
|
+
> [data-part='title']
|
|
1006
|
+
> [data-part='heading'] {
|
|
1007
|
+
/* The width a title has, and no growth: grown, the heading pushed the
|
|
1008
|
+
marks to the far end and they jumped back beside it when the title
|
|
1009
|
+
arrived. The bar is what the words will be. */
|
|
1010
|
+
--slotted-skeleton-inline-size: 24ch;
|
|
1011
|
+
flex: 0 1 auto;
|
|
1012
|
+
}
|
|
1013
|
+
[data-ui='page-header'][data-loading]
|
|
1014
|
+
> *
|
|
1015
|
+
> [data-part='title-area']
|
|
1016
|
+
> [data-part='title-row']
|
|
1017
|
+
> [data-part='title']
|
|
1018
|
+
> [data-part='subheading'] {
|
|
1019
|
+
--slotted-skeleton-inline-size: 32ch;
|
|
1020
|
+
}
|
|
1021
|
+
[data-ui='page-header'][data-loading]
|
|
1022
|
+
> *
|
|
1023
|
+
> [data-part='header-area']
|
|
1024
|
+
> [data-part='content']
|
|
1025
|
+
> [data-part='description'] {
|
|
1026
|
+
--slotted-skeleton-inline-size: 40ch;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/* ── The wait on the chrome's edge ───────────────────────────────────── */
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* A refresh of what is already on screen: the content stays, and the one
|
|
1033
|
+
* thing drawn is a progress bar over the line where the chrome ends. The
|
|
1034
|
+
* frame is a row of the container one hairline tall and given back by its
|
|
1035
|
+
* margin, so it costs the flow nothing, and it runs from `full` to `full` —
|
|
1036
|
+
* the lines the container's own breakout part spans — so the bar is the
|
|
1037
|
+
* header's width, as the line is, and not the measure's.
|
|
1038
|
+
*
|
|
1039
|
+
* Written after the controls and before the tabs, as the controls are, it
|
|
1040
|
+
* stands on the edge the pill straddles; on the root's own edge the bar
|
|
1041
|
+
* would run under the tabs and read as the selected tab's indicator drawn
|
|
1042
|
+
* across the whole row. With no tabs it is the root's own hairline. The bar
|
|
1043
|
+
* stands on the frame's block-end edge and its thickness rises above it,
|
|
1044
|
+
* which is why the frame does not clip.
|
|
1045
|
+
*
|
|
1046
|
+
* A snapped header keeps its edge on screen, and the frame keeps its place
|
|
1047
|
+
* beside the controls, so the bar stays with it. Above the title area,
|
|
1048
|
+
* whose ground would cover the bar's upper pixel once the area has slid
|
|
1049
|
+
* under the title; below the controls, whose pill straddles the same line.
|
|
1050
|
+
*/
|
|
1051
|
+
[data-ui='page-header'] > * > [data-part='progress'] {
|
|
1052
|
+
block-size: var(--_rule-size);
|
|
1053
|
+
grid-column: full;
|
|
1054
|
+
margin-block-end: calc(-1 * var(--_rule-size));
|
|
1055
|
+
pointer-events: none;
|
|
1056
|
+
position: relative;
|
|
1057
|
+
z-index: 3;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* One wait at a time. The title's skeleton is the first load, and a
|
|
1062
|
+
* refresh of nothing is not a refresh; a region around the header that
|
|
1063
|
+
* waits as a whole — a shell between two pages — answers for everything
|
|
1064
|
+
* inside it. The second rule reads an ARIA state rather than another
|
|
1065
|
+
* family's identity, so any region a consumer marks busy takes it.
|
|
1066
|
+
*/
|
|
1067
|
+
[data-ui='page-header'][data-loading] > * > [data-part='progress'],
|
|
1068
|
+
[aria-busy='true'] [data-ui='page-header'] > * > [data-part='progress'] {
|
|
1069
|
+
display: none;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* ── Narrow ──────────────────────────────────────────────────────────── */
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* Below 600 pixels: the breadcrumbs go and the way back stays; the title
|
|
1076
|
+
* steps down; the actions and the navigation take the row under the title,
|
|
1077
|
+
* navigation first so the pair of arrows keeps to the edge. The rule that
|
|
1078
|
+
* moves the navigation into the actions row is sap.f's at 1280, read
|
|
1079
|
+
* against the component's own width rather than the viewport.
|
|
1080
|
+
*/
|
|
1081
|
+
@container page-header (width < 600px) {
|
|
1082
|
+
[data-ui='page-header'] {
|
|
1083
|
+
--_heading-size: var(
|
|
1084
|
+
--slotted-page-header-heading-size-sm,
|
|
1085
|
+
var(--slotted-font-size-xl, 1.125rem)
|
|
1086
|
+
);
|
|
1087
|
+
--_heading-line-height: var(--slotted-line-height-xl, 1.45);
|
|
1088
|
+
--_snapped-heading-size: var(
|
|
1089
|
+
--slotted-page-header-snapped-heading-size-sm,
|
|
1090
|
+
var(--slotted-font-size-md, 0.875rem)
|
|
1091
|
+
);
|
|
1092
|
+
--_snapped-heading-line-height: var(--slotted-line-height-md, 1.5);
|
|
1093
|
+
}
|
|
1094
|
+
[data-ui='page-header']
|
|
1095
|
+
> *
|
|
1096
|
+
> [data-part='title-area']
|
|
1097
|
+
> [data-part='nav']
|
|
1098
|
+
> [data-part='back']
|
|
1099
|
+
~ [data-part='breadcrumbs'] {
|
|
1100
|
+
display: none;
|
|
1101
|
+
}
|
|
1102
|
+
[data-ui='page-header']
|
|
1103
|
+
> *
|
|
1104
|
+
> [data-part='title-area']
|
|
1105
|
+
> [data-part='nav']
|
|
1106
|
+
> [data-part='back']:has(~ [data-part='breadcrumbs']:not(:empty)) {
|
|
1107
|
+
display: inline-flex;
|
|
1108
|
+
}
|
|
1109
|
+
[data-ui='page-header'] > * > [data-part='controls'] > * > [data-part='pin'] {
|
|
1110
|
+
display: none;
|
|
1111
|
+
}
|
|
1112
|
+
[data-ui='page-header']
|
|
1113
|
+
> *
|
|
1114
|
+
> [data-part='title-area']
|
|
1115
|
+
> [data-part='title-row']
|
|
1116
|
+
> [data-part='title'] {
|
|
1117
|
+
flex: 1 1 0;
|
|
1118
|
+
min-inline-size: 60%;
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* The cluster grows so that, wrapped onto its own row, it takes the row.
|
|
1122
|
+
* Its content stands at the end whichever row it lands on: beside a short
|
|
1123
|
+
* title that leaves it room, the grown box put one button at its own
|
|
1124
|
+
* start, mid-row, off the edge every other width aligns it to.
|
|
1125
|
+
*/
|
|
1126
|
+
[data-ui='page-header']
|
|
1127
|
+
> *
|
|
1128
|
+
> [data-part='title-area']
|
|
1129
|
+
> [data-part='title-row']
|
|
1130
|
+
> [data-part='actions'] {
|
|
1131
|
+
flex: 1 1 auto;
|
|
1132
|
+
justify-content: flex-end;
|
|
1133
|
+
}
|
|
1134
|
+
/* The marks take a line of their own under the heading: beside it their break depends on
|
|
1135
|
+
what they say, which the waiting header cannot know, and the header grew when it landed. */
|
|
1136
|
+
[data-ui='page-header']
|
|
1137
|
+
> *
|
|
1138
|
+
> [data-part='title-area']
|
|
1139
|
+
> [data-part='title-row']
|
|
1140
|
+
> [data-part='title']
|
|
1141
|
+
> [data-part='marks'] {
|
|
1142
|
+
flex-basis: 100%;
|
|
1143
|
+
}
|
|
1144
|
+
[data-ui='page-header']
|
|
1145
|
+
> *
|
|
1146
|
+
> [data-part='title-area']
|
|
1147
|
+
> [data-part='title-row']:has(
|
|
1148
|
+
> [data-part='actions'] > [data-ui='toolbar'][data-overflow='menu']
|
|
1149
|
+
)
|
|
1150
|
+
> [data-part='title']
|
|
1151
|
+
> [data-part='marks'] {
|
|
1152
|
+
grid-column: 1 / -1;
|
|
1153
|
+
}
|
|
1154
|
+
/* One line, as snapped: the phone has no second one to give it. */
|
|
1155
|
+
[data-ui='page-header']
|
|
1156
|
+
> *
|
|
1157
|
+
> [data-part='title-area']
|
|
1158
|
+
> [data-part='title-row']
|
|
1159
|
+
> [data-part='title']
|
|
1160
|
+
> [data-part='subheading'] {
|
|
1161
|
+
display: block;
|
|
1162
|
+
text-overflow: ellipsis;
|
|
1163
|
+
white-space: nowrap;
|
|
1164
|
+
}
|
|
1165
|
+
[data-ui='page-header']
|
|
1166
|
+
> *
|
|
1167
|
+
> [data-part='title-area']
|
|
1168
|
+
> [data-part='title-row']
|
|
1169
|
+
> [data-part='navigation'] {
|
|
1170
|
+
border-inline-start: 0;
|
|
1171
|
+
padding-inline-start: 0;
|
|
1172
|
+
}
|
|
1173
|
+
[data-ui='page-header'] > * > [data-part='header-area'] {
|
|
1174
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1175
|
+
}
|
|
1176
|
+
[data-ui='page-header'] > * > [data-part='header-area'] > [data-part='figure'] {
|
|
1177
|
+
padding-block-end: var(--_gap);
|
|
1178
|
+
padding-inline-end: 0;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|