@expressivecss/expressive 0.7.0 → 0.8.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 +1266 -1
- package/README.md +14 -11
- package/dist/css/expressive.css +4215 -1668
- 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 +1231 -1253
- package/dist/js/expressive.cjs.map +4 -4
- package/dist/js/expressive.js +1231 -1253
- package/dist/js/expressive.js.map +4 -4
- package/dist/js/expressive.min.js +12 -12
- package/dist/js/expressive.min.js.map +4 -4
- package/dist/js/expressive.mjs +1231 -1253
- package/dist/js/expressive.mjs.map +4 -4
- package/dist/types/behaviors/bottomSheets.d.ts +19 -11
- package/dist/types/behaviors/sheetDrag.d.ts +40 -0
- package/dist/types/behaviors/sideSheets.d.ts +2 -9
- package/dist/types/components/autocomplete.d.ts +15 -3
- package/dist/types/components/buttons.d.ts +5 -1
- package/dist/types/components/carousel.d.ts +87 -53
- package/dist/types/components/chips.d.ts +21 -1
- package/dist/types/components/expandingCard.d.ts +31 -0
- package/dist/types/components/index.d.ts +21 -20
- package/dist/types/components/lightbox.d.ts +1 -0
- package/dist/types/components/menu.d.ts +7 -1
- package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
- package/dist/types/components/registry.d.ts +8 -8
- package/dist/types/components/slider.d.ts +26 -85
- package/dist/types/components/snackbar.d.ts +20 -5
- package/dist/types/components/tabs.d.ts +12 -0
- package/dist/types/components/timepicker.d.ts +12 -9
- package/dist/types/core/utils.d.ts +64 -53
- package/dist/types/index.d.ts +8 -9
- package/package.json +15 -1
- package/src/sass/README.md +61 -11
- package/src/sass/abstracts/_breakpoints.scss +17 -19
- package/src/sass/abstracts/_mixins.scss +103 -21
- package/src/sass/abstracts/_variables.scss +50 -5
- package/src/sass/base/_global.scss +6 -24
- package/src/sass/base/_grid.scss +15 -12
- package/src/sass/base/_normalize.scss +34 -38
- package/src/sass/base/_typography.scss +4 -18
- package/src/sass/components/_badges.scss +2 -3
- package/src/sass/components/_banners.scss +298 -0
- package/src/sass/components/_bottom-app-bar.scss +180 -0
- package/src/sass/components/_bottom-sheet.scss +12 -4
- package/src/sass/components/_button-groups.scss +186 -0
- package/src/sass/components/_buttons.scss +245 -92
- package/src/sass/components/_cards.scss +494 -73
- package/src/sass/components/_carousel.scss +562 -68
- package/src/sass/components/_chips.scss +64 -5
- package/src/sass/components/_datepicker.scss +24 -24
- package/src/sass/components/_dialog.scss +17 -11
- package/src/sass/components/_drag-handle.scss +130 -0
- package/src/sass/components/_expanding-card.scss +260 -0
- package/src/sass/components/_fab-menu.scss +232 -0
- package/src/sass/components/_floating-sheet.scss +86 -0
- package/src/sass/components/_icon-buttons.scss +194 -0
- package/src/sass/components/_icons-material-design.scss +16 -4
- package/src/sass/components/_index.scss +22 -6
- package/src/sass/components/_list.scss +16 -13
- package/src/sass/components/_loading-indicator.scss +106 -0
- package/src/sass/components/_menu.scss +335 -61
- package/src/sass/components/_navbar.scss +71 -24
- package/src/sass/components/_navigation-bar.scss +13 -10
- package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
- package/src/sass/components/_navigation-rail.scss +22 -22
- package/src/sass/components/_page-footer.scss +19 -16
- package/src/sass/components/_pagination.scss +24 -6
- package/src/sass/components/_panes.scss +52 -11
- package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
- package/src/sass/components/_scrim.scss +37 -0
- package/src/sass/components/_search.scss +259 -0
- package/src/sass/components/_segmented-buttons.scss +188 -0
- package/src/sass/components/_side-sheet.scss +2 -2
- package/src/sass/components/_snackbar.scss +20 -15
- package/src/sass/components/_split-button.scss +196 -0
- package/src/sass/components/_tabs.scss +7 -8
- package/src/sass/components/_timepicker.scss +67 -58
- package/src/sass/components/_toolbar.scss +206 -80
- package/src/sass/components/forms/_checkboxes.scss +49 -12
- package/src/sass/components/forms/_fieldset.scss +4 -4
- package/src/sass/components/forms/_file-input.scss +8 -1
- package/src/sass/components/forms/_index.scss +1 -1
- package/src/sass/components/forms/_input-fields.scss +50 -44
- package/src/sass/components/forms/_radio-buttons.scss +42 -10
- package/src/sass/components/forms/_select.scss +0 -1
- package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
- package/src/sass/components/forms/_switches.scss +6 -7
- package/src/sass/tokens/_index.scss +4 -1
- package/src/sass/tokens/_reference.scss +70 -61
- package/src/sass/tokens/_state.scss +24 -0
- package/src/sass/tokens/_theme.scss +6 -3
- package/src/sass/tokens/_vibrant.scss +76 -0
- package/src/sass/utilities/_colors.scss +14 -1
- package/src/sass/utilities/_helpers.scss +5 -5
- package/src/sass/utilities/_visibility.scss +48 -26
- package/dist/types/behaviors/waves.d.ts +0 -15
- package/dist/types/components/parallax.d.ts +0 -16
- package/dist/types/components/range.d.ts +0 -43
- package/dist/types/core/bounding.d.ts +0 -6
- package/dist/types/core/edges.d.ts +0 -6
- package/src/sass/components/_parallax.scss +0 -104
- package/src/sass/components/_pulse.scss +0 -37
- package/src/sass/components/_slider.scss +0 -129
|
@@ -12,8 +12,15 @@
|
|
|
12
12
|
// (https://m3.material.io/components/app-bars/specs):
|
|
13
13
|
// container surface, 64dp small / 112 medium / 152 large
|
|
14
14
|
// headline on-surface, title-large (small), headline-small/medium
|
|
15
|
-
// icons
|
|
16
|
-
//
|
|
15
|
+
// icons 24dp in a 48dp target, 4dp inset. The spec mutes the
|
|
16
|
+
// trailing ones (on-surface-variant) and leaves the leading
|
|
17
|
+
// one on-surface; both inherit here instead, so a fill +
|
|
18
|
+
// on-* pair on the header stays readable. The two tokens
|
|
19
|
+
// below are the opt-in to the spec colors.
|
|
20
|
+
// medium/large headline 16dp from the inline edges, 20dp (medium) /
|
|
21
|
+
// 28dp (large) above the container bottom
|
|
22
|
+
// on scroll surface-container fill (not elevation - the spec's
|
|
23
|
+
// level2 elevation carries a transparent shadow) so the bar
|
|
17
24
|
// separates from the body. A color utility on the
|
|
18
25
|
// header still wins — it sets background-color itself.
|
|
19
26
|
|
|
@@ -29,12 +36,25 @@
|
|
|
29
36
|
initial-value: 0;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
// The bar itself. A top app bar that holds destinations is a <nav>; one that
|
|
40
|
+
// holds only a title and controls - as the docs chrome does - is not
|
|
41
|
+
// navigation and says so with `.bar`, rather than claiming a landmark and
|
|
42
|
+
// then having nothing to put in it. Both look identical.
|
|
43
|
+
// Wrapped in :is() so it stays ONE compound selector. A bare comma-separated
|
|
44
|
+
// list breaks out of its context on interpolation: `header.medium > #{$_bar}`
|
|
45
|
+
// expanded to `header.medium > nav:not(...), .bar`, which styled every .bar on
|
|
46
|
+
// the page as an app bar. Same trap as $_icon in _icons-material-design.
|
|
47
|
+
// :is() also keeps the specificity where nav:not(...) had it.
|
|
48
|
+
$_bar: ":is(nav:not(.tabs, .navigation-bar, .navigation-rail), .bar)";
|
|
49
|
+
|
|
50
|
+
header:has(> :is(nav:not(.navigation-bar, .navigation-rail), .bar)) {
|
|
33
51
|
--md-comp-top-app-bar-container-color: var(--md-sys-color-surface);
|
|
34
52
|
--md-comp-top-app-bar-scrolled-container-color: var(--md-sys-color-surface-container);
|
|
35
53
|
--md-comp-top-app-bar-headline-color: var(--md-sys-color-on-surface);
|
|
36
|
-
// Inherit so a fill + on-* pair on the header recolors the icons.
|
|
37
|
-
//
|
|
54
|
+
// Inherit so a fill + on-* pair on the header recolors the icons. The spec
|
|
55
|
+
// splits them - leading on-surface, trailing on-surface-variant - and each
|
|
56
|
+
// token below is the opt-in for its own side. Leading is everything before
|
|
57
|
+
// the headline; trailing is everything after it.
|
|
38
58
|
--md-comp-top-app-bar-leading-icon-color: inherit;
|
|
39
59
|
--md-comp-top-app-bar-trailing-icon-color: inherit;
|
|
40
60
|
--md-comp-top-app-bar-container-height: 64px;
|
|
@@ -51,7 +71,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
51
71
|
border-radius: var(--md-comp-top-app-bar-container-shape);
|
|
52
72
|
width: 100%;
|
|
53
73
|
|
|
54
|
-
>
|
|
74
|
+
> #{$_bar} {
|
|
55
75
|
display: flex;
|
|
56
76
|
align-items: center;
|
|
57
77
|
box-sizing: border-box;
|
|
@@ -61,7 +81,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
61
81
|
margin: 0;
|
|
62
82
|
}
|
|
63
83
|
|
|
64
|
-
>
|
|
84
|
+
> #{$_bar} > :is(h1, h2, h3, h4, h5, h6) {
|
|
65
85
|
flex: 1 1 auto;
|
|
66
86
|
min-width: 0;
|
|
67
87
|
margin: 0;
|
|
@@ -83,7 +103,11 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
// Icon-only actions — 48dp target, 24dp glyph, 4dp from the container edge.
|
|
86
|
-
|
|
106
|
+
// $icon, not `i`: the canonical icon is a <span class="material-symbols">, and
|
|
107
|
+
// keying on the element meant every bar written the documented way lost this
|
|
108
|
+
// rule — the leading action fell through to the generic button pill and the
|
|
109
|
+
// trailing ones were picked up by the text-destination rule below.
|
|
110
|
+
> #{$_bar} > :is(a, button):not(.button, .icon-button):not(.circle):not(.extra):has(> #{$icon}:only-child) {
|
|
87
111
|
display: inline-flex;
|
|
88
112
|
flex: 0 0 auto;
|
|
89
113
|
align-items: center;
|
|
@@ -105,7 +129,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
105
129
|
text-decoration: none;
|
|
106
130
|
@include focus-visible;
|
|
107
131
|
|
|
108
|
-
>
|
|
132
|
+
> #{$icon} {
|
|
109
133
|
display: block;
|
|
110
134
|
font-size: var(--md-comp-top-app-bar-leading-icon-size);
|
|
111
135
|
width: var(--md-comp-top-app-bar-leading-icon-size);
|
|
@@ -122,12 +146,20 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
122
146
|
}
|
|
123
147
|
}
|
|
124
148
|
|
|
149
|
+
// Trailing actions are the ones that follow the headline - DOM order is the
|
|
150
|
+
// anatomy, here as everywhere else in this file. Without this the trailing
|
|
151
|
+
// token was declared and consumed by nothing, so the documented way to opt
|
|
152
|
+
// into the spec's muted trailing icons did nothing at all.
|
|
153
|
+
> #{$_bar} > :is(h1, h2, h3, h4, h5, h6) ~ :is(a, button):not(.button, .icon-button):not(.circle):not(.extra):has(> #{$icon}:only-child) {
|
|
154
|
+
color: var(--md-comp-top-app-bar-trailing-icon-color);
|
|
155
|
+
}
|
|
156
|
+
|
|
125
157
|
// Text destinations, either loose or grouped in a menu/list.
|
|
126
|
-
>
|
|
158
|
+
> #{$_bar} > a:not(:has(> #{$icon}:only-child)):not(.button, .icon-button):not(.circle):not(.breadcrumb) {
|
|
127
159
|
@extend %app-bar-destination;
|
|
128
160
|
}
|
|
129
161
|
|
|
130
|
-
>
|
|
162
|
+
> #{$_bar} > :is(menu, ul) {
|
|
131
163
|
display: flex;
|
|
132
164
|
align-items: stretch;
|
|
133
165
|
align-self: stretch;
|
|
@@ -151,7 +183,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
151
183
|
// Breadcrumb trail. Layout only — colors stay on the breadcrumb tokens
|
|
152
184
|
// (which this header remaps to inherit). Do not treat the crumbs as
|
|
153
185
|
// destinations.
|
|
154
|
-
>
|
|
186
|
+
> #{$_bar} > ol {
|
|
155
187
|
display: flex;
|
|
156
188
|
flex-wrap: wrap;
|
|
157
189
|
align-items: center;
|
|
@@ -163,7 +195,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
163
195
|
padding-inline: 12px;
|
|
164
196
|
}
|
|
165
197
|
|
|
166
|
-
>
|
|
198
|
+
> #{$_bar} > form {
|
|
167
199
|
display: flex;
|
|
168
200
|
flex: 1 1 auto;
|
|
169
201
|
align-items: center;
|
|
@@ -220,7 +252,7 @@ header:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
|
220
252
|
|
|
221
253
|
// Center-aligned small bar. The headline is taken out of flow so leading and
|
|
222
254
|
// trailing actions can sit on the edges without shifting the title.
|
|
223
|
-
header.center:has(>
|
|
255
|
+
header.center:has(> #{$_bar}) > #{$_bar} {
|
|
224
256
|
position: relative;
|
|
225
257
|
justify-content: space-between;
|
|
226
258
|
|
|
@@ -241,17 +273,32 @@ header.center:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
|
241
273
|
// so leading/trailing icons would otherwise pack together at the start.
|
|
242
274
|
// `space-between` parks the icon row at the top and the title at the
|
|
243
275
|
// bottom — that's what makes the 112/152 height readable.
|
|
244
|
-
header.medium:has(>
|
|
245
|
-
header.large:has(>
|
|
276
|
+
header.medium:has(> #{$_bar}) > #{$_bar},
|
|
277
|
+
header.large:has(> #{$_bar}) > #{$_bar} {
|
|
246
278
|
flex-wrap: wrap;
|
|
247
279
|
align-content: space-between;
|
|
248
280
|
align-items: flex-start;
|
|
249
281
|
height: auto;
|
|
250
|
-
|
|
282
|
+
// 8dp so the 48dp icon targets are centred in the 64dp top row, the
|
|
283
|
+
// same row the bar collapses to.
|
|
284
|
+
padding-top: 8px;
|
|
285
|
+
|
|
286
|
+
// The top row is 64dp whether or not it holds icons. Without this
|
|
287
|
+
// zero-width spacer a title-only bar has a single flex line and
|
|
288
|
+
// `space-between` pins it to the *top*, so the headline sits where the
|
|
289
|
+
// icons belong instead of on the bottom row.
|
|
290
|
+
&::before {
|
|
291
|
+
content: "";
|
|
292
|
+
flex: 0 0 0;
|
|
293
|
+
height: 48px;
|
|
294
|
+
}
|
|
251
295
|
|
|
252
296
|
> :is(h1, h2, h3, h4, h5, h6) {
|
|
253
297
|
order: 1;
|
|
254
|
-
|
|
298
|
+
// Wider than the line, so it always breaks onto its own one - a basis of
|
|
299
|
+
// exactly 100% still fits beside the zero-width spacer above, which put
|
|
300
|
+
// the headline back on the icon row. It shrinks to fit once there.
|
|
301
|
+
flex: 1 1 calc(100% + 1px);
|
|
255
302
|
white-space: normal;
|
|
256
303
|
overflow: visible;
|
|
257
304
|
text-overflow: unset;
|
|
@@ -262,12 +309,12 @@ header.large:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
|
262
309
|
}
|
|
263
310
|
}
|
|
264
311
|
|
|
265
|
-
header.medium:has(>
|
|
312
|
+
header.medium:has(> #{$_bar}) > #{$_bar} {
|
|
266
313
|
min-height: 112px;
|
|
267
|
-
padding-bottom:
|
|
314
|
+
padding-bottom: 20px;
|
|
268
315
|
|
|
269
316
|
> :is(h1, h2, h3, h4, h5, h6) {
|
|
270
|
-
padding:
|
|
317
|
+
padding: 0 12px;
|
|
271
318
|
font-family: var(--md-sys-typescale-headline-small-font-family-name);
|
|
272
319
|
font-size: var(--md-sys-typescale-headline-small-font-size);
|
|
273
320
|
font-weight: var(--md-sys-typescale-headline-small-font-weight);
|
|
@@ -276,12 +323,12 @@ header.medium:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
|
276
323
|
}
|
|
277
324
|
}
|
|
278
325
|
|
|
279
|
-
header.large:has(>
|
|
326
|
+
header.large:has(> #{$_bar}) > #{$_bar} {
|
|
280
327
|
min-height: 152px;
|
|
281
328
|
padding-bottom: 28px;
|
|
282
329
|
|
|
283
330
|
> :is(h1, h2, h3, h4, h5, h6) {
|
|
284
|
-
padding:
|
|
331
|
+
padding: 0 12px;
|
|
285
332
|
font-family: var(--md-sys-typescale-headline-medium-font-family-name);
|
|
286
333
|
font-size: var(--md-sys-typescale-headline-medium-font-size);
|
|
287
334
|
font-weight: var(--md-sys-typescale-headline-medium-font-weight);
|
|
@@ -295,7 +342,7 @@ header.large:has(> nav) > nav:not(.tabs, .navigation-bar, .navigation-rail) {
|
|
|
295
342
|
// progress; a utility class on the header sets background-color itself
|
|
296
343
|
// and is not overwritten. Without animation-timeline the bar stays at
|
|
297
344
|
// rest (surface), the spec default.
|
|
298
|
-
header.fixed:has(> nav:not(.navigation-bar, .navigation-rail)) {
|
|
345
|
+
header.fixed:has(> :is(nav:not(.navigation-bar, .navigation-rail), .bar)) {
|
|
299
346
|
position: sticky;
|
|
300
347
|
top: 0;
|
|
301
348
|
z-index: 997;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// --md-comp-nav-bar-container-color
|
|
16
16
|
// --md-comp-nav-bar-container-height 64px
|
|
17
17
|
// --md-comp-nav-bar-icon-size 24px
|
|
18
|
-
// --md-comp-nav-bar-indicator-width
|
|
18
|
+
// --md-comp-nav-bar-indicator-width 64px
|
|
19
19
|
// --md-comp-nav-bar-indicator-height 32px
|
|
20
20
|
// --md-comp-nav-bar-indicator-shape 16px / 100px horizontal
|
|
21
21
|
// --md-comp-nav-bar-active-indicator-color secondary-container
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
--md-comp-nav-bar-container-color: var(--md-sys-color-surface-container);
|
|
28
28
|
--md-comp-nav-bar-container-height: 64px;
|
|
29
29
|
--md-comp-nav-bar-icon-size: 24px;
|
|
30
|
-
|
|
30
|
+
// 64dp, not the rail's 56dp: md.comp.navigation-bar.active-indicator.width
|
|
31
|
+
// is 64 while md.comp.navigation-rail.active-indicator.width is 56. Both were
|
|
32
|
+
// 56 here, which is the rail's value copied onto the bar.
|
|
33
|
+
--md-comp-nav-bar-indicator-width: 64px;
|
|
31
34
|
--md-comp-nav-bar-indicator-height: 32px;
|
|
32
35
|
--md-comp-nav-bar-indicator-shape: 16px;
|
|
33
36
|
--md-comp-nav-bar-active-indicator-color: var(--md-sys-color-secondary-container);
|
|
@@ -66,7 +69,7 @@
|
|
|
66
69
|
@include typescale("label-medium");
|
|
67
70
|
@include focus-visible;
|
|
68
71
|
|
|
69
|
-
>
|
|
72
|
+
> #{$icon} {
|
|
70
73
|
display: flex;
|
|
71
74
|
flex-shrink: 0;
|
|
72
75
|
align-items: center;
|
|
@@ -80,15 +83,15 @@
|
|
|
80
83
|
transition: background-color 0.2s ease-out;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
&:hover:not(.active, [aria-current]) >
|
|
84
|
-
background-color: color-mix(in oklab, var(--md-sys-color-on-surface)
|
|
86
|
+
&:hover:not(.active, [aria-current]) > #{$icon} {
|
|
87
|
+
background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
&.active,
|
|
88
91
|
&[aria-current] {
|
|
89
92
|
color: var(--md-sys-color-on-surface);
|
|
90
93
|
|
|
91
|
-
>
|
|
94
|
+
> #{$icon} {
|
|
92
95
|
background-color: var(--md-comp-nav-bar-active-indicator-color);
|
|
93
96
|
color: var(--md-comp-nav-bar-active-color);
|
|
94
97
|
--md-icon-fill: 1;
|
|
@@ -123,7 +126,7 @@
|
|
|
123
126
|
padding: 8px 16px;
|
|
124
127
|
border-radius: var(--md-comp-nav-bar-indicator-shape);
|
|
125
128
|
|
|
126
|
-
>
|
|
129
|
+
> #{$icon} {
|
|
127
130
|
width: var(--md-comp-nav-bar-icon-size);
|
|
128
131
|
height: var(--md-comp-nav-bar-icon-size);
|
|
129
132
|
border-radius: 0;
|
|
@@ -137,9 +140,9 @@
|
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
&:hover:not(.active, [aria-current]) {
|
|
140
|
-
background-color: color-mix(in oklab, var(--md-sys-color-on-surface)
|
|
143
|
+
background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
|
|
141
144
|
|
|
142
|
-
>
|
|
145
|
+
> #{$icon} {
|
|
143
146
|
background-color: transparent;
|
|
144
147
|
}
|
|
145
148
|
}
|
|
@@ -149,7 +152,7 @@
|
|
|
149
152
|
background-color: var(--md-comp-nav-bar-active-indicator-color);
|
|
150
153
|
color: var(--md-comp-nav-bar-active-color);
|
|
151
154
|
|
|
152
|
-
>
|
|
155
|
+
> #{$icon} {
|
|
153
156
|
background-color: transparent;
|
|
154
157
|
color: inherit;
|
|
155
158
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Navigation drawer.
|
|
4
4
|
//
|
|
5
5
|
// Overlay is a modal <dialog> (JS wraps a <ul class="sidenav"> in
|
|
6
|
-
// dialog.sidenav-overlay, or the author writes <dialog class="sidenav">).
|
|
6
|
+
// dialog:is(.sidenav-overlay, .navigation-drawer-overlay), or the author writes <dialog class="sidenav">).
|
|
7
7
|
// showModal() puts it on the top layer; ::backdrop is the scrim; Escape
|
|
8
8
|
// and a scrim tap close it. Drag writes --md-comp-nav-drawer-shift;
|
|
9
9
|
// this file does the slide.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// accordion. The summary is a destination row with an expand_more
|
|
13
13
|
// chevron; there is no Collapsible plugin.
|
|
14
14
|
//
|
|
15
|
-
// .sidenav-fixed is a breakpoint, not a JS open(): at `large` the host
|
|
15
|
+
// :is(.sidenav-fixed, .navigation-drawer-fixed) is a breakpoint, not a JS open(): at `large` the host
|
|
16
16
|
// is display: contents so the list is a docked sidebar. Below that it
|
|
17
17
|
// is the same overlay drawer as any other sidenav.
|
|
18
18
|
//
|
|
@@ -26,13 +26,31 @@
|
|
|
26
26
|
// --md-comp-nav-drawer-font-size 14px
|
|
27
27
|
// --md-comp-nav-drawer-padding 16px
|
|
28
28
|
// --md-comp-nav-drawer-line-height item height
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// --md-comp-nav-drawer-user-view-shape
|
|
30
|
+
// 16px, bottom corners of the header
|
|
31
|
+
// --md-comp-nav-drawer-user-view-scrim
|
|
32
|
+
// gradient laid over the header image
|
|
33
|
+
// --md-comp-nav-drawer-user-view-ink-color
|
|
34
|
+
// text colour over that scrim
|
|
35
|
+
|
|
36
|
+
:root, :host {
|
|
31
37
|
--md-comp-nav-drawer-width: 300px;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
// A drawer row is whatever element the list item holds: a destination
|
|
41
|
+
// <a>, the <summary> of a nested section, the <button> a command row is
|
|
42
|
+
// written with (.navigation-drawer-close), and the plain element a
|
|
43
|
+
// .subheader label is. Keyed on `li > a`, the row layout disappeared the
|
|
44
|
+
// moment a row stopped being an anchor - the same trap $_icon documents.
|
|
45
|
+
// $_row-control is the subset that is operable: it takes the hover and
|
|
46
|
+
// focus treatment, a label does not. Both are wrapped in :is() - a bare
|
|
47
|
+
// comma list breaks out of its context (see $_bar in _navbar).
|
|
48
|
+
$_row: ":is(li > :is(a, button, .subheader), summary)";
|
|
49
|
+
$_row-control: ":is(li > :is(a, button):not(.button, .icon-button, .circle, .extra), summary)";
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
dialog:is(.sidenav-overlay, .navigation-drawer-overlay),
|
|
53
|
+
dialog:is(.sidenav, .navigation-drawer) {
|
|
36
54
|
--md-comp-nav-drawer-item-height: 48px;
|
|
37
55
|
--md-comp-nav-drawer-duration: 250ms;
|
|
38
56
|
--md-comp-nav-drawer-easing: ease;
|
|
@@ -41,6 +59,14 @@ dialog.sidenav {
|
|
|
41
59
|
--md-comp-nav-drawer-font-size: 14px;
|
|
42
60
|
--md-comp-nav-drawer-padding: 16px;
|
|
43
61
|
--md-comp-nav-drawer-line-height: var(--md-comp-nav-drawer-item-height);
|
|
62
|
+
--md-comp-nav-drawer-user-view-shape: 16px;
|
|
63
|
+
--md-comp-nav-drawer-user-view-ink-color: var(--md-ref-palette-neutral100);
|
|
64
|
+
--md-comp-nav-drawer-user-view-scrim: linear-gradient(
|
|
65
|
+
to bottom,
|
|
66
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 15%, transparent) 0%,
|
|
67
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 50%, transparent) 55%,
|
|
68
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 80%, transparent) 100%
|
|
69
|
+
);
|
|
44
70
|
|
|
45
71
|
box-sizing: border-box;
|
|
46
72
|
width: var(--md-comp-nav-drawer-width);
|
|
@@ -85,7 +111,7 @@ dialog.sidenav {
|
|
|
85
111
|
}
|
|
86
112
|
|
|
87
113
|
&::backdrop {
|
|
88
|
-
background-color:
|
|
114
|
+
background-color: var(--md-comp-scrim-color);
|
|
89
115
|
opacity: var(--md-comp-nav-drawer-open, 1);
|
|
90
116
|
backdrop-filter: none;
|
|
91
117
|
transition:
|
|
@@ -96,22 +122,22 @@ dialog.sidenav {
|
|
|
96
122
|
}
|
|
97
123
|
|
|
98
124
|
@starting-style {
|
|
99
|
-
:is(dialog.sidenav-overlay, dialog.sidenav):modal {
|
|
125
|
+
:is(dialog:is(.sidenav-overlay, .navigation-drawer-overlay), dialog:is(.sidenav, .navigation-drawer)):modal {
|
|
100
126
|
transform: translateX(-100%);
|
|
101
127
|
}
|
|
102
128
|
|
|
103
|
-
:is(dialog.sidenav-overlay, dialog.sidenav).right-aligned:modal {
|
|
129
|
+
:is(dialog:is(.sidenav-overlay, .navigation-drawer-overlay), dialog:is(.sidenav, .navigation-drawer)).right-aligned:modal {
|
|
104
130
|
transform: translateX(100%);
|
|
105
131
|
}
|
|
106
132
|
|
|
107
|
-
:is(dialog.sidenav-overlay, dialog.sidenav):modal::backdrop {
|
|
133
|
+
:is(dialog:is(.sidenav-overlay, .navigation-drawer-overlay), dialog:is(.sidenav, .navigation-drawer)):modal::backdrop {
|
|
108
134
|
opacity: 0;
|
|
109
135
|
}
|
|
110
136
|
}
|
|
111
137
|
|
|
112
138
|
@media (prefers-reduced-motion: reduce) {
|
|
113
|
-
dialog.sidenav-overlay,
|
|
114
|
-
dialog.sidenav {
|
|
139
|
+
dialog:is(.sidenav-overlay, .navigation-drawer-overlay),
|
|
140
|
+
dialog:is(.sidenav, .navigation-drawer) {
|
|
115
141
|
transition: none;
|
|
116
142
|
|
|
117
143
|
&::backdrop {
|
|
@@ -123,17 +149,25 @@ dialog.sidenav {
|
|
|
123
149
|
// Docked sidebar: the host must not be a box, or it would steal the
|
|
124
150
|
// list's position:fixed. Closed, so it is not :modal and does not
|
|
125
151
|
// lock the page.
|
|
126
|
-
@include bp-up("
|
|
127
|
-
dialog.sidenav-overlay:has(> .sidenav-fixed):not(:modal) {
|
|
152
|
+
@include bp-up("expanded") {
|
|
153
|
+
dialog:is(.sidenav-overlay, .navigation-drawer-overlay):has(> :is(.sidenav-fixed, .navigation-drawer-fixed)):not(:modal) {
|
|
128
154
|
display: contents;
|
|
129
155
|
}
|
|
130
156
|
}
|
|
131
157
|
|
|
132
|
-
.sidenav {
|
|
158
|
+
:is(.sidenav, .navigation-drawer) {
|
|
133
159
|
--md-comp-nav-drawer-item-height: 48px;
|
|
134
160
|
--md-comp-nav-drawer-font-size: 14px;
|
|
135
161
|
--md-comp-nav-drawer-padding: 16px;
|
|
136
162
|
--md-comp-nav-drawer-line-height: var(--md-comp-nav-drawer-item-height);
|
|
163
|
+
--md-comp-nav-drawer-user-view-shape: 16px;
|
|
164
|
+
--md-comp-nav-drawer-user-view-ink-color: var(--md-ref-palette-neutral100);
|
|
165
|
+
--md-comp-nav-drawer-user-view-scrim: linear-gradient(
|
|
166
|
+
to bottom,
|
|
167
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 15%, transparent) 0%,
|
|
168
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 50%, transparent) 55%,
|
|
169
|
+
color-mix(in oklab, var(--md-sys-color-scrim) 80%, transparent) 100%
|
|
170
|
+
);
|
|
137
171
|
|
|
138
172
|
box-sizing: border-box;
|
|
139
173
|
position: fixed;
|
|
@@ -162,12 +196,32 @@ dialog.sidenav {
|
|
|
162
196
|
align-content: center;
|
|
163
197
|
}
|
|
164
198
|
|
|
165
|
-
|
|
199
|
+
// The selected destination keeps its pill under the pointer: the row's own
|
|
200
|
+
// state layer mixes into `transparent`, so letting it reach a selected row
|
|
201
|
+
// would erase the fill rather than tint it. Higher specificity than the row
|
|
202
|
+
// rule, so order does not matter.
|
|
203
|
+
li.active > :is(a, button):not(.button, .icon-button, .circle, .extra) {
|
|
166
204
|
background-color: var(--md-sys-color-secondary-container);
|
|
167
205
|
color: var(--md-sys-color-on-secondary-container);
|
|
206
|
+
|
|
207
|
+
&:hover {
|
|
208
|
+
background-color: color-mix(
|
|
209
|
+
in oklab,
|
|
210
|
+
var(--md-sys-color-on-secondary-container) state-opacity("hover"),
|
|
211
|
+
var(--md-sys-color-secondary-container)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
&:active {
|
|
216
|
+
background-color: color-mix(
|
|
217
|
+
in oklab,
|
|
218
|
+
var(--md-sys-color-on-secondary-container) state-opacity("pressed"),
|
|
219
|
+
var(--md-sys-color-secondary-container)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
168
222
|
}
|
|
169
223
|
|
|
170
|
-
|
|
224
|
+
#{$_row} {
|
|
171
225
|
display: flex;
|
|
172
226
|
align-items: center;
|
|
173
227
|
margin: 0 12px;
|
|
@@ -191,25 +245,32 @@ dialog.sidenav {
|
|
|
191
245
|
}
|
|
192
246
|
}
|
|
193
247
|
|
|
194
|
-
|
|
195
|
-
summary {
|
|
248
|
+
#{$_row-control} {
|
|
196
249
|
color: var(--md-sys-color-on-surface-variant);
|
|
197
250
|
|
|
198
251
|
&:hover {
|
|
199
|
-
background-color: color-mix(in oklab, var(--md-sys-color-on-surface)
|
|
252
|
+
background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
&:active {
|
|
256
|
+
background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("pressed"), transparent);
|
|
200
257
|
}
|
|
201
258
|
|
|
202
259
|
@include focus-visible;
|
|
203
260
|
}
|
|
204
261
|
|
|
205
|
-
li > a.button,
|
|
206
|
-
li > a.circle,
|
|
207
|
-
li > a.extra {
|
|
262
|
+
li > :is(a, button):is(.button, .circle, .extra) {
|
|
208
263
|
margin: 10px 15px;
|
|
209
264
|
}
|
|
210
265
|
|
|
211
|
-
|
|
266
|
+
// Every <button> is a styled button ($_btn in _buttons is `button:not(...)`),
|
|
267
|
+
// so a command row has to be stripped back to a row the same way <summary>
|
|
268
|
+
// is. The chevron below stays summary-only.
|
|
269
|
+
:is(summary, li > button:not(.button, .icon-button, .circle, .extra)) {
|
|
212
270
|
box-sizing: border-box;
|
|
271
|
+
// A <button> centres its own content, so a command row's label sat in the
|
|
272
|
+
// middle of the row while every anchor row started at the inset.
|
|
273
|
+
justify-content: flex-start;
|
|
213
274
|
border: 0;
|
|
214
275
|
background: transparent;
|
|
215
276
|
font: inherit;
|
|
@@ -217,7 +278,9 @@ dialog.sidenav {
|
|
|
217
278
|
list-style: none;
|
|
218
279
|
cursor: pointer;
|
|
219
280
|
appearance: none;
|
|
281
|
+
}
|
|
220
282
|
|
|
283
|
+
summary {
|
|
221
284
|
&::-webkit-details-marker {
|
|
222
285
|
display: none;
|
|
223
286
|
}
|
|
@@ -282,16 +345,33 @@ dialog.sidenav {
|
|
|
282
345
|
height: auto;
|
|
283
346
|
padding: 0;
|
|
284
347
|
|
|
285
|
-
&:hover {
|
|
348
|
+
&:is(:hover, :active) {
|
|
286
349
|
background-color: transparent;
|
|
287
350
|
}
|
|
288
351
|
}
|
|
289
352
|
|
|
353
|
+
// The header image is clipped by .background, so the radius goes on the
|
|
354
|
+
// clipping box rather than the <img>: the image is a child of an
|
|
355
|
+
// overflow: hidden box, and rounding the child alone leaves the box's own
|
|
356
|
+
// square corners painting behind it.
|
|
290
357
|
.background {
|
|
291
358
|
overflow: hidden;
|
|
292
359
|
position: absolute;
|
|
293
360
|
inset: 0;
|
|
294
361
|
z-index: -1;
|
|
362
|
+
border-end-start-radius: var(--md-comp-nav-drawer-user-view-shape);
|
|
363
|
+
border-end-end-radius: var(--md-comp-nav-drawer-user-view-shape);
|
|
364
|
+
|
|
365
|
+
// The name and email sit on whatever photograph the author supplied, so
|
|
366
|
+
// the scrim is what makes them legible - bottom-weighted, because that
|
|
367
|
+
// is where the text is. It rides inside .background so the rounded
|
|
368
|
+
// corners clip it too.
|
|
369
|
+
&::after {
|
|
370
|
+
content: "";
|
|
371
|
+
position: absolute;
|
|
372
|
+
inset: 0;
|
|
373
|
+
background: var(--md-comp-nav-drawer-user-view-scrim);
|
|
374
|
+
}
|
|
295
375
|
}
|
|
296
376
|
|
|
297
377
|
.circle,
|
|
@@ -311,6 +391,17 @@ dialog.sidenav {
|
|
|
311
391
|
line-height: calc(var(--md-comp-nav-drawer-line-height) * 0.5);
|
|
312
392
|
}
|
|
313
393
|
|
|
394
|
+
// Over an image the surface roles mean nothing and the link colour these
|
|
395
|
+
// spans inherit is unreadable on a mid-tone photograph. Scoped to
|
|
396
|
+
// :has(> .background): with no image the header is on the drawer surface
|
|
397
|
+
// and must keep tracking the theme, where fixed light ink would vanish.
|
|
398
|
+
&:has(> .background) {
|
|
399
|
+
.name,
|
|
400
|
+
.email {
|
|
401
|
+
color: var(--md-comp-nav-drawer-user-view-ink-color);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
314
405
|
.name {
|
|
315
406
|
margin-top: 16px;
|
|
316
407
|
font-weight: 500;
|
|
@@ -324,10 +415,10 @@ dialog.sidenav {
|
|
|
324
415
|
}
|
|
325
416
|
|
|
326
417
|
// Overlay only: the dialog is the panel, so the list fills it. When the
|
|
327
|
-
// host is `display: contents` (a docked .sidenav-fixed) this must not
|
|
418
|
+
// host is `display: contents` (a docked :is(.sidenav-fixed, .navigation-drawer-fixed)) this must not
|
|
328
419
|
// apply — static positioning would drop the list into the header flow
|
|
329
420
|
// and it would read as part of the app bar.
|
|
330
|
-
dialog.sidenav-overlay:modal > .sidenav {
|
|
421
|
+
dialog:is(.sidenav-overlay, .navigation-drawer-overlay):modal > :is(.sidenav, .navigation-drawer) {
|
|
331
422
|
position: static;
|
|
332
423
|
inset: auto;
|
|
333
424
|
width: 100%;
|
|
@@ -338,7 +429,7 @@ dialog.sidenav-overlay:modal > .sidenav {
|
|
|
338
429
|
z-index: auto;
|
|
339
430
|
}
|
|
340
431
|
|
|
341
|
-
.sidenav.sidenav-fixed {
|
|
432
|
+
:is(.sidenav, .navigation-drawer):is(.sidenav-fixed, .navigation-drawer-fixed) {
|
|
342
433
|
transform: none;
|
|
343
434
|
|
|
344
435
|
&.right-aligned {
|
|
@@ -346,11 +437,11 @@ dialog.sidenav-overlay:modal > .sidenav {
|
|
|
346
437
|
}
|
|
347
438
|
}
|
|
348
439
|
|
|
349
|
-
@include bp-down("
|
|
440
|
+
@include bp-down("expanded") {
|
|
350
441
|
// Uninitialized / no-js: keep a fixed drawer off-canvas. Once JS wraps
|
|
351
442
|
// it, the host's display:none hides the closed drawer and this must
|
|
352
443
|
// not also hide the list inside an open dialog.
|
|
353
|
-
.sidenav.sidenav-fixed {
|
|
444
|
+
:is(.sidenav, .navigation-drawer):is(.sidenav-fixed, .navigation-drawer-fixed) {
|
|
354
445
|
transform: translateX(-100%);
|
|
355
446
|
|
|
356
447
|
&.right-aligned {
|
|
@@ -358,7 +449,7 @@ dialog.sidenav-overlay:modal > .sidenav {
|
|
|
358
449
|
}
|
|
359
450
|
}
|
|
360
451
|
|
|
361
|
-
.sidenav .user-view {
|
|
452
|
+
:is(.sidenav, .navigation-drawer) .user-view {
|
|
362
453
|
padding: var(--md-comp-nav-drawer-padding) var(--md-comp-nav-drawer-padding) 0;
|
|
363
454
|
}
|
|
364
455
|
}
|
|
@@ -376,11 +467,10 @@ dialog.sidenav-overlay:modal > .sidenav {
|
|
|
376
467
|
}
|
|
377
468
|
}
|
|
378
469
|
|
|
379
|
-
@include bp-up("
|
|
470
|
+
@include bp-up("expanded") {
|
|
380
471
|
.drag-target[data-fixed] {
|
|
381
472
|
display: none;
|
|
382
473
|
pointer-events: none;
|
|
383
474
|
}
|
|
384
475
|
}
|
|
385
476
|
|
|
386
|
-
|