@expressivecss/expressive 0.3.0 → 0.5.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 +96 -0
- package/README.md +4 -9
- package/dist/css/expressive.css +3183 -2150
- 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 +1254 -1567
- package/dist/js/expressive.cjs.map +4 -4
- package/dist/js/expressive.js +1254 -1567
- package/dist/js/expressive.js.map +4 -4
- package/dist/js/expressive.min.js +12 -22
- package/dist/js/expressive.min.js.map +4 -4
- package/dist/js/expressive.mjs +1254 -1568
- 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 +13 -20
- package/dist/types/components/buttons.d.ts +2 -2
- package/dist/types/components/cards.d.ts +3 -6
- package/dist/types/components/carousel.d.ts +3 -3
- package/dist/types/components/characterCounter.d.ts +3 -5
- package/dist/types/components/chips.d.ts +2 -2
- package/dist/types/components/datepicker.d.ts +2 -8
- package/dist/types/components/index.d.ts +3 -6
- package/dist/types/components/lightbox.d.ts +2 -2
- package/dist/types/components/{dropdown.d.ts → menu.d.ts} +48 -38
- package/dist/types/components/navigationRail.d.ts +53 -0
- package/dist/types/components/parallax.d.ts +2 -8
- package/dist/types/components/range.d.ts +5 -2
- package/dist/types/components/registry.d.ts +7 -19
- package/dist/types/components/scrollspy.d.ts +26 -50
- package/dist/types/components/select.d.ts +28 -14
- package/dist/types/components/sidenav.d.ts +40 -55
- package/dist/types/components/slider.d.ts +2 -2
- package/dist/types/components/{toasts.d.ts → snackbar.d.ts} +28 -25
- package/dist/types/components/tabs.d.ts +2 -2
- package/dist/types/components/timepicker.d.ts +2 -4
- package/dist/types/components/tooltip.d.ts +2 -2
- package/dist/types/core/component.d.ts +4 -4
- package/dist/types/index.d.ts +3 -1
- package/package.json +4 -3
- package/src/sass/README.md +13 -15
- package/src/sass/abstracts/_breakpoints.scss +14 -19
- package/src/sass/abstracts/_mixins.scss +3 -3
- package/src/sass/abstracts/_variables.scss +27 -23
- package/src/sass/base/_global.scss +1 -1
- package/src/sass/base/_grid.scss +64 -133
- package/src/sass/base/_index.scss +1 -1
- package/src/sass/components/_badges.scss +76 -61
- package/src/sass/components/_bottom-sheet.scss +137 -0
- package/src/sass/components/_buttons.scss +22 -54
- package/src/sass/components/_cards.scss +54 -127
- package/src/sass/components/_carousel.scss +4 -6
- package/src/sass/components/_chips.scss +6 -5
- package/src/sass/components/_datepicker.scss +1 -15
- 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 +68 -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 +138 -15
- package/src/sass/components/_parallax.scss +7 -28
- package/src/sass/components/_preloader.scss +145 -300
- package/src/sass/components/_scrollspy.scss +18 -0
- package/src/sass/components/_side-sheet.scss +193 -0
- package/src/sass/components/_sidenav.scss +281 -146
- package/src/sass/components/{_toast.scss → _snackbar.scss} +43 -43
- package/src/sass/components/_table_of_contents.scss +30 -14
- package/src/sass/components/_tabs.scss +43 -50
- package/src/sass/components/_toolbar.scss +93 -27
- package/src/sass/components/_tooltip.scss +5 -10
- package/src/sass/components/_transitions.scss +0 -5
- package/src/sass/components/forms/_fieldset.scss +1 -1
- package/src/sass/components/forms/_index.scss +0 -5
- package/src/sass/components/forms/_input-fields.scss +52 -55
- package/src/sass/components/forms/_range.scss +423 -98
- package/src/sass/components/forms/_select.scss +177 -60
- package/src/sass/components/forms/_switches.scss +21 -8
- 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/_helpers.scss +1 -3
- package/src/sass/utilities/_typescale.scss +0 -5
- package/src/sass/utilities/_visibility.scss +10 -2
- package/dist/types/components/collapsible.d.ts +0 -96
- package/dist/types/components/modal.d.ts +0 -45
- 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 -181
- package/src/sass/components/_collection.scss +0 -198
- package/src/sass/components/_dropdown.scss +0 -129
- package/src/sass/components/_modal.scss +0 -208
- package/src/sass/components/_pushpin.scss +0 -11
- package/src/sass/components/_tapTarget.scss +0 -191
|
@@ -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(.
|
|
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(.
|
|
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,10 @@ header:has(> nav) {
|
|
|
117
140
|
align-items: stretch;
|
|
118
141
|
}
|
|
119
142
|
|
|
120
|
-
|
|
143
|
+
// Direct destinations only. Menu JS parks the menu next to the
|
|
144
|
+
// trigger, so a descendant `a` would also paint a square hover on
|
|
145
|
+
// top of the menu's pill.
|
|
146
|
+
> li > a {
|
|
121
147
|
@extend %app-bar-destination;
|
|
122
148
|
}
|
|
123
149
|
}
|
|
@@ -125,7 +151,7 @@ header:has(> nav) {
|
|
|
125
151
|
// Breadcrumb trail. Layout only — colors stay on the breadcrumb tokens
|
|
126
152
|
// (which this header remaps to inherit). Do not treat the crumbs as
|
|
127
153
|
// destinations.
|
|
128
|
-
> nav:not(.tabs) > ol {
|
|
154
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > ol {
|
|
129
155
|
display: flex;
|
|
130
156
|
flex-wrap: wrap;
|
|
131
157
|
align-items: center;
|
|
@@ -137,7 +163,7 @@ header:has(> nav) {
|
|
|
137
163
|
padding-inline: 12px;
|
|
138
164
|
}
|
|
139
165
|
|
|
140
|
-
> nav:not(.tabs) > form {
|
|
166
|
+
> nav:not(.tabs, .navigation-bar, .navigation-rail) > form {
|
|
141
167
|
display: flex;
|
|
142
168
|
flex: 1 1 auto;
|
|
143
169
|
align-items: center;
|
|
@@ -168,25 +194,6 @@ header:has(> nav) {
|
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
196
|
}
|
|
171
|
-
|
|
172
|
-
// Secondary row (tabs sit under the bar, not inside it).
|
|
173
|
-
> .tabs {
|
|
174
|
-
background-color: transparent;
|
|
175
|
-
width: 100%;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
@include bp-up("large") {
|
|
179
|
-
// Beats the icon-button display:inline-flex (higher specificity).
|
|
180
|
-
// Same flag the visibility utilities use for breakpoint hides.
|
|
181
|
-
> nav:not(.tabs) > .sidenav-trigger {
|
|
182
|
-
display: none !important;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// The trigger is still :first-child, so the title needs the no-icon inset.
|
|
186
|
-
> nav:not(.tabs) > .sidenav-trigger + :is(h1, h2, h3, h4, h5, h6) {
|
|
187
|
-
padding-inline-start: 12px;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
197
|
}
|
|
191
198
|
|
|
192
199
|
%app-bar-destination {
|
|
@@ -213,7 +220,7 @@ header:has(> nav) {
|
|
|
213
220
|
|
|
214
221
|
// Center-aligned small bar. The headline is taken out of flow so leading and
|
|
215
222
|
// trailing actions can sit on the edges without shifting the title.
|
|
216
|
-
header.center:has(> nav) > nav:not(.tabs) {
|
|
223
|
+
header.center:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
217
224
|
position: relative;
|
|
218
225
|
justify-content: space-between;
|
|
219
226
|
|
|
@@ -229,12 +236,18 @@ header.center:has(> nav) > nav:not(.tabs) {
|
|
|
229
236
|
}
|
|
230
237
|
}
|
|
231
238
|
|
|
232
|
-
// Medium (112dp) and large (152dp): same markup, title
|
|
233
|
-
|
|
234
|
-
|
|
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) {
|
|
235
246
|
flex-wrap: wrap;
|
|
236
|
-
align-content:
|
|
247
|
+
align-content: space-between;
|
|
248
|
+
align-items: flex-start;
|
|
237
249
|
height: auto;
|
|
250
|
+
padding-top: 4px;
|
|
238
251
|
|
|
239
252
|
> :is(h1, h2, h3, h4, h5, h6) {
|
|
240
253
|
order: 1;
|
|
@@ -243,9 +256,13 @@ header.large:has(> nav) > nav:not(.tabs) {
|
|
|
243
256
|
overflow: visible;
|
|
244
257
|
text-overflow: unset;
|
|
245
258
|
}
|
|
259
|
+
|
|
260
|
+
> :is(h1, h2, h3, h4, h5, h6) + * {
|
|
261
|
+
margin-inline-start: auto;
|
|
262
|
+
}
|
|
246
263
|
}
|
|
247
264
|
|
|
248
|
-
header.medium:has(> nav) > nav:not(.tabs) {
|
|
265
|
+
header.medium:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
249
266
|
min-height: 112px;
|
|
250
267
|
padding-bottom: 16px;
|
|
251
268
|
|
|
@@ -259,7 +276,7 @@ header.medium:has(> nav) > nav:not(.tabs) {
|
|
|
259
276
|
}
|
|
260
277
|
}
|
|
261
278
|
|
|
262
|
-
header.large:has(> nav) > nav:not(.tabs) {
|
|
279
|
+
header.large:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
263
280
|
min-height: 152px;
|
|
264
281
|
padding-bottom: 28px;
|
|
265
282
|
|
|
@@ -273,9 +290,12 @@ header.large:has(> nav) > nav:not(.tabs) {
|
|
|
273
290
|
}
|
|
274
291
|
}
|
|
275
292
|
|
|
276
|
-
// Pin the bar.
|
|
277
|
-
//
|
|
278
|
-
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)) {
|
|
279
299
|
position: sticky;
|
|
280
300
|
top: 0;
|
|
281
301
|
z-index: 997;
|
|
@@ -286,75 +306,6 @@ header.fixed:has(> nav) {
|
|
|
286
306
|
|
|
287
307
|
@keyframes top-app-bar-scroll {
|
|
288
308
|
to {
|
|
289
|
-
|
|
290
|
-
0 1px 10px 0 rgba(0, 0, 0, 0.12),
|
|
291
|
-
0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Bottom app bar. 80dp row, icons on the start, a FAB (any .btn) pushed to the end.
|
|
296
|
-
footer:not(.page-footer):has(> nav) {
|
|
297
|
-
--md-comp-bottom-app-bar-container-color: var(--md-sys-color-surface);
|
|
298
|
-
--md-comp-bottom-app-bar-container-height: 80px;
|
|
299
|
-
|
|
300
|
-
background-color: var(--md-comp-bottom-app-bar-container-color);
|
|
301
|
-
color: var(--md-sys-color-on-surface);
|
|
302
|
-
width: 100%;
|
|
303
|
-
@include z-depth("2");
|
|
304
|
-
|
|
305
|
-
> nav:not(.tabs) {
|
|
306
|
-
display: flex;
|
|
307
|
-
align-items: center;
|
|
308
|
-
box-sizing: border-box;
|
|
309
|
-
width: 100%;
|
|
310
|
-
min-height: var(--md-comp-bottom-app-bar-container-height);
|
|
311
|
-
padding: 12px 4px;
|
|
312
|
-
margin: 0;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
> nav:not(.tabs) > :is(a, button):not(.btn):not(.button):not(.btn-floating):not(.btn-large):not(.btn-small):has(> i:only-child) {
|
|
316
|
-
display: inline-flex;
|
|
317
|
-
flex: 0 0 auto;
|
|
318
|
-
align-items: center;
|
|
319
|
-
justify-content: center;
|
|
320
|
-
width: 48px;
|
|
321
|
-
height: 48px;
|
|
322
|
-
padding: 0;
|
|
323
|
-
margin: 0;
|
|
324
|
-
border: 0;
|
|
325
|
-
border-radius: 50%;
|
|
326
|
-
background: transparent;
|
|
327
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
328
|
-
cursor: pointer;
|
|
329
|
-
appearance: none;
|
|
330
|
-
font: inherit;
|
|
331
|
-
text-decoration: none;
|
|
332
|
-
@include focus-visible;
|
|
333
|
-
|
|
334
|
-
> i {
|
|
335
|
-
display: block;
|
|
336
|
-
font-size: 24px;
|
|
337
|
-
width: 24px;
|
|
338
|
-
height: 24px;
|
|
339
|
-
line-height: 24px;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
&:hover {
|
|
343
|
-
background-color: color-mix(in oklab, currentColor 8%, transparent);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
&:active {
|
|
347
|
-
background-color: color-mix(in oklab, currentColor 10%, transparent);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
> nav:not(.tabs) > :is(.btn, .btn-floating, .btn-large) {
|
|
352
|
-
margin-inline-start: auto;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
&.fixed {
|
|
356
|
-
position: sticky;
|
|
357
|
-
bottom: 0;
|
|
358
|
-
z-index: 997;
|
|
309
|
+
--md-comp-top-app-bar-scroll: 1;
|
|
359
310
|
}
|
|
360
311
|
}
|