@expressivecss/expressive 0.4.0 → 0.6.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/CHANGELOG.md +127 -0
- package/README.md +4 -9
- package/dist/css/expressive.css +2617 -951
- package/dist/css/expressive.css.map +1 -1
- package/dist/css/expressive.min.css +1 -1
- package/dist/css/expressive.min.css.map +1 -1
- package/dist/js/expressive.cjs +643 -804
- package/dist/js/expressive.cjs.map +4 -4
- package/dist/js/expressive.js +643 -804
- package/dist/js/expressive.js.map +4 -4
- package/dist/js/expressive.min.js +11 -11
- package/dist/js/expressive.min.js.map +4 -4
- package/dist/js/expressive.mjs +643 -804
- package/dist/js/expressive.mjs.map +4 -4
- package/dist/types/behaviors/bottomSheets.d.ts +18 -0
- package/dist/types/behaviors/sideSheets.d.ts +18 -0
- package/dist/types/components/autocomplete.d.ts +11 -11
- package/dist/types/components/index.d.ts +3 -5
- package/dist/types/components/{dropdown.d.ts → menu.d.ts} +38 -37
- package/dist/types/components/navigationRail.d.ts +53 -0
- package/dist/types/components/range.d.ts +3 -0
- package/dist/types/components/registry.d.ts +7 -15
- package/dist/types/components/select.d.ts +12 -12
- package/dist/types/components/{toasts.d.ts → snackbar.d.ts} +28 -25
- package/dist/types/index.d.ts +3 -1
- package/package.json +3 -2
- package/src/sass/README.md +13 -11
- package/src/sass/abstracts/_breakpoints.scss +14 -3
- package/src/sass/abstracts/_mixins.scss +3 -3
- package/src/sass/abstracts/_variables.scss +27 -2
- package/src/sass/base/_global.scss +1 -1
- package/src/sass/base/_grid.scss +66 -116
- package/src/sass/base/_index.scss +1 -1
- package/src/sass/components/_badges.scss +80 -60
- package/src/sass/components/_bottom-sheet.scss +137 -0
- package/src/sass/components/_cards.scss +4 -4
- package/src/sass/components/_chips.scss +6 -5
- package/src/sass/components/_dialog.scss +268 -0
- package/src/sass/components/_index.scss +9 -7
- package/src/sass/components/_list.scss +253 -0
- package/src/sass/components/_menu.scss +341 -0
- package/src/sass/components/_navbar.scss +65 -117
- package/src/sass/components/_navigation-bar.scss +165 -0
- package/src/sass/components/_navigation-rail.scss +308 -0
- package/src/sass/components/_page-footer.scss +7 -3
- package/src/sass/components/_panes.scss +294 -0
- package/src/sass/components/_side-sheet.scss +193 -0
- package/src/sass/components/_sidenav.scss +78 -49
- package/src/sass/components/{_toast.scss → _snackbar.scss} +35 -34
- package/src/sass/components/_tabs.scss +1 -27
- package/src/sass/components/_toolbar.scss +91 -25
- package/src/sass/components/_tooltip.scss +1 -1
- package/src/sass/components/forms/_fieldset.scss +1 -1
- package/src/sass/components/forms/_input-fields.scss +3 -3
- package/src/sass/components/forms/_range.scss +423 -98
- package/src/sass/components/forms/_select.scss +19 -17
- package/src/sass/tokens/_reference.scss +37 -13
- package/src/sass/tokens/_theme.scss +5 -13
- package/src/sass/utilities/_colors.scss +16 -74
- package/src/sass/utilities/_visibility.scss +8 -0
- package/dist/types/components/collapsible.d.ts +0 -79
- package/dist/types/components/pushpin.d.ts +0 -51
- package/dist/types/components/tapTarget.d.ts +0 -57
- package/src/sass/components/_collapsible.scss +0 -179
- package/src/sass/components/_collection.scss +0 -180
- package/src/sass/components/_dropdown.scss +0 -225
- package/src/sass/components/_modal.scss +0 -190
- package/src/sass/components/_pushpin.scss +0 -11
- package/src/sass/components/_tapTarget.scss +0 -187
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
@use "../abstracts" as *;
|
|
2
|
+
|
|
3
|
+
// Vertical menus (M3 Expressive).
|
|
4
|
+
//
|
|
5
|
+
// Anatomy is the HTML. A <menu id> is the surface; each <li> is an item.
|
|
6
|
+
// A leading <i> is the leading icon; a trailing <i> or <kbd> is trailing
|
|
7
|
+
// content. An <hr> is a divider. A .gap <li> splits groups. A .label
|
|
8
|
+
// <li> is a 32dp group heading. A nested <menu> is a flyout. The trigger
|
|
9
|
+
// is .menu-trigger[data-target] — that is the JS contract.
|
|
10
|
+
//
|
|
11
|
+
// Tokens follow https://m3.material.io/components/menus/specs
|
|
12
|
+
// container surface-container, 16dp corners, elevation 2, 112–280
|
|
13
|
+
// padding 4dp
|
|
14
|
+
// item 48dp, extra-small (4dp) corners, label-large / on-surface
|
|
15
|
+
// selected medium (12dp) corners, tertiary-container / on-tertiary
|
|
16
|
+
// icon 20dp, on-surface-variant
|
|
17
|
+
// divider 1dp outline-variant, inset (not full-bleed)
|
|
18
|
+
// gap 6dp, more expressive than a divider
|
|
19
|
+
// label 32dp, on-surface-variant
|
|
20
|
+
// vibrant tertiary-container surface; selected is tertiary
|
|
21
|
+
// submenu scale + fade; focused flyout rounds up, parent rounds down
|
|
22
|
+
|
|
23
|
+
$_menu-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M10 6l6 6-6 6'/%3E%3C/svg%3E");
|
|
24
|
+
|
|
25
|
+
[popover] {
|
|
26
|
+
outline: none;
|
|
27
|
+
padding: 0;
|
|
28
|
+
border: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@mixin menu-surface {
|
|
32
|
+
--md-comp-menu-container-color: var(--md-sys-color-surface-container);
|
|
33
|
+
--md-comp-menu-container-shape: 16px;
|
|
34
|
+
--md-comp-menu-item-container-height: 48px;
|
|
35
|
+
--md-comp-menu-item-container-shape: 4px;
|
|
36
|
+
--md-comp-menu-item-selected-container-shape: 12px;
|
|
37
|
+
--md-comp-menu-item-label-text-color: var(--md-sys-color-on-surface);
|
|
38
|
+
--md-comp-menu-item-leading-icon-color: var(--md-sys-color-on-surface-variant);
|
|
39
|
+
--md-comp-menu-item-trailing-icon-color: var(--md-sys-color-on-surface-variant);
|
|
40
|
+
--md-comp-menu-item-selected-container-color: var(--md-sys-color-tertiary-container);
|
|
41
|
+
--md-comp-menu-item-selected-label-text-color: var(--md-sys-color-on-tertiary-container);
|
|
42
|
+
--md-comp-menu-item-selected-leading-icon-color: var(--md-sys-color-on-tertiary-container);
|
|
43
|
+
--md-comp-menu-item-icon-size: 20px;
|
|
44
|
+
--md-comp-menu-supporting-text-color: var(--md-sys-color-on-surface-variant);
|
|
45
|
+
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
min-width: 112px;
|
|
48
|
+
margin: 0;
|
|
49
|
+
padding: 4px;
|
|
50
|
+
list-style: none;
|
|
51
|
+
background-color: var(--md-comp-menu-container-color);
|
|
52
|
+
border-radius: var(--md-comp-menu-container-shape);
|
|
53
|
+
color: var(--md-comp-menu-item-label-text-color);
|
|
54
|
+
@include z-depth("2");
|
|
55
|
+
transition: border-radius 200ms ease;
|
|
56
|
+
|
|
57
|
+
&.vibrant {
|
|
58
|
+
--md-comp-menu-container-color: var(--md-sys-color-tertiary-container);
|
|
59
|
+
--md-comp-menu-item-label-text-color: var(--md-sys-color-on-tertiary-container);
|
|
60
|
+
--md-comp-menu-item-leading-icon-color: var(--md-sys-color-on-tertiary-container);
|
|
61
|
+
--md-comp-menu-item-trailing-icon-color: var(--md-sys-color-on-tertiary-container);
|
|
62
|
+
--md-comp-menu-supporting-text-color: var(--md-sys-color-on-tertiary-container);
|
|
63
|
+
--md-comp-menu-item-selected-container-color: var(--md-sys-color-tertiary);
|
|
64
|
+
--md-comp-menu-item-selected-label-text-color: var(--md-sys-color-on-tertiary);
|
|
65
|
+
--md-comp-menu-item-selected-leading-icon-color: var(--md-sys-color-on-tertiary);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
> li {
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
margin: 0;
|
|
71
|
+
padding: 0;
|
|
72
|
+
width: 100%;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
text-align: left;
|
|
75
|
+
border-radius: var(--md-comp-menu-item-container-shape);
|
|
76
|
+
|
|
77
|
+
> a,
|
|
78
|
+
> button,
|
|
79
|
+
> span {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 12px;
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
width: 100%;
|
|
85
|
+
min-height: var(--md-comp-menu-item-container-height);
|
|
86
|
+
margin: 0;
|
|
87
|
+
padding: 0 8px;
|
|
88
|
+
border: 0;
|
|
89
|
+
background: none;
|
|
90
|
+
color: var(--md-comp-menu-item-label-text-color);
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
|
|
94
|
+
&:hover,
|
|
95
|
+
&:focus {
|
|
96
|
+
background: none;
|
|
97
|
+
}
|
|
98
|
+
@include typescale("label-large");
|
|
99
|
+
font-family: $font-stack;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
> a > i,
|
|
103
|
+
> button > i,
|
|
104
|
+
> i {
|
|
105
|
+
flex: 0 0 var(--md-comp-menu-item-icon-size);
|
|
106
|
+
font-size: var(--md-comp-menu-item-icon-size);
|
|
107
|
+
width: var(--md-comp-menu-item-icon-size);
|
|
108
|
+
height: var(--md-comp-menu-item-icon-size);
|
|
109
|
+
line-height: var(--md-comp-menu-item-icon-size);
|
|
110
|
+
color: var(--md-comp-menu-item-leading-icon-color);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
> :is(a, button) > i:last-child:not(:only-child) {
|
|
114
|
+
margin-inline-start: auto;
|
|
115
|
+
color: var(--md-comp-menu-item-trailing-icon-color);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
> :is(a, button) > :is(kbd, .shortcut) {
|
|
119
|
+
margin-inline-start: auto;
|
|
120
|
+
padding: 0;
|
|
121
|
+
border: 0;
|
|
122
|
+
background: none;
|
|
123
|
+
color: var(--md-comp-menu-supporting-text-color);
|
|
124
|
+
font-family: inherit;
|
|
125
|
+
@include typescale("label-large");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
> li:not(.disabled, .divider, .optgroup, .gap, .label) {
|
|
130
|
+
min-height: var(--md-comp-menu-item-container-height);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Hover / press sit on extra-small corners and do not span the container.
|
|
134
|
+
> li:not(.disabled, .divider, .optgroup, .gap, .label):hover,
|
|
135
|
+
> li.active:not(.selected, [aria-selected="true"]) {
|
|
136
|
+
background-color: color-mix(
|
|
137
|
+
in oklab,
|
|
138
|
+
var(--md-comp-menu-item-label-text-color) 8%,
|
|
139
|
+
transparent
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
> li:not(.disabled, .divider, .optgroup, .gap, .label):active {
|
|
144
|
+
background-color: color-mix(
|
|
145
|
+
in oklab,
|
|
146
|
+
var(--md-comp-menu-item-label-text-color) 10%,
|
|
147
|
+
transparent
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
> li.selected,
|
|
152
|
+
> li[aria-selected="true"] {
|
|
153
|
+
border-radius: var(--md-comp-menu-item-selected-container-shape);
|
|
154
|
+
background-color: var(--md-comp-menu-item-selected-container-color);
|
|
155
|
+
color: var(--md-comp-menu-item-selected-label-text-color);
|
|
156
|
+
|
|
157
|
+
> :is(a, button, span) {
|
|
158
|
+
color: var(--md-comp-menu-item-selected-label-text-color);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
> :is(a, button, span) > i,
|
|
162
|
+
> i {
|
|
163
|
+
color: var(--md-comp-menu-item-selected-leading-icon-color);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
> li.selected:hover,
|
|
168
|
+
> li[aria-selected="true"]:hover {
|
|
169
|
+
background-color: color-mix(
|
|
170
|
+
in oklab,
|
|
171
|
+
var(--md-comp-menu-item-selected-label-text-color) 8%,
|
|
172
|
+
var(--md-comp-menu-item-selected-container-color)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
> li:focus-visible {
|
|
177
|
+
outline: 3px solid var(--md-sys-color-secondary);
|
|
178
|
+
outline-offset: -3px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
> li.disabled {
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
opacity: 0.38;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Inset divider — selection and rules do not span the container.
|
|
187
|
+
> hr,
|
|
188
|
+
> li.divider {
|
|
189
|
+
display: block;
|
|
190
|
+
width: calc(100% - 8px);
|
|
191
|
+
height: 1px;
|
|
192
|
+
min-height: 0;
|
|
193
|
+
margin: 4px auto;
|
|
194
|
+
padding: 0;
|
|
195
|
+
border: 0;
|
|
196
|
+
background-color: var(--md-sys-color-outline-variant);
|
|
197
|
+
cursor: default;
|
|
198
|
+
pointer-events: none;
|
|
199
|
+
border-radius: 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Gap: more expressive than a divider. One or two per menu; not in a scroller.
|
|
203
|
+
> li.gap {
|
|
204
|
+
height: 6px;
|
|
205
|
+
min-height: 6px;
|
|
206
|
+
margin: 0 -4px;
|
|
207
|
+
padding: 0;
|
|
208
|
+
pointer-events: none;
|
|
209
|
+
cursor: default;
|
|
210
|
+
background: transparent;
|
|
211
|
+
border-radius: 0;
|
|
212
|
+
box-shadow: none;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
> li.label {
|
|
216
|
+
display: flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
min-height: 32px;
|
|
219
|
+
padding: 0 8px;
|
|
220
|
+
pointer-events: none;
|
|
221
|
+
cursor: default;
|
|
222
|
+
color: var(--md-comp-menu-supporting-text-color);
|
|
223
|
+
@include typescale("label-large");
|
|
224
|
+
font-family: $font-stack;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
li:has(> menu) {
|
|
228
|
+
position: relative;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
li:has(> menu) > :is(a, button, span)::after {
|
|
232
|
+
content: "";
|
|
233
|
+
flex: 0 0 var(--md-comp-menu-item-icon-size);
|
|
234
|
+
width: var(--md-comp-menu-item-icon-size);
|
|
235
|
+
height: var(--md-comp-menu-item-icon-size);
|
|
236
|
+
margin-inline-start: auto;
|
|
237
|
+
background-color: var(--md-comp-menu-item-trailing-icon-color);
|
|
238
|
+
mask: $_menu-chevron center / 20px no-repeat;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
menu[id] {
|
|
243
|
+
@include menu-surface;
|
|
244
|
+
|
|
245
|
+
display: none;
|
|
246
|
+
position: absolute;
|
|
247
|
+
left: 0;
|
|
248
|
+
top: 0;
|
|
249
|
+
z-index: 9999;
|
|
250
|
+
overflow-y: auto;
|
|
251
|
+
opacity: 0;
|
|
252
|
+
user-select: none;
|
|
253
|
+
transform-origin: 0 0;
|
|
254
|
+
|
|
255
|
+
// Nested flyouts sit outside the list box; do not clip them.
|
|
256
|
+
&:has(> li > menu) {
|
|
257
|
+
overflow: visible;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Unfocused parent rounds down while a flyout is the focus.
|
|
261
|
+
&:has(> li:is(:hover, :focus-within, .open) > menu) {
|
|
262
|
+
--md-comp-menu-container-shape: 8px;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Nested menu. Optional, desktop-first. No second Menu instance —
|
|
267
|
+
// a <menu> inside an <li> is a flyout. Hover / :focus-within on fine
|
|
268
|
+
// pointers; .open is the tap / keyboard switch.
|
|
269
|
+
menu[id] li > menu {
|
|
270
|
+
@include menu-surface;
|
|
271
|
+
|
|
272
|
+
display: block;
|
|
273
|
+
position: absolute;
|
|
274
|
+
inset-block-start: 0;
|
|
275
|
+
inset-inline-start: 100%;
|
|
276
|
+
z-index: 1;
|
|
277
|
+
overflow: visible;
|
|
278
|
+
visibility: hidden;
|
|
279
|
+
opacity: 0;
|
|
280
|
+
pointer-events: none;
|
|
281
|
+
transform: translateX(-4px) scale(0.96);
|
|
282
|
+
transform-origin: 0 0;
|
|
283
|
+
transition:
|
|
284
|
+
opacity 180ms ease,
|
|
285
|
+
transform 180ms ease,
|
|
286
|
+
visibility 180ms ease,
|
|
287
|
+
border-radius 200ms ease;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@media (hover: hover) and (pointer: fine) {
|
|
291
|
+
menu[id] li:is(:hover, :focus-within) > menu {
|
|
292
|
+
visibility: visible;
|
|
293
|
+
opacity: 1;
|
|
294
|
+
pointer-events: auto;
|
|
295
|
+
transform: none;
|
|
296
|
+
--md-comp-menu-container-shape: 20px;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
menu[id] li.open > menu {
|
|
301
|
+
visibility: visible;
|
|
302
|
+
opacity: 1;
|
|
303
|
+
pointer-events: auto;
|
|
304
|
+
transform: none;
|
|
305
|
+
--md-comp-menu-container-shape: 20px;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
menu[id] li.submenu-start > menu {
|
|
309
|
+
inset-inline-start: auto;
|
|
310
|
+
inset-inline-end: 100%;
|
|
311
|
+
transform-origin: 100% 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
menu[id] li.submenu-start:is(:hover, :focus-within, .open) > menu {
|
|
315
|
+
transform: none;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
body.keyboard-focused {
|
|
319
|
+
menu[id] > li:focus {
|
|
320
|
+
background-color: color-mix(
|
|
321
|
+
in oklab,
|
|
322
|
+
var(--md-comp-menu-item-label-text-color) 10%,
|
|
323
|
+
transparent
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
menu[id] > li:focus:not(.selected, [aria-selected="true"]) {
|
|
328
|
+
outline: 3px solid var(--md-sys-color-secondary);
|
|
329
|
+
outline-offset: -3px;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@media (prefers-reduced-motion: reduce) {
|
|
334
|
+
menu[id] li > menu {
|
|
335
|
+
transition: none;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.menu-trigger {
|
|
340
|
+
cursor: pointer;
|
|
341
|
+
}
|
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
@use "../abstracts" as *;
|
|
2
2
|
|
|
3
|
-
// Top
|
|
3
|
+
// Top app bar.
|
|
4
4
|
//
|
|
5
|
-
// Anatomy is the HTML. A header whose child is a nav is a top app bar
|
|
6
|
-
//
|
|
7
|
-
// bar. Headings are the headline, icon-only links/buttons are the leading and
|
|
5
|
+
// Anatomy is the HTML. A header whose child is a nav is a top app bar.
|
|
6
|
+
// Headings are the headline, icon-only links/buttons are the leading and
|
|
8
7
|
// trailing actions, <menu> / <ul> hold text destinations. Helpers are only
|
|
9
8
|
// for variants the markup cannot express: .center, .medium, .large, .fixed.
|
|
9
|
+
// Destinations at the bottom of the screen are nav.navigation-bar, not this.
|
|
10
10
|
//
|
|
11
|
-
// Tokens follow the M3 top-app-bar
|
|
11
|
+
// Tokens follow the M3 top-app-bar spec
|
|
12
12
|
// (https://m3.material.io/components/app-bars/specs):
|
|
13
|
-
// container surface, 64dp small / 112 medium / 152 large
|
|
13
|
+
// container surface, 64dp small / 112 medium / 152 large
|
|
14
14
|
// headline on-surface, title-large (small), headline-small/medium
|
|
15
15
|
// icons on-surface-variant, 24dp in a 48dp target, 4dp inset
|
|
16
|
-
//
|
|
16
|
+
// on scroll surface-container fill (not elevation) so the bar
|
|
17
|
+
// separates from the body. A color utility on the
|
|
18
|
+
// header still wins — it sets background-color itself.
|
|
19
|
+
|
|
20
|
+
// Scroll progress, not the fill color. Registering the fill as <color>
|
|
21
|
+
// with initial-value: transparent made the 8px lerp run from
|
|
22
|
+
// transparent-black through black: light-dark() tokens are IACVT for a
|
|
23
|
+
// typed <color> property, so the used value collapsed to the initial
|
|
24
|
+
// value. A <number> interpolates; color-mix() then resolves the two
|
|
25
|
+
// live surfaces at use time.
|
|
26
|
+
@property --md-comp-top-app-bar-scroll {
|
|
27
|
+
syntax: "<number>";
|
|
28
|
+
inherits: false;
|
|
29
|
+
initial-value: 0;
|
|
30
|
+
}
|
|
17
31
|
|
|
18
|
-
header:has(> nav) {
|
|
32
|
+
header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
19
33
|
--md-comp-top-app-bar-container-color: var(--md-sys-color-surface);
|
|
34
|
+
--md-comp-top-app-bar-scrolled-container-color: var(--md-sys-color-surface-container);
|
|
20
35
|
--md-comp-top-app-bar-headline-color: var(--md-sys-color-on-surface);
|
|
21
36
|
// Inherit so a fill + on-* pair on the header recolors the icons.
|
|
22
37
|
// Set this to --md-sys-color-on-surface-variant for the spec mute.
|
|
@@ -26,12 +41,17 @@ header:has(> nav) {
|
|
|
26
41
|
--md-comp-top-app-bar-leading-icon-size: 24px;
|
|
27
42
|
--md-comp-top-app-bar-container-shape: 0;
|
|
28
43
|
|
|
29
|
-
background-color:
|
|
44
|
+
background-color: color-mix(
|
|
45
|
+
in oklab,
|
|
46
|
+
var(--md-comp-top-app-bar-scrolled-container-color)
|
|
47
|
+
calc(100% * var(--md-comp-top-app-bar-scroll)),
|
|
48
|
+
var(--md-comp-top-app-bar-container-color)
|
|
49
|
+
);
|
|
30
50
|
color: var(--md-comp-top-app-bar-headline-color);
|
|
31
51
|
border-radius: var(--md-comp-top-app-bar-container-shape);
|
|
32
52
|
width: 100%;
|
|
33
53
|
|
|
34
|
-
> nav:not(.tabs) {
|
|
54
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
35
55
|
display: flex;
|
|
36
56
|
align-items: center;
|
|
37
57
|
box-sizing: border-box;
|
|
@@ -41,7 +61,7 @@ header:has(> nav) {
|
|
|
41
61
|
margin: 0;
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
> nav:not(.tabs) > :is(h1, h2, h3, h4, h5, h6) {
|
|
64
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > :is(h1, h2, h3, h4, h5, h6) {
|
|
45
65
|
flex: 1 1 auto;
|
|
46
66
|
min-width: 0;
|
|
47
67
|
margin: 0;
|
|
@@ -63,7 +83,7 @@ header:has(> nav) {
|
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
// Icon-only actions — 48dp target, 24dp glyph, 4dp from the container edge.
|
|
66
|
-
> nav:not(.tabs) > :is(a, button):not(.button):not(.circle):not(.extra):has(> i:only-child) {
|
|
86
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > :is(a, button):not(.button):not(.circle):not(.extra):has(> i:only-child) {
|
|
67
87
|
display: inline-flex;
|
|
68
88
|
flex: 0 0 auto;
|
|
69
89
|
align-items: center;
|
|
@@ -71,7 +91,10 @@ header:has(> nav) {
|
|
|
71
91
|
width: 48px;
|
|
72
92
|
height: 48px;
|
|
73
93
|
padding: 0;
|
|
74
|
-
margin
|
|
94
|
+
// Leave margin-inline-start unset so a medium/large bar can push
|
|
95
|
+
// the first trailing action to the end with margin-inline-start: auto.
|
|
96
|
+
margin-block: 0;
|
|
97
|
+
margin-inline-end: 0;
|
|
75
98
|
border: 0;
|
|
76
99
|
border-radius: 50%;
|
|
77
100
|
background: transparent;
|
|
@@ -100,11 +123,11 @@ header:has(> nav) {
|
|
|
100
123
|
}
|
|
101
124
|
|
|
102
125
|
// Text destinations, either loose or grouped in a menu/list.
|
|
103
|
-
> nav:not(.tabs) > a:not(:has(> i:only-child)):not(.button):not(.circle):not(.breadcrumb) {
|
|
126
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > a:not(:has(> i:only-child)):not(.button):not(.circle):not(.breadcrumb) {
|
|
104
127
|
@extend %app-bar-destination;
|
|
105
128
|
}
|
|
106
129
|
|
|
107
|
-
> nav:not(.tabs) > :is(menu, ul) {
|
|
130
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > :is(menu, ul) {
|
|
108
131
|
display: flex;
|
|
109
132
|
align-items: stretch;
|
|
110
133
|
align-self: stretch;
|
|
@@ -117,7 +140,7 @@ header:has(> nav) {
|
|
|
117
140
|
align-items: stretch;
|
|
118
141
|
}
|
|
119
142
|
|
|
120
|
-
// Direct destinations only.
|
|
143
|
+
// Direct destinations only. Menu JS parks the menu next to the
|
|
121
144
|
// trigger, so a descendant `a` would also paint a square hover on
|
|
122
145
|
// top of the menu's pill.
|
|
123
146
|
> li > a {
|
|
@@ -128,7 +151,7 @@ header:has(> nav) {
|
|
|
128
151
|
// Breadcrumb trail. Layout only — colors stay on the breadcrumb tokens
|
|
129
152
|
// (which this header remaps to inherit). Do not treat the crumbs as
|
|
130
153
|
// destinations.
|
|
131
|
-
> nav:not(.tabs) > ol {
|
|
154
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > ol {
|
|
132
155
|
display: flex;
|
|
133
156
|
flex-wrap: wrap;
|
|
134
157
|
align-items: center;
|
|
@@ -140,7 +163,7 @@ header:has(> nav) {
|
|
|
140
163
|
padding-inline: 12px;
|
|
141
164
|
}
|
|
142
165
|
|
|
143
|
-
> nav:not(.tabs) > form {
|
|
166
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > form {
|
|
144
167
|
display: flex;
|
|
145
168
|
flex: 1 1 auto;
|
|
146
169
|
align-items: center;
|
|
@@ -171,25 +194,6 @@ header:has(> nav) {
|
|
|
171
194
|
}
|
|
172
195
|
}
|
|
173
196
|
}
|
|
174
|
-
|
|
175
|
-
// Secondary row (tabs sit under the bar, not inside it).
|
|
176
|
-
> .tabs {
|
|
177
|
-
background-color: transparent;
|
|
178
|
-
width: 100%;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
@include bp-up("large") {
|
|
182
|
-
// Beats the icon-button display:inline-flex (higher specificity).
|
|
183
|
-
// Same flag the visibility utilities use for breakpoint hides.
|
|
184
|
-
> nav:not(.tabs) > .sidenav-trigger {
|
|
185
|
-
display: none !important;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// The trigger is still :first-child, so the title needs the no-icon inset.
|
|
189
|
-
> nav:not(.tabs) > .sidenav-trigger + :is(h1, h2, h3, h4, h5, h6) {
|
|
190
|
-
padding-inline-start: 12px;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
%app-bar-destination {
|
|
@@ -216,7 +220,7 @@ header:has(> nav) {
|
|
|
216
220
|
|
|
217
221
|
// Center-aligned small bar. The headline is taken out of flow so leading and
|
|
218
222
|
// trailing actions can sit on the edges without shifting the title.
|
|
219
|
-
header.center:has(> nav) > nav:not(.tabs) {
|
|
223
|
+
header.center:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
220
224
|
position: relative;
|
|
221
225
|
justify-content: space-between;
|
|
222
226
|
|
|
@@ -232,12 +236,18 @@ header.center:has(> nav) > nav:not(.tabs) {
|
|
|
232
236
|
}
|
|
233
237
|
}
|
|
234
238
|
|
|
235
|
-
// Medium (112dp) and large (152dp): same markup, title
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
// Medium (112dp) and large (152dp): same markup, title on a second row.
|
|
240
|
+
// The title is pulled out of the first flex line (`order` + 100% basis),
|
|
241
|
+
// so leading/trailing icons would otherwise pack together at the start.
|
|
242
|
+
// `space-between` parks the icon row at the top and the title at the
|
|
243
|
+
// bottom — that's what makes the 112/152 height readable.
|
|
244
|
+
header.medium:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail),
|
|
245
|
+
header.large:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
238
246
|
flex-wrap: wrap;
|
|
239
|
-
align-content:
|
|
247
|
+
align-content: space-between;
|
|
248
|
+
align-items: flex-start;
|
|
240
249
|
height: auto;
|
|
250
|
+
padding-top: 4px;
|
|
241
251
|
|
|
242
252
|
> :is(h1, h2, h3, h4, h5, h6) {
|
|
243
253
|
order: 1;
|
|
@@ -246,9 +256,13 @@ header.large:has(> nav) > nav:not(.tabs) {
|
|
|
246
256
|
overflow: visible;
|
|
247
257
|
text-overflow: unset;
|
|
248
258
|
}
|
|
259
|
+
|
|
260
|
+
> :is(h1, h2, h3, h4, h5, h6) + * {
|
|
261
|
+
margin-inline-start: auto;
|
|
262
|
+
}
|
|
249
263
|
}
|
|
250
264
|
|
|
251
|
-
header.medium:has(> nav) > nav:not(.tabs) {
|
|
265
|
+
header.medium:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
252
266
|
min-height: 112px;
|
|
253
267
|
padding-bottom: 16px;
|
|
254
268
|
|
|
@@ -262,7 +276,7 @@ header.medium:has(> nav) > nav:not(.tabs) {
|
|
|
262
276
|
}
|
|
263
277
|
}
|
|
264
278
|
|
|
265
|
-
header.large:has(> nav) > nav:not(.tabs) {
|
|
279
|
+
header.large:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
266
280
|
min-height: 152px;
|
|
267
281
|
padding-bottom: 28px;
|
|
268
282
|
|
|
@@ -276,9 +290,12 @@ header.large:has(> nav) > nav:not(.tabs) {
|
|
|
276
290
|
}
|
|
277
291
|
}
|
|
278
292
|
|
|
279
|
-
// Pin the bar.
|
|
280
|
-
//
|
|
281
|
-
header
|
|
293
|
+
// Pin the bar. On scroll, fill with surface-container so it separates
|
|
294
|
+
// from the body (M3 Expressive). The animation only drives the mix
|
|
295
|
+
// progress; a utility class on the header sets background-color itself
|
|
296
|
+
// and is not overwritten. Without animation-timeline the bar stays at
|
|
297
|
+
// rest (surface), the spec default.
|
|
298
|
+
header.fixed:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
282
299
|
position: sticky;
|
|
283
300
|
top: 0;
|
|
284
301
|
z-index: 997;
|
|
@@ -289,75 +306,6 @@ header.fixed:has(> nav) {
|
|
|
289
306
|
|
|
290
307
|
@keyframes top-app-bar-scroll {
|
|
291
308
|
to {
|
|
292
|
-
|
|
293
|
-
0 1px 10px 0 rgba(0, 0, 0, 0.12),
|
|
294
|
-
0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Bottom app bar. 80dp row, icons on the start, a FAB (any .btn) pushed to the end.
|
|
299
|
-
footer:has(> nav:only-child) {
|
|
300
|
-
--md-comp-bottom-app-bar-container-color: var(--md-sys-color-surface);
|
|
301
|
-
--md-comp-bottom-app-bar-container-height: 80px;
|
|
302
|
-
|
|
303
|
-
background-color: var(--md-comp-bottom-app-bar-container-color);
|
|
304
|
-
color: var(--md-sys-color-on-surface);
|
|
305
|
-
width: 100%;
|
|
306
|
-
@include z-depth("2");
|
|
307
|
-
|
|
308
|
-
> nav:not(.tabs) {
|
|
309
|
-
display: flex;
|
|
310
|
-
align-items: center;
|
|
311
|
-
box-sizing: border-box;
|
|
312
|
-
width: 100%;
|
|
313
|
-
min-height: var(--md-comp-bottom-app-bar-container-height);
|
|
314
|
-
padding: 12px 4px;
|
|
315
|
-
margin: 0;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
> nav:not(.tabs) > :is(a, button):not(.button):not(.circle):not(.extra):has(> i:only-child) {
|
|
319
|
-
display: inline-flex;
|
|
320
|
-
flex: 0 0 auto;
|
|
321
|
-
align-items: center;
|
|
322
|
-
justify-content: center;
|
|
323
|
-
width: 48px;
|
|
324
|
-
height: 48px;
|
|
325
|
-
padding: 0;
|
|
326
|
-
margin: 0;
|
|
327
|
-
border: 0;
|
|
328
|
-
border-radius: 50%;
|
|
329
|
-
background: transparent;
|
|
330
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
331
|
-
cursor: pointer;
|
|
332
|
-
appearance: none;
|
|
333
|
-
font: inherit;
|
|
334
|
-
text-decoration: none;
|
|
335
|
-
@include focus-visible;
|
|
336
|
-
|
|
337
|
-
> i {
|
|
338
|
-
display: block;
|
|
339
|
-
font-size: 24px;
|
|
340
|
-
width: 24px;
|
|
341
|
-
height: 24px;
|
|
342
|
-
line-height: 24px;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
&:hover {
|
|
346
|
-
background-color: color-mix(in oklab, currentColor 8%, transparent);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
&:active {
|
|
350
|
-
background-color: color-mix(in oklab, currentColor 10%, transparent);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
> nav:not(.tabs) > :is(.button, .circle, .extra) {
|
|
355
|
-
margin-inline-start: auto;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
&.fixed {
|
|
359
|
-
position: sticky;
|
|
360
|
-
bottom: 0;
|
|
361
|
-
z-index: 997;
|
|
309
|
+
--md-comp-top-app-bar-scroll: 1;
|
|
362
310
|
}
|
|
363
311
|
}
|