@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.
Files changed (105) hide show
  1. package/CHANGELOG.md +1266 -1
  2. package/README.md +14 -11
  3. package/dist/css/expressive.css +4215 -1668
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/js/expressive.cjs +1231 -1253
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +1231 -1253
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +12 -12
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +1231 -1253
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  16. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  17. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  18. package/dist/types/components/autocomplete.d.ts +15 -3
  19. package/dist/types/components/buttons.d.ts +5 -1
  20. package/dist/types/components/carousel.d.ts +87 -53
  21. package/dist/types/components/chips.d.ts +21 -1
  22. package/dist/types/components/expandingCard.d.ts +31 -0
  23. package/dist/types/components/index.d.ts +21 -20
  24. package/dist/types/components/lightbox.d.ts +1 -0
  25. package/dist/types/components/menu.d.ts +7 -1
  26. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  27. package/dist/types/components/registry.d.ts +8 -8
  28. package/dist/types/components/slider.d.ts +26 -85
  29. package/dist/types/components/snackbar.d.ts +20 -5
  30. package/dist/types/components/tabs.d.ts +12 -0
  31. package/dist/types/components/timepicker.d.ts +12 -9
  32. package/dist/types/core/utils.d.ts +64 -53
  33. package/dist/types/index.d.ts +8 -9
  34. package/package.json +15 -1
  35. package/src/sass/README.md +61 -11
  36. package/src/sass/abstracts/_breakpoints.scss +17 -19
  37. package/src/sass/abstracts/_mixins.scss +103 -21
  38. package/src/sass/abstracts/_variables.scss +50 -5
  39. package/src/sass/base/_global.scss +6 -24
  40. package/src/sass/base/_grid.scss +15 -12
  41. package/src/sass/base/_normalize.scss +34 -38
  42. package/src/sass/base/_typography.scss +4 -18
  43. package/src/sass/components/_badges.scss +2 -3
  44. package/src/sass/components/_banners.scss +298 -0
  45. package/src/sass/components/_bottom-app-bar.scss +180 -0
  46. package/src/sass/components/_bottom-sheet.scss +12 -4
  47. package/src/sass/components/_button-groups.scss +186 -0
  48. package/src/sass/components/_buttons.scss +245 -92
  49. package/src/sass/components/_cards.scss +494 -73
  50. package/src/sass/components/_carousel.scss +562 -68
  51. package/src/sass/components/_chips.scss +64 -5
  52. package/src/sass/components/_datepicker.scss +24 -24
  53. package/src/sass/components/_dialog.scss +17 -11
  54. package/src/sass/components/_drag-handle.scss +130 -0
  55. package/src/sass/components/_expanding-card.scss +260 -0
  56. package/src/sass/components/_fab-menu.scss +232 -0
  57. package/src/sass/components/_floating-sheet.scss +86 -0
  58. package/src/sass/components/_icon-buttons.scss +194 -0
  59. package/src/sass/components/_icons-material-design.scss +16 -4
  60. package/src/sass/components/_index.scss +22 -6
  61. package/src/sass/components/_list.scss +16 -13
  62. package/src/sass/components/_loading-indicator.scss +106 -0
  63. package/src/sass/components/_menu.scss +335 -61
  64. package/src/sass/components/_navbar.scss +71 -24
  65. package/src/sass/components/_navigation-bar.scss +13 -10
  66. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  67. package/src/sass/components/_navigation-rail.scss +22 -22
  68. package/src/sass/components/_page-footer.scss +19 -16
  69. package/src/sass/components/_pagination.scss +24 -6
  70. package/src/sass/components/_panes.scss +52 -11
  71. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  72. package/src/sass/components/_scrim.scss +37 -0
  73. package/src/sass/components/_search.scss +259 -0
  74. package/src/sass/components/_segmented-buttons.scss +188 -0
  75. package/src/sass/components/_side-sheet.scss +2 -2
  76. package/src/sass/components/_snackbar.scss +20 -15
  77. package/src/sass/components/_split-button.scss +196 -0
  78. package/src/sass/components/_tabs.scss +7 -8
  79. package/src/sass/components/_timepicker.scss +67 -58
  80. package/src/sass/components/_toolbar.scss +206 -80
  81. package/src/sass/components/forms/_checkboxes.scss +49 -12
  82. package/src/sass/components/forms/_fieldset.scss +4 -4
  83. package/src/sass/components/forms/_file-input.scss +8 -1
  84. package/src/sass/components/forms/_index.scss +1 -1
  85. package/src/sass/components/forms/_input-fields.scss +50 -44
  86. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  87. package/src/sass/components/forms/_select.scss +0 -1
  88. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  89. package/src/sass/components/forms/_switches.scss +6 -7
  90. package/src/sass/tokens/_index.scss +4 -1
  91. package/src/sass/tokens/_reference.scss +70 -61
  92. package/src/sass/tokens/_state.scss +24 -0
  93. package/src/sass/tokens/_theme.scss +6 -3
  94. package/src/sass/tokens/_vibrant.scss +76 -0
  95. package/src/sass/utilities/_colors.scss +14 -1
  96. package/src/sass/utilities/_helpers.scss +5 -5
  97. package/src/sass/utilities/_visibility.scss +48 -26
  98. package/dist/types/behaviors/waves.d.ts +0 -15
  99. package/dist/types/components/parallax.d.ts +0 -16
  100. package/dist/types/components/range.d.ts +0 -43
  101. package/dist/types/core/bounding.d.ts +0 -6
  102. package/dist/types/core/edges.d.ts +0 -6
  103. package/src/sass/components/_parallax.scss +0 -104
  104. package/src/sass/components/_pulse.scss +0 -37
  105. 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
+ }