@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
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
@use "../abstracts" as *;
|
|
2
|
+
|
|
3
|
+
// Expanding cards.
|
|
4
|
+
//
|
|
5
|
+
// A compact feed card morphs into a full-screen modal detail surface. The
|
|
6
|
+
// dialog begins clipped to the measured card bounds, then expands to the
|
|
7
|
+
// viewport while its hero media grows and the detail content fades in.
|
|
8
|
+
|
|
9
|
+
article.expanding-card {
|
|
10
|
+
--md-comp-expanding-card-container-color: var(--md-sys-color-surface);
|
|
11
|
+
--md-comp-expanding-card-expanded-shape: 0;
|
|
12
|
+
--md-comp-expanding-card-motion-duration: 500ms;
|
|
13
|
+
--md-comp-expanding-card-motion-easing: cubic-bezier(0.2, 0, 0, 1);
|
|
14
|
+
--md-comp-expanding-card-content-padding: 16px;
|
|
15
|
+
--md-comp-expanding-card-expanded-media-height: min(56dvh, 560px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
article.expanding-card > figure > .expanding-card-trigger {
|
|
19
|
+
position: absolute;
|
|
20
|
+
inset: 0;
|
|
21
|
+
z-index: 1;
|
|
22
|
+
display: block;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
min-width: 0;
|
|
26
|
+
min-height: 0;
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
background: none;
|
|
30
|
+
border: 0;
|
|
31
|
+
border-radius: inherit;
|
|
32
|
+
box-shadow: none;
|
|
33
|
+
color: transparent;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The trigger covers the media, so it carries the state layer itself, off the
|
|
38
|
+
// same `--md-comp-card-*` opacities the card's own layer reads.
|
|
39
|
+
article.expanding-card > figure > .expanding-card-trigger:hover {
|
|
40
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-hover-state-layer-opacity) * 100%), transparent);
|
|
41
|
+
box-shadow: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
article.expanding-card > figure > .expanding-card-trigger:focus-visible {
|
|
45
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-focus-state-layer-opacity) * 100%), transparent);
|
|
46
|
+
box-shadow: none;
|
|
47
|
+
outline: var(--md-comp-card-focus-indicator-thickness) solid var(--md-comp-card-focus-indicator-color);
|
|
48
|
+
outline-offset: calc(-1 * var(--md-comp-card-focus-indicator-thickness));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
article.expanding-card > figure > .expanding-card-trigger:active {
|
|
52
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-pressed-state-layer-opacity) * 100%), transparent);
|
|
53
|
+
box-shadow: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
article.expanding-card > header.expanding-card-summary {
|
|
57
|
+
padding-bottom: var(--md-comp-expanding-card-content-padding);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
dialog.expanding-card-dialog {
|
|
61
|
+
--md-comp-expanding-card-from-top: 0px;
|
|
62
|
+
--md-comp-expanding-card-from-right: 0px;
|
|
63
|
+
--md-comp-expanding-card-from-bottom: 0px;
|
|
64
|
+
--md-comp-expanding-card-from-left: 0px;
|
|
65
|
+
--md-comp-expanding-card-from-media-height: 0px;
|
|
66
|
+
|
|
67
|
+
position: fixed;
|
|
68
|
+
inset: 0;
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
width: 100%;
|
|
71
|
+
min-width: 0;
|
|
72
|
+
max-width: none;
|
|
73
|
+
height: 100dvh;
|
|
74
|
+
max-height: none;
|
|
75
|
+
margin: 0;
|
|
76
|
+
padding: 0;
|
|
77
|
+
overflow-y: auto;
|
|
78
|
+
overscroll-behavior: contain;
|
|
79
|
+
background-color: var(--md-comp-expanding-card-container-color);
|
|
80
|
+
color: var(--md-sys-color-on-surface);
|
|
81
|
+
border: 0;
|
|
82
|
+
border-radius: var(--md-comp-expanding-card-expanded-shape);
|
|
83
|
+
box-shadow: none;
|
|
84
|
+
clip-path: inset(
|
|
85
|
+
var(--md-comp-expanding-card-from-top)
|
|
86
|
+
var(--md-comp-expanding-card-from-right)
|
|
87
|
+
var(--md-comp-expanding-card-from-bottom)
|
|
88
|
+
var(--md-comp-expanding-card-from-left)
|
|
89
|
+
round var(--md-comp-card-container-shape)
|
|
90
|
+
);
|
|
91
|
+
transition: clip-path var(--md-comp-expanding-card-motion-duration) var(--md-comp-expanding-card-motion-easing);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
dialog.expanding-card-dialog[open] {
|
|
95
|
+
display: block;
|
|
96
|
+
overflow-y: auto;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
dialog.expanding-card-dialog::backdrop {
|
|
100
|
+
background: transparent;
|
|
101
|
+
backdrop-filter: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
dialog.expanding-card-dialog.expanded {
|
|
105
|
+
clip-path: inset(0 round var(--md-comp-expanding-card-expanded-shape));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
dialog.expanding-card-dialog > .expanding-card-hero {
|
|
109
|
+
position: relative;
|
|
110
|
+
box-sizing: border-box;
|
|
111
|
+
width: calc(100% - 16px);
|
|
112
|
+
height: var(--md-comp-expanding-card-from-media-height);
|
|
113
|
+
min-height: 0;
|
|
114
|
+
margin: 8px;
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
border-radius: var(--md-comp-card-media-shape);
|
|
117
|
+
transition: height var(--md-comp-expanding-card-motion-duration) var(--md-comp-expanding-card-motion-easing);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
dialog.expanding-card-dialog.expanded > .expanding-card-hero {
|
|
121
|
+
height: var(--md-comp-expanding-card-expanded-media-height);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
dialog.expanding-card-dialog > .expanding-card-hero > :is(img, picture),
|
|
125
|
+
dialog.expanding-card-dialog > .expanding-card-hero > picture > img {
|
|
126
|
+
display: block;
|
|
127
|
+
width: 100%;
|
|
128
|
+
height: 100%;
|
|
129
|
+
object-fit: cover;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
dialog.expanding-card-dialog > .expanding-card-close {
|
|
133
|
+
position: absolute;
|
|
134
|
+
inset-block-start: 16px;
|
|
135
|
+
inset-inline-start: 16px;
|
|
136
|
+
z-index: 2;
|
|
137
|
+
width: 48px;
|
|
138
|
+
height: 48px;
|
|
139
|
+
min-width: 48px;
|
|
140
|
+
margin: 0;
|
|
141
|
+
padding: 0;
|
|
142
|
+
color: var(--md-sys-color-on-surface);
|
|
143
|
+
background-color: var(--md-sys-color-surface);
|
|
144
|
+
border: 0;
|
|
145
|
+
border-radius: 50%;
|
|
146
|
+
box-shadow: none;
|
|
147
|
+
opacity: 0;
|
|
148
|
+
transform: scale(.8);
|
|
149
|
+
transition:
|
|
150
|
+
opacity 150ms ease-out,
|
|
151
|
+
transform 300ms var(--md-comp-expanding-card-motion-easing);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
dialog.expanding-card-dialog.expanded > .expanding-card-close {
|
|
155
|
+
opacity: 1;
|
|
156
|
+
transform: scale(1);
|
|
157
|
+
transition-delay: 150ms;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
dialog.expanding-card-dialog > .expanding-card-content {
|
|
161
|
+
box-sizing: border-box;
|
|
162
|
+
width: 100%;
|
|
163
|
+
min-height: 0;
|
|
164
|
+
margin: 0;
|
|
165
|
+
padding: 0 var(--md-comp-expanding-card-content-padding) 32px;
|
|
166
|
+
overflow: visible;
|
|
167
|
+
opacity: 0;
|
|
168
|
+
transform: translateY(16px);
|
|
169
|
+
transition:
|
|
170
|
+
opacity 150ms ease-out,
|
|
171
|
+
transform 300ms var(--md-comp-expanding-card-motion-easing);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
dialog.expanding-card-dialog.expanded > .expanding-card-content {
|
|
175
|
+
opacity: 1;
|
|
176
|
+
transform: translateY(0);
|
|
177
|
+
transition-delay: 180ms;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.expanding-card-detail-header {
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
justify-content: space-between;
|
|
184
|
+
gap: 16px;
|
|
185
|
+
margin: 0 0 8px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.expanding-card-detail-header > :is(h1, h2, h3, h4, h5, h6) {
|
|
189
|
+
flex: 1 1 auto;
|
|
190
|
+
min-width: 0;
|
|
191
|
+
margin: 0;
|
|
192
|
+
color: var(--md-sys-color-on-surface);
|
|
193
|
+
@include typescale("title-large");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.expanding-card-actions {
|
|
197
|
+
display: flex;
|
|
198
|
+
flex: 0 0 auto;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 8px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.expanding-card-actions > .expanding-card-favorite {
|
|
204
|
+
width: 48px;
|
|
205
|
+
height: 48px;
|
|
206
|
+
min-width: 48px;
|
|
207
|
+
padding: 0;
|
|
208
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
209
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
210
|
+
border-radius: 50%;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.expanding-card-actions > .expanding-card-play {
|
|
214
|
+
width: 88px;
|
|
215
|
+
height: 48px;
|
|
216
|
+
min-width: 88px;
|
|
217
|
+
padding: 0;
|
|
218
|
+
color: var(--md-sys-color-on-primary-container);
|
|
219
|
+
background-color: var(--md-sys-color-primary-container);
|
|
220
|
+
border-radius: 24px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.expanding-card-track {
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
226
|
+
gap: 2px 16px;
|
|
227
|
+
align-items: center;
|
|
228
|
+
padding-block: 8px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.expanding-card-track > :is(strong, span, small, time) {
|
|
232
|
+
margin: 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.expanding-card-track > strong {
|
|
236
|
+
color: var(--md-sys-color-on-surface);
|
|
237
|
+
@include typescale("body-large");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.expanding-card-track > small,
|
|
241
|
+
.expanding-card-track > time {
|
|
242
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
243
|
+
@include typescale("body-small");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.expanding-card-track > small {
|
|
247
|
+
grid-column: 1;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.expanding-card-track > time {
|
|
251
|
+
grid-column: 2;
|
|
252
|
+
grid-row: 1 / span 2;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@media (prefers-reduced-motion: reduce) {
|
|
256
|
+
dialog.expanding-card-dialog,
|
|
257
|
+
dialog.expanding-card-dialog > :is(.expanding-card-hero, .expanding-card-close, .expanding-card-content) {
|
|
258
|
+
transition: none;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
@use "../abstracts" as *;
|
|
2
|
+
|
|
3
|
+
// FAB menu. A FAB that expands into a list of labelled actions and morphs
|
|
4
|
+
// into a close button while it is open. `.fab-menu` is the host; `.active`
|
|
5
|
+
// and `aria-expanded` are the framework's, never the author's.
|
|
6
|
+
//
|
|
7
|
+
// It shares the FloatingActionButton class with the `.fab` speed dial next
|
|
8
|
+
// door and nothing else. The two never collide: `$_fab` is
|
|
9
|
+
// `:is(.fab, .fixed-action-btn)`, and a class selector matches whole class
|
|
10
|
+
// names, so `.fab` does not reach `.fab-menu`. The one name that looks shared
|
|
11
|
+
// is `--md-comp-fab-menu-gap`, which is the speed dial's own gap token and is
|
|
12
|
+
// deliberately not one of the names below.
|
|
13
|
+
//
|
|
14
|
+
// Tokens (M3 md.comp.fab-menu.*, DSP 34.0.21):
|
|
15
|
+
//
|
|
16
|
+
// close-button 56x56, icon 20dp, corner.full, elevation 3, 8dp above list
|
|
17
|
+
// menu-item 56dp tall, icon 24dp, 24dp insets, 8dp icon-label space,
|
|
18
|
+
// 4dp between items, corner.full, elevation 0, title-medium
|
|
19
|
+
//
|
|
20
|
+
// The colour axis is M3's three roles, and each one moves two pairs at once:
|
|
21
|
+
// the list item takes `<role>-container` / `on-<role>-container`, the close
|
|
22
|
+
// button the solid `<role>` / `on-<role>`. That pairing is M3's - the close
|
|
23
|
+
// button is deliberately the louder of the two.
|
|
24
|
+
$_fab-menu-roles: ("primary", "secondary", "tertiary");
|
|
25
|
+
|
|
26
|
+
.fab-menu {
|
|
27
|
+
--md-comp-fab-menu-close-button-container-height: 56px;
|
|
28
|
+
--md-comp-fab-menu-close-button-container-width: 56px;
|
|
29
|
+
--md-comp-fab-menu-close-button-icon-size: 20px;
|
|
30
|
+
--md-comp-fab-menu-close-button-between-space: 8px;
|
|
31
|
+
--md-comp-fab-menu-close-button-container-color: var(--md-sys-color-primary);
|
|
32
|
+
--md-comp-fab-menu-close-button-icon-color: var(--md-sys-color-on-primary);
|
|
33
|
+
--md-comp-fab-menu-menu-item-container-height: 56px;
|
|
34
|
+
--md-comp-fab-menu-menu-item-between-space: 4px;
|
|
35
|
+
--md-comp-fab-menu-menu-item-icon-size: 24px;
|
|
36
|
+
--md-comp-fab-menu-menu-item-icon-label-space: 8px;
|
|
37
|
+
--md-comp-fab-menu-menu-item-leading-space: 24px;
|
|
38
|
+
--md-comp-fab-menu-menu-item-trailing-space: 24px;
|
|
39
|
+
--md-comp-fab-menu-menu-item-container-color: var(
|
|
40
|
+
--md-sys-color-primary-container
|
|
41
|
+
);
|
|
42
|
+
--md-comp-fab-menu-menu-item-label-text-color: var(
|
|
43
|
+
--md-sys-color-on-primary-container
|
|
44
|
+
);
|
|
45
|
+
--md-comp-fab-menu-offset: 16px;
|
|
46
|
+
--md-comp-fab-menu-z: 997;
|
|
47
|
+
--md-comp-fab-menu-duration: 200ms;
|
|
48
|
+
--md-comp-fab-menu-stagger: 40ms;
|
|
49
|
+
--md-comp-fab-menu-easing: cubic-bezier(0.65, 0, 0.35, 1);
|
|
50
|
+
|
|
51
|
+
position: fixed;
|
|
52
|
+
inset-inline-end: var(--md-comp-fab-menu-offset);
|
|
53
|
+
bottom: var(--md-comp-fab-menu-offset);
|
|
54
|
+
z-index: var(--md-comp-fab-menu-z);
|
|
55
|
+
margin: 0;
|
|
56
|
+
|
|
57
|
+
// The trigger is the host's only in-flow child, so the host is exactly its
|
|
58
|
+
// size and the list can hang off `bottom: 100%`. Taking the list out of
|
|
59
|
+
// flow is what keeps a closed menu from reserving its own height.
|
|
60
|
+
> :is(a, button) {
|
|
61
|
+
position: relative;
|
|
62
|
+
z-index: 1;
|
|
63
|
+
|
|
64
|
+
// A second glyph in the trigger is the close button's, and only one of
|
|
65
|
+
// the two is ever in the box. With one glyph the morph is the whole
|
|
66
|
+
// affordance. `inline-block` is restated rather than reverted: the icon's
|
|
67
|
+
// own rule is in this layer too, so `revert-layer` would skip past it to
|
|
68
|
+
// the base default.
|
|
69
|
+
> #{$icon}:nth-child(2) {
|
|
70
|
+
display: none;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:is(ul, menu) {
|
|
75
|
+
position: absolute;
|
|
76
|
+
inset-inline-end: 0;
|
|
77
|
+
bottom: calc(100% + var(--md-comp-fab-menu-close-button-between-space));
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
align-items: flex-end;
|
|
81
|
+
gap: var(--md-comp-fab-menu-menu-item-between-space);
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding: 0;
|
|
84
|
+
list-style: none;
|
|
85
|
+
visibility: hidden;
|
|
86
|
+
transition: visibility 0s var(--md-comp-fab-menu-duration);
|
|
87
|
+
|
|
88
|
+
// Opening runs bottom-up, so the item nearest the FAB moves first. Past
|
|
89
|
+
// six the ladder runs out and the rest move together, which is where M3
|
|
90
|
+
// stops recommending actions anyway.
|
|
91
|
+
li:nth-last-child(1) { --i: 0; }
|
|
92
|
+
li:nth-last-child(2) { --i: 1; }
|
|
93
|
+
li:nth-last-child(3) { --i: 2; }
|
|
94
|
+
li:nth-last-child(4) { --i: 3; }
|
|
95
|
+
li:nth-last-child(5) { --i: 4; }
|
|
96
|
+
li:nth-last-child(6) { --i: 5; }
|
|
97
|
+
|
|
98
|
+
> li > :is(a, button) {
|
|
99
|
+
display: inline-flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: var(--md-comp-fab-menu-menu-item-icon-label-space);
|
|
102
|
+
height: var(--md-comp-fab-menu-menu-item-container-height);
|
|
103
|
+
padding-inline: var(--md-comp-fab-menu-menu-item-leading-space)
|
|
104
|
+
var(--md-comp-fab-menu-menu-item-trailing-space);
|
|
105
|
+
border: none;
|
|
106
|
+
border-radius: 9999px;
|
|
107
|
+
color: var(--md-comp-fab-menu-menu-item-label-text-color);
|
|
108
|
+
background-color: var(--md-comp-fab-menu-menu-item-container-color);
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
text-decoration: none;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
appearance: none;
|
|
113
|
+
opacity: 0;
|
|
114
|
+
transform: scale(.8) translateY(1rem);
|
|
115
|
+
transition:
|
|
116
|
+
opacity var(--md-comp-fab-menu-duration) var(--md-comp-fab-menu-easing),
|
|
117
|
+
transform var(--md-comp-fab-menu-duration) var(--md-comp-fab-menu-easing),
|
|
118
|
+
background-color .2s ease-out,
|
|
119
|
+
box-shadow .2s ease-out;
|
|
120
|
+
@include typescale("title-medium");
|
|
121
|
+
@include focus-visible;
|
|
122
|
+
|
|
123
|
+
> #{$icon} {
|
|
124
|
+
font-size: var(--md-comp-fab-menu-menu-item-icon-size);
|
|
125
|
+
width: var(--md-comp-fab-menu-menu-item-icon-size);
|
|
126
|
+
height: var(--md-comp-fab-menu-menu-item-icon-size);
|
|
127
|
+
line-height: var(--md-comp-fab-menu-menu-item-icon-size);
|
|
128
|
+
color: inherit;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Enabled is elevation 0; only the states lift.
|
|
132
|
+
&:hover {
|
|
133
|
+
@include z-depth("4");
|
|
134
|
+
background-color: color-mix(
|
|
135
|
+
in oklab,
|
|
136
|
+
var(--md-comp-fab-menu-menu-item-label-text-color) state-opacity("hover"),
|
|
137
|
+
var(--md-comp-fab-menu-menu-item-container-color)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&:active,
|
|
142
|
+
&:focus {
|
|
143
|
+
@include z-depth("3");
|
|
144
|
+
background-color: color-mix(
|
|
145
|
+
in oklab,
|
|
146
|
+
var(--md-comp-fab-menu-menu-item-label-text-color) state-opacity("focus"),
|
|
147
|
+
var(--md-comp-fab-menu-menu-item-container-color)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&.active :is(ul, menu) {
|
|
154
|
+
visibility: visible;
|
|
155
|
+
transition-delay: 0s;
|
|
156
|
+
|
|
157
|
+
> li > :is(a, button) {
|
|
158
|
+
opacity: 1;
|
|
159
|
+
transform: none;
|
|
160
|
+
transition-delay: calc(var(--md-comp-fab-menu-stagger) * var(--i, 0));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Open, the FAB is the close button: the same disc on corner.full, the
|
|
165
|
+
// solid role colour, and a 20dp glyph. Four of those are the FAB's own
|
|
166
|
+
// tokens, so retuning them is the whole morph - `.fab-menu.active` outweighs
|
|
167
|
+
// `:where(.button).extra.circle`, which contributes only `.extra.circle`.
|
|
168
|
+
&.active > :is(a, button) {
|
|
169
|
+
--md-comp-fab-container-height: var(
|
|
170
|
+
--md-comp-fab-menu-close-button-container-height
|
|
171
|
+
);
|
|
172
|
+
--md-comp-fab-container-width: var(
|
|
173
|
+
--md-comp-fab-menu-close-button-container-width
|
|
174
|
+
);
|
|
175
|
+
--md-comp-fab-container-shape: 9999px;
|
|
176
|
+
--md-comp-fab-icon-size: var(--md-comp-fab-menu-close-button-icon-size);
|
|
177
|
+
|
|
178
|
+
color: var(--md-comp-fab-menu-close-button-icon-color);
|
|
179
|
+
background-color: var(--md-comp-fab-menu-close-button-container-color);
|
|
180
|
+
|
|
181
|
+
> #{$icon}:first-child:not(:only-child) {
|
|
182
|
+
display: none;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
> #{$icon}:nth-child(2) {
|
|
186
|
+
display: inline-block;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
&:hover {
|
|
190
|
+
background-color: color-mix(
|
|
191
|
+
in oklab,
|
|
192
|
+
var(--md-comp-fab-menu-close-button-icon-color) state-opacity("hover"),
|
|
193
|
+
var(--md-comp-fab-menu-close-button-container-color)
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
&:active,
|
|
198
|
+
&:focus {
|
|
199
|
+
background-color: color-mix(
|
|
200
|
+
in oklab,
|
|
201
|
+
var(--md-comp-fab-menu-close-button-icon-color) state-opacity("focus"),
|
|
202
|
+
var(--md-comp-fab-menu-close-button-container-color)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// `.primary-container` is the default and is named anyway, so all three
|
|
208
|
+
// roles are a class a page can be written with.
|
|
209
|
+
@each $role in $_fab-menu-roles {
|
|
210
|
+
&.#{$role}-container {
|
|
211
|
+
--md-comp-fab-menu-menu-item-container-color: var(
|
|
212
|
+
--md-sys-color-#{$role}-container
|
|
213
|
+
);
|
|
214
|
+
--md-comp-fab-menu-menu-item-label-text-color: var(
|
|
215
|
+
--md-sys-color-on-#{$role}-container
|
|
216
|
+
);
|
|
217
|
+
--md-comp-fab-menu-close-button-container-color: var(
|
|
218
|
+
--md-sys-color-#{$role}
|
|
219
|
+
);
|
|
220
|
+
--md-comp-fab-menu-close-button-icon-color: var(
|
|
221
|
+
--md-sys-color-on-#{$role}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@media (prefers-reduced-motion: reduce) {
|
|
228
|
+
.fab-menu {
|
|
229
|
+
--md-comp-fab-menu-duration: 0s;
|
|
230
|
+
--md-comp-fab-menu-stagger: 0s;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@use "../abstracts" as *;
|
|
2
|
+
|
|
3
|
+
// Floating sheets.
|
|
4
|
+
//
|
|
5
|
+
// The third member of M3's sheet family (`md.comp.sheet.floating`). A
|
|
6
|
+
// dialog.floating-sheet is the surface, detached from every window edge
|
|
7
|
+
// instead of docked to one. showModal() is the modal variant (scrim,
|
|
8
|
+
// page inert); show() is the standard variant (no scrim, the page stays
|
|
9
|
+
// interactive and the sheet floats above it).
|
|
10
|
+
//
|
|
11
|
+
// It is a <dialog>, so every dialog slot already applies - heading, body,
|
|
12
|
+
// optional <hr> and extra content, and a last-child form[method=dialog]
|
|
13
|
+
// as the action row. Only the container differs, and the sheet states it
|
|
14
|
+
// by re-pointing the basic-dialog container tokens at its own.
|
|
15
|
+
//
|
|
16
|
+
// The token set is small on purpose: md.comp.sheet.floating publishes
|
|
17
|
+
// exactly three values (DSP 34.0.21) and nothing about placement.
|
|
18
|
+
// container surface-container-low, 28dp corners, elevation 1
|
|
19
|
+
// inset 24dp from every edge; the sheet centres in what is left
|
|
20
|
+
//
|
|
21
|
+
// There are deliberately no edge modifiers. `.bottom` and `.left` /
|
|
22
|
+
// `.right` already select a bottom or side sheet, so a floating sheet
|
|
23
|
+
// that borrowed those names would pick up two sheets' rules at once.
|
|
24
|
+
// Author placement with `inset` / `margin` when a corner is wanted.
|
|
25
|
+
|
|
26
|
+
dialog.floating-sheet {
|
|
27
|
+
--md-comp-floating-sheet-container-color: var(--md-sys-color-surface-container-low);
|
|
28
|
+
--md-comp-floating-sheet-container-shape: 28px;
|
|
29
|
+
--md-comp-floating-sheet-container-max-width: 400px;
|
|
30
|
+
--md-comp-floating-sheet-inset: 24px;
|
|
31
|
+
|
|
32
|
+
// The surface is the dialog's; the sheet only swaps what it owns.
|
|
33
|
+
--md-comp-basic-dialog-container-color: var(--md-comp-floating-sheet-container-color);
|
|
34
|
+
--md-comp-basic-dialog-container-shape: var(--md-comp-floating-sheet-container-shape);
|
|
35
|
+
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
position: fixed;
|
|
38
|
+
inset: var(--md-comp-floating-sheet-inset);
|
|
39
|
+
width: min(
|
|
40
|
+
var(--md-comp-floating-sheet-container-max-width),
|
|
41
|
+
calc(100% - 2 * var(--md-comp-floating-sheet-inset))
|
|
42
|
+
);
|
|
43
|
+
min-width: 0;
|
|
44
|
+
max-width: var(--md-comp-floating-sheet-container-max-width);
|
|
45
|
+
// `inset` sets both block edges, and an `auto` height between two fixed
|
|
46
|
+
// edges is *solved*, not shrink-wrapped - the sheet would fill the window
|
|
47
|
+
// whatever it holds. `fit-content` makes all three non-auto, which is the
|
|
48
|
+
// case where auto margins centre instead.
|
|
49
|
+
height: fit-content;
|
|
50
|
+
// A percentage max-height resolves against the containing block, which is
|
|
51
|
+
// the whole viewport - not the box `inset` leaves. At 100% a tall sheet
|
|
52
|
+
// grew to viewport height, went over-constrained, and auto margins split
|
|
53
|
+
// the overflow as -24px top and bottom: the inset disappeared exactly when
|
|
54
|
+
// the sheet was large enough to need it.
|
|
55
|
+
max-height: calc(100% - 2 * var(--md-comp-floating-sheet-inset));
|
|
56
|
+
margin: auto;
|
|
57
|
+
@include z-depth("1");
|
|
58
|
+
opacity: 1;
|
|
59
|
+
scale: 1;
|
|
60
|
+
transition: opacity 200ms ease, scale 200ms ease;
|
|
61
|
+
|
|
62
|
+
// Non-modal `show()` is not in the top layer - sit above page content
|
|
63
|
+
// and below snackbars (z-index 10000).
|
|
64
|
+
&:not(:modal) {
|
|
65
|
+
z-index: 1100;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Standard: no scrim. Modal keeps the basic-dialog backdrop.
|
|
69
|
+
&:not(:modal)::backdrop {
|
|
70
|
+
background: none;
|
|
71
|
+
backdrop-filter: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@starting-style {
|
|
76
|
+
dialog.floating-sheet[open] {
|
|
77
|
+
opacity: 0;
|
|
78
|
+
scale: 0.9;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
dialog.floating-sheet {
|
|
84
|
+
transition: none;
|
|
85
|
+
}
|
|
86
|
+
}
|