@expressivecss/expressive 0.4.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +4 -9
  3. package/dist/css/expressive.css +2309 -948
  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 +643 -804
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +643 -804
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +11 -11
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +643 -804
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +18 -0
  16. package/dist/types/behaviors/sideSheets.d.ts +18 -0
  17. package/dist/types/components/autocomplete.d.ts +11 -11
  18. package/dist/types/components/index.d.ts +3 -5
  19. package/dist/types/components/{dropdown.d.ts → menu.d.ts} +38 -37
  20. package/dist/types/components/navigationRail.d.ts +53 -0
  21. package/dist/types/components/range.d.ts +3 -0
  22. package/dist/types/components/registry.d.ts +7 -15
  23. package/dist/types/components/select.d.ts +12 -12
  24. package/dist/types/components/{toasts.d.ts → snackbar.d.ts} +28 -25
  25. package/dist/types/index.d.ts +3 -1
  26. package/package.json +3 -2
  27. package/src/sass/README.md +13 -11
  28. package/src/sass/abstracts/_breakpoints.scss +14 -3
  29. package/src/sass/abstracts/_mixins.scss +3 -3
  30. package/src/sass/abstracts/_variables.scss +27 -2
  31. package/src/sass/base/_global.scss +1 -1
  32. package/src/sass/base/_grid.scss +66 -116
  33. package/src/sass/base/_index.scss +1 -1
  34. package/src/sass/components/_badges.scss +76 -61
  35. package/src/sass/components/_bottom-sheet.scss +137 -0
  36. package/src/sass/components/_cards.scss +4 -4
  37. package/src/sass/components/_chips.scss +6 -5
  38. package/src/sass/components/_dialog.scss +268 -0
  39. package/src/sass/components/_index.scss +8 -7
  40. package/src/sass/components/_list.scss +253 -0
  41. package/src/sass/components/_menu.scss +341 -0
  42. package/src/sass/components/_navbar.scss +65 -117
  43. package/src/sass/components/_navigation-bar.scss +165 -0
  44. package/src/sass/components/_navigation-rail.scss +308 -0
  45. package/src/sass/components/_page-footer.scss +7 -3
  46. package/src/sass/components/_side-sheet.scss +193 -0
  47. package/src/sass/components/_sidenav.scss +78 -49
  48. package/src/sass/components/{_toast.scss → _snackbar.scss} +35 -34
  49. package/src/sass/components/_tabs.scss +1 -27
  50. package/src/sass/components/_toolbar.scss +91 -25
  51. package/src/sass/components/_tooltip.scss +1 -1
  52. package/src/sass/components/forms/_fieldset.scss +1 -1
  53. package/src/sass/components/forms/_input-fields.scss +3 -3
  54. package/src/sass/components/forms/_range.scss +423 -98
  55. package/src/sass/components/forms/_select.scss +19 -17
  56. package/src/sass/tokens/_reference.scss +37 -13
  57. package/src/sass/tokens/_theme.scss +5 -13
  58. package/src/sass/utilities/_colors.scss +16 -74
  59. package/src/sass/utilities/_visibility.scss +8 -0
  60. package/dist/types/components/collapsible.d.ts +0 -79
  61. package/dist/types/components/pushpin.d.ts +0 -51
  62. package/dist/types/components/tapTarget.d.ts +0 -57
  63. package/src/sass/components/_collapsible.scss +0 -179
  64. package/src/sass/components/_collection.scss +0 -180
  65. package/src/sass/components/_dropdown.scss +0 -225
  66. package/src/sass/components/_modal.scss +0 -190
  67. package/src/sass/components/_pushpin.scss +0 -11
  68. package/src/sass/components/_tapTarget.scss +0 -187
@@ -0,0 +1,165 @@
1
+ @use "../abstracts" as *;
2
+
3
+ // Navigation bar. Destinations, not the app bar (header:has(> nav)).
4
+ // Anatomy is the HTML: a nav.navigation-bar of <a> or <button> items.
5
+ // A leading <i> is the icon; the remaining text is the label.
6
+ // aria-current (or .active) is the selected destination.
7
+ //
8
+ // Two layouts (M3 Expressive):
9
+ // (default) stacked — icon above label, pill on the icon
10
+ // .horizontal icon beside label, pill around both (medium windows)
11
+ //
12
+ // 3–5 destinations of equal importance. They do not change per screen.
13
+ //
14
+ // Tokens:
15
+ // --md-comp-nav-bar-container-color
16
+ // --md-comp-nav-bar-container-height 64px
17
+ // --md-comp-nav-bar-icon-size 24px
18
+ // --md-comp-nav-bar-indicator-width 56px
19
+ // --md-comp-nav-bar-indicator-height 32px
20
+ // --md-comp-nav-bar-indicator-shape 16px / 100px horizontal
21
+ // --md-comp-nav-bar-active-indicator-color secondary-container
22
+ // --md-comp-nav-bar-active-color on-secondary-container
23
+ //
24
+ // Nest descendants inside this block.
25
+
26
+ .navigation-bar {
27
+ --md-comp-nav-bar-container-color: var(--md-sys-color-surface-container);
28
+ --md-comp-nav-bar-container-height: 64px;
29
+ --md-comp-nav-bar-icon-size: 24px;
30
+ --md-comp-nav-bar-indicator-width: 56px;
31
+ --md-comp-nav-bar-indicator-height: 32px;
32
+ --md-comp-nav-bar-indicator-shape: 16px;
33
+ --md-comp-nav-bar-active-indicator-color: var(--md-sys-color-secondary-container);
34
+ --md-comp-nav-bar-active-color: var(--md-sys-color-on-secondary-container);
35
+
36
+ box-sizing: border-box;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: space-evenly;
40
+ width: 100%;
41
+ min-height: var(--md-comp-nav-bar-container-height);
42
+ margin: 0;
43
+ padding: 6px 8px;
44
+ padding-bottom: max(6px, env(safe-area-inset-bottom, 0px));
45
+ background-color: var(--md-comp-nav-bar-container-color);
46
+ color: var(--md-sys-color-on-surface);
47
+
48
+ > :is(a, button) {
49
+ position: relative;
50
+ box-sizing: border-box;
51
+ display: flex;
52
+ flex: 1 1 0;
53
+ flex-direction: column;
54
+ align-items: center;
55
+ justify-content: center;
56
+ gap: 4px;
57
+ min-width: 0;
58
+ margin: 0;
59
+ padding: 4px 0 6px;
60
+ border: 0;
61
+ background: transparent;
62
+ color: var(--md-sys-color-on-surface-variant);
63
+ cursor: pointer;
64
+ appearance: none;
65
+ text-decoration: none;
66
+ @include typescale("label-medium");
67
+ @include focus-visible;
68
+
69
+ > i {
70
+ display: flex;
71
+ flex-shrink: 0;
72
+ align-items: center;
73
+ justify-content: center;
74
+ box-sizing: border-box;
75
+ width: var(--md-comp-nav-bar-indicator-width);
76
+ height: var(--md-comp-nav-bar-indicator-height);
77
+ border-radius: var(--md-comp-nav-bar-indicator-shape);
78
+ font-size: var(--md-comp-nav-bar-icon-size);
79
+ color: inherit;
80
+ transition: background-color 0.2s ease-out;
81
+ }
82
+
83
+ &:hover:not(.active, [aria-current]) > i {
84
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
85
+ }
86
+
87
+ &.active,
88
+ &[aria-current] {
89
+ color: var(--md-sys-color-on-surface);
90
+
91
+ > i {
92
+ background-color: var(--md-comp-nav-bar-active-indicator-color);
93
+ color: var(--md-comp-nav-bar-active-color);
94
+ --md-icon-fill: 1;
95
+ }
96
+ }
97
+
98
+ &.disabled,
99
+ &[aria-disabled="true"] {
100
+ pointer-events: none;
101
+ color: color-mix(in oklab, var(--md-sys-color-on-surface) 38%, transparent);
102
+ }
103
+
104
+ // Sibling badge. Child-of-icon is handled in _badges.scss.
105
+ > .badge {
106
+ position: absolute;
107
+ z-index: 1;
108
+ top: calc(4px + (var(--md-comp-nav-bar-indicator-height) - 24px) / 2);
109
+ left: calc(50% + 12px);
110
+ transform: translate(-50%, -50%);
111
+ }
112
+ }
113
+
114
+ &.horizontal {
115
+ --md-comp-nav-bar-indicator-shape: 100px;
116
+ min-height: 56px;
117
+
118
+ > :is(a, button) {
119
+ flex: 0 1 auto;
120
+ flex-direction: row;
121
+ gap: 8px;
122
+ min-height: 40px;
123
+ padding: 8px 16px;
124
+ border-radius: var(--md-comp-nav-bar-indicator-shape);
125
+
126
+ > i {
127
+ width: var(--md-comp-nav-bar-icon-size);
128
+ height: var(--md-comp-nav-bar-icon-size);
129
+ border-radius: 0;
130
+ }
131
+
132
+ > .badge {
133
+ top: calc(50% - 12px);
134
+ left: auto;
135
+ inset-inline-start: calc(16px + 24px);
136
+ transform: translate(-50%, -50%);
137
+ }
138
+
139
+ &:hover:not(.active, [aria-current]) {
140
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
141
+
142
+ > i {
143
+ background-color: transparent;
144
+ }
145
+ }
146
+
147
+ &.active,
148
+ &[aria-current] {
149
+ background-color: var(--md-comp-nav-bar-active-indicator-color);
150
+ color: var(--md-comp-nav-bar-active-color);
151
+
152
+ > i {
153
+ background-color: transparent;
154
+ color: inherit;
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ &.fixed {
161
+ position: sticky;
162
+ bottom: 0;
163
+ z-index: 997;
164
+ }
165
+ }
@@ -0,0 +1,308 @@
1
+ @use "../abstracts" as *;
2
+
3
+ // Navigation rail. Destinations on mid-size screens, not the bottom
4
+ // navigation bar and not the sidenav drawer.
5
+ //
6
+ // Anatomy is the HTML: a nav.navigation-rail. The first
7
+ // button:not(.button) is the menu toggle. A .button child is the
8
+ // optional FAB. Other <a>/<button> are destinations (leading <i>,
9
+ // remaining text is the label, optional .badge). A heading is a
10
+ // section header (expanded only). aria-current / .active is selected.
11
+ //
12
+ // .expanded widens the rail (icon+label on one row, extended FAB).
13
+ // Below `small`, .expanded is modal (scrim). .modal forces that
14
+ // overlay at every breakpoint. JS toggles .expanded from the menu
15
+ // button; the class is also valid set by the author.
16
+ //
17
+ // Tokens:
18
+ // --md-comp-nav-rail-collapsed-width 96px
19
+ // --md-comp-nav-rail-expanded-width 280px (clamped 220–360)
20
+ // --md-comp-nav-rail-icon-size 24px
21
+ // --md-comp-nav-rail-indicator-width 56px
22
+ // --md-comp-nav-rail-indicator-height 32px / 56px expanded
23
+ // --md-comp-nav-rail-fab-size 56px
24
+ //
25
+ // Nest descendants inside this block.
26
+
27
+ :root {
28
+ --md-comp-nav-rail-collapsed-width: 96px;
29
+ --md-comp-nav-rail-expanded-width: 280px;
30
+ }
31
+
32
+ .navigation-rail {
33
+ --md-comp-nav-rail-icon-size: 24px;
34
+ --md-comp-nav-rail-indicator-width: 56px;
35
+ --md-comp-nav-rail-indicator-height: 32px;
36
+ --md-comp-nav-rail-indicator-shape: 16px;
37
+ --md-comp-nav-rail-fab-size: 56px;
38
+ --md-comp-nav-rail-active-indicator-color: var(--md-sys-color-secondary-container);
39
+ --md-comp-nav-rail-active-color: var(--md-sys-color-on-secondary-container);
40
+ --md-comp-nav-rail-duration: 250ms;
41
+
42
+ box-sizing: border-box;
43
+ display: flex;
44
+ flex-direction: column;
45
+ align-items: center;
46
+ gap: 4px;
47
+ width: var(--md-comp-nav-rail-collapsed-width);
48
+ min-width: var(--md-comp-nav-rail-collapsed-width);
49
+ min-height: 100dvh;
50
+ margin: 0;
51
+ padding: 20px 8px 16px;
52
+ overflow-x: hidden;
53
+ overflow-y: auto;
54
+ overscroll-behavior: contain;
55
+ background-color: var(--md-sys-color-surface-container);
56
+ color: var(--md-sys-color-on-surface);
57
+ transition: width var(--md-comp-nav-rail-duration) ease,
58
+ min-width var(--md-comp-nav-rail-duration) ease,
59
+ padding var(--md-comp-nav-rail-duration) ease;
60
+
61
+ // Menu toggle.
62
+ > button:not(.button):first-child {
63
+ display: flex;
64
+ flex: 0 0 auto;
65
+ align-items: center;
66
+ justify-content: center;
67
+ width: var(--md-comp-nav-rail-indicator-width);
68
+ height: 48px;
69
+ margin: 0 0 8px;
70
+ padding: 0;
71
+ border: 0;
72
+ border-radius: var(--md-comp-nav-rail-indicator-shape);
73
+ background: transparent;
74
+ color: var(--md-sys-color-on-surface-variant);
75
+ cursor: pointer;
76
+ appearance: none;
77
+ @include focus-visible;
78
+
79
+ > i {
80
+ font-size: var(--md-comp-nav-rail-icon-size);
81
+ width: var(--md-comp-nav-rail-icon-size);
82
+ height: var(--md-comp-nav-rail-icon-size);
83
+ }
84
+
85
+ &:hover {
86
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
87
+ }
88
+ }
89
+
90
+ // Optional FAB. Collapsed: icon-only 56dp. Expanded: extended.
91
+ > :is(a, button).button {
92
+ display: inline-flex;
93
+ flex: 0 0 auto;
94
+ align-items: center;
95
+ justify-content: center;
96
+ gap: 8px;
97
+ box-sizing: border-box;
98
+ width: var(--md-comp-nav-rail-fab-size);
99
+ height: var(--md-comp-nav-rail-fab-size);
100
+ margin: 4px 0 16px;
101
+ padding: 0;
102
+ overflow: hidden;
103
+ border-radius: 16px;
104
+ color: var(--md-sys-color-on-primary-container);
105
+ background-color: var(--md-sys-color-primary-container);
106
+ text-decoration: none;
107
+
108
+ > :not(i) {
109
+ position: absolute;
110
+ width: 1px;
111
+ height: 1px;
112
+ overflow: hidden;
113
+ clip-path: inset(50%);
114
+ }
115
+ }
116
+
117
+ // Destinations.
118
+ > a:not(.button),
119
+ > button:not(.button):not(:first-child) {
120
+ position: relative;
121
+ display: flex;
122
+ flex: 0 0 auto;
123
+ flex-direction: column;
124
+ align-items: center;
125
+ justify-content: center;
126
+ gap: 4px;
127
+ box-sizing: border-box;
128
+ width: 100%;
129
+ margin: 0;
130
+ padding: 6px 0;
131
+ border: 0;
132
+ background: transparent;
133
+ color: var(--md-sys-color-on-surface-variant);
134
+ cursor: pointer;
135
+ appearance: none;
136
+ text-decoration: none;
137
+ text-align: center;
138
+ @include typescale("label-medium");
139
+ @include focus-visible;
140
+
141
+ > i {
142
+ display: flex;
143
+ flex-shrink: 0;
144
+ align-items: center;
145
+ justify-content: center;
146
+ box-sizing: border-box;
147
+ width: var(--md-comp-nav-rail-indicator-width);
148
+ height: var(--md-comp-nav-rail-indicator-height);
149
+ border-radius: var(--md-comp-nav-rail-indicator-shape);
150
+ font-size: var(--md-comp-nav-rail-icon-size);
151
+ color: inherit;
152
+ transition: background-color var(--md-comp-nav-rail-duration) ease;
153
+ }
154
+
155
+ &:hover:not(.active, [aria-current]) > i {
156
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
157
+ }
158
+
159
+ &.active,
160
+ &[aria-current] {
161
+ color: var(--md-sys-color-on-surface);
162
+
163
+ > i {
164
+ background-color: var(--md-comp-nav-rail-active-indicator-color);
165
+ color: var(--md-comp-nav-rail-active-color);
166
+ --md-icon-fill: 1;
167
+ }
168
+ }
169
+
170
+ // Sibling badge (icon + label + .badge). Child-of-icon is handled
171
+ // in _badges.scss. Sit on the upper trailing corner of the 24dp glyph.
172
+ > .badge {
173
+ position: absolute;
174
+ z-index: 1;
175
+ top: calc(6px + (var(--md-comp-nav-rail-indicator-height) - 24px) / 2);
176
+ left: calc(50% + 12px);
177
+ transform: translate(-50%, -50%);
178
+ }
179
+ }
180
+
181
+ > :is(h1, h2, h3, h4, h5, h6) {
182
+ display: none;
183
+ box-sizing: border-box;
184
+ width: 100%;
185
+ margin: 0;
186
+ padding: 12px 16px 8px;
187
+ color: var(--md-sys-color-on-surface-variant);
188
+ @include typescale("title-small");
189
+ }
190
+
191
+ &.expanded {
192
+ --md-comp-nav-rail-indicator-height: 56px;
193
+ --md-comp-nav-rail-indicator-shape: 28px;
194
+
195
+ align-items: stretch;
196
+ width: clamp(220px, var(--md-comp-nav-rail-expanded-width), 360px);
197
+ min-width: 220px;
198
+ padding-inline: 16px;
199
+
200
+ > button:not(.button):first-child {
201
+ justify-content: flex-start;
202
+ width: auto;
203
+ padding-inline: 16px;
204
+ }
205
+
206
+ > :is(a, button).button {
207
+ width: 100%;
208
+ justify-content: flex-start;
209
+ padding-inline: 16px 20px;
210
+ color: var(--md-sys-color-on-primary-container);
211
+ background-color: var(--md-sys-color-primary-container);
212
+
213
+ > :not(i) {
214
+ position: static;
215
+ width: auto;
216
+ height: auto;
217
+ overflow: visible;
218
+ clip-path: none;
219
+ @include typescale("label-large");
220
+ }
221
+ }
222
+
223
+ > a:not(.button),
224
+ > button:not(.button):not(:first-child) {
225
+ flex-direction: row;
226
+ justify-content: flex-start;
227
+ gap: 12px;
228
+ min-height: var(--md-comp-nav-rail-indicator-height);
229
+ padding: 0 16px;
230
+ border-radius: var(--md-comp-nav-rail-indicator-shape);
231
+ text-align: start;
232
+
233
+ > i {
234
+ width: var(--md-comp-nav-rail-icon-size);
235
+ height: var(--md-comp-nav-rail-icon-size);
236
+ border-radius: 0;
237
+ }
238
+
239
+ &:hover:not(.active, [aria-current]) {
240
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
241
+
242
+ > i {
243
+ background-color: transparent;
244
+ }
245
+ }
246
+
247
+ &.active,
248
+ &[aria-current] {
249
+ background-color: var(--md-comp-nav-rail-active-indicator-color);
250
+ color: var(--md-comp-nav-rail-active-color);
251
+
252
+ > i {
253
+ background-color: transparent;
254
+ color: inherit;
255
+ }
256
+ }
257
+
258
+ > .badge {
259
+ top: calc((var(--md-comp-nav-rail-indicator-height) - 24px) / 2);
260
+ left: auto;
261
+ inset-inline-start: calc(16px + 24px);
262
+ transform: translate(-50%, -50%);
263
+ }
264
+ }
265
+
266
+ > :is(h1, h2, h3, h4, h5, h6) {
267
+ display: block;
268
+ }
269
+ }
270
+
271
+ &.expanded.modal {
272
+ position: fixed;
273
+ inset: 0 auto 0 0;
274
+ z-index: 998;
275
+ @include z-depth("1");
276
+
277
+ &::before {
278
+ content: "";
279
+ position: fixed;
280
+ inset: 0;
281
+ background-color: color-mix(in oklab, var(--md-sys-color-scrim) 32%, transparent);
282
+ z-index: -1;
283
+ }
284
+ }
285
+
286
+ @include bp-down("small") {
287
+ &.expanded:not(.modal) {
288
+ position: fixed;
289
+ inset: 0 auto 0 0;
290
+ z-index: 998;
291
+ @include z-depth("1");
292
+
293
+ &::before {
294
+ content: "";
295
+ position: fixed;
296
+ inset: 0;
297
+ background-color: color-mix(in oklab, var(--md-sys-color-scrim) 32%, transparent);
298
+ z-index: -1;
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ @media (prefers-reduced-motion: reduce) {
305
+ .navigation-rail {
306
+ transition: none;
307
+ }
308
+ }
@@ -4,8 +4,8 @@
4
4
  //
5
5
  // Anatomy is the HTML. A <footer> is the landmark. A heading is a column
6
6
  // title. A <nav> (or ul) is a link list. The last child <small> is the
7
- // legal bar. A footer whose only child is a <nav> is the bottom app bar,
8
- // not this component.
7
+ // legal bar. A footer whose only child is nav.navigation-bar is the
8
+ // navigation bar, not this component.
9
9
  //
10
10
  // Tokens:
11
11
  // --md-comp-footer-padding-block 3rem
@@ -16,7 +16,7 @@
16
16
  // Unfilled by default (dashed hairline). .filled uses surface-container.
17
17
  // There is no JavaScript.
18
18
 
19
- footer:not(:has(> nav:only-child)) {
19
+ footer:not(:has(> .navigation-bar:only-child)) {
20
20
  --md-comp-footer-padding-block: 3rem;
21
21
  --md-comp-footer-padding-inline: 1rem;
22
22
  --md-comp-footer-gap: 2rem;
@@ -80,6 +80,10 @@ footer:not(:has(> nav:only-child)):not(:has(> .container)) {
80
80
  @include bp-up("xlarge") {
81
81
  grid-template-columns: repeat(5, 1fr);
82
82
  }
83
+
84
+ @include bp-up("xxlarge") {
85
+ grid-template-columns: repeat(6, 1fr);
86
+ }
83
87
  }
84
88
 
85
89
  footer:not(:has(> nav:only-child)) > .container > .row,
@@ -0,0 +1,193 @@
1
+ @use "../abstracts" as *;
2
+
3
+ // Side sheets.
4
+ //
5
+ // Anatomy is the HTML. A dialog.side-sheet (or .right / .left) is the
6
+ // surface, docked to the trailing edge by default. showModal() is the
7
+ // modal variant (scrim, rounded inner corners). show() is the standard
8
+ // variant (no scrim, a divider on the inner edge, page stays interactive).
9
+ //
10
+ // A <header> is the top bar: an optional back button, a heading, and a
11
+ // close control (form[method=dialog] with an icon button). Body content
12
+ // is a <div> or <p>. A last-child form[method=dialog] is the action row.
13
+ //
14
+ // Tokens follow https://m3.material.io/components/side-sheets
15
+ // container surface-container-low, 400dp, full height
16
+ // standard coplanar, 1dp outline-variant rule on the inner edge
17
+ // modal 28dp inner corners, elevation 1, scrim
18
+ // header 64dp, title-large, 24dp inset, 48dp icon buttons
19
+ // actions 16dp pad, outline-variant rule above
20
+ //
21
+ // Standard is for large windows next to the main pane. Modal is for
22
+ // compact/medium, or whenever the sheet should interrupt.
23
+
24
+ $_sheet: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet";
25
+
26
+ #{$_sheet} {
27
+ --md-comp-side-sheet-container-color: var(--md-sys-color-surface-container-low);
28
+ --md-comp-side-sheet-container-shape: 28px;
29
+ --md-comp-side-sheet-container-width: 400px;
30
+ --md-comp-side-sheet-shift: 0px;
31
+
32
+ box-sizing: border-box;
33
+ position: fixed;
34
+ float: none !important;
35
+ inset-block: 0;
36
+ inset-inline-start: auto;
37
+ inset-inline-end: 0;
38
+ width: min(var(--md-comp-side-sheet-container-width), 85%);
39
+ max-width: var(--md-comp-side-sheet-container-width);
40
+ height: auto;
41
+ max-height: none;
42
+ min-width: 0;
43
+ min-height: 0;
44
+ margin: 0;
45
+ padding: 0;
46
+ border: none;
47
+ background-color: var(--md-comp-side-sheet-container-color);
48
+ color: var(--md-sys-color-on-surface);
49
+ outline: none;
50
+ translate: var(--md-comp-side-sheet-shift) 0;
51
+ transition: translate 280ms ease, border-radius 200ms ease;
52
+
53
+ &:not(:modal) {
54
+ z-index: 1100;
55
+ border-radius: 0;
56
+ box-shadow: none;
57
+ // Standard: attached to the page, inner edge is a divider.
58
+ border-inline-start: 1px solid var(--md-sys-color-outline-variant);
59
+ }
60
+
61
+ &:modal {
62
+ border-radius: var(--md-comp-side-sheet-container-shape) 0 0
63
+ var(--md-comp-side-sheet-container-shape);
64
+ @include z-depth("1");
65
+ }
66
+
67
+ &:not(:modal)::backdrop {
68
+ background: none;
69
+ backdrop-filter: none;
70
+ }
71
+
72
+ // Leading-edge dock.
73
+ &.left,
74
+ &.left-sheet,
75
+ &.start {
76
+ inset-inline-start: 0;
77
+ inset-inline-end: auto;
78
+
79
+ &:not(:modal) {
80
+ border-inline-start: 0;
81
+ border-inline-end: 1px solid var(--md-sys-color-outline-variant);
82
+ }
83
+
84
+ &:modal {
85
+ border-radius: 0 var(--md-comp-side-sheet-container-shape)
86
+ var(--md-comp-side-sheet-container-shape) 0;
87
+ }
88
+ }
89
+
90
+ > header {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 12px;
94
+ flex: 0 0 auto;
95
+ box-sizing: border-box;
96
+ min-height: 64px;
97
+ margin: 0;
98
+ padding: 8px 12px 8px 24px;
99
+ background: none;
100
+ box-shadow: none;
101
+ }
102
+
103
+ > header:has(> :is(button, a):first-child) {
104
+ padding-inline-start: 8px;
105
+ }
106
+
107
+ > header > :is(h1, h2, h3, h4, h5, h6) {
108
+ flex: 1 1 auto;
109
+ min-width: 0;
110
+ margin: 0;
111
+ padding: 0;
112
+ color: var(--md-sys-color-on-surface);
113
+ @include typescale("title-large");
114
+ }
115
+
116
+ > header > form {
117
+ display: flex;
118
+ flex: 0 0 auto;
119
+ align-items: center;
120
+ margin: 0;
121
+ padding: 0;
122
+ border: 0;
123
+ background: none;
124
+ box-shadow: none;
125
+ }
126
+
127
+ > header :is(button, a.button) {
128
+ flex: 0 0 48px;
129
+ box-sizing: border-box;
130
+ width: 48px;
131
+ height: 48px;
132
+ min-width: 48px;
133
+ padding: 0;
134
+ border: none;
135
+ border-radius: 50%;
136
+ background-color: transparent;
137
+ color: var(--md-sys-color-on-surface-variant);
138
+ box-shadow: none;
139
+
140
+ > i {
141
+ font-size: 24px;
142
+ width: 24px;
143
+ height: 24px;
144
+ line-height: 24px;
145
+ }
146
+
147
+ &:hover {
148
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
149
+ }
150
+
151
+ &:focus-visible {
152
+ outline: 3px solid var(--md-sys-color-secondary);
153
+ outline-offset: 2px;
154
+ }
155
+ }
156
+
157
+ > :is(form, nav):last-child {
158
+ justify-content: flex-start;
159
+ gap: 8px;
160
+ padding: 16px 24px;
161
+ border-block-start: 1px solid var(--md-sys-color-outline-variant);
162
+ }
163
+ }
164
+
165
+ // :is() so [open] applies to every alternative. `#{$_sheet}[open]` only
166
+ // suffixes the last selector and would paint a closed .side-sheet.
167
+ :is(#{$_sheet}):not([open]) {
168
+ display: none;
169
+ }
170
+
171
+ :is(#{$_sheet})[open] {
172
+ display: flex;
173
+ flex-direction: column;
174
+ }
175
+
176
+ @starting-style {
177
+ dialog.side-sheet[open],
178
+ dialog.right[open],
179
+ dialog.right-sheet[open] {
180
+ translate: 100% 0;
181
+ }
182
+
183
+ dialog.left[open],
184
+ dialog.left-sheet[open] {
185
+ translate: -100% 0;
186
+ }
187
+ }
188
+
189
+ @media (prefers-reduced-motion: reduce) {
190
+ #{$_sheet} {
191
+ transition: none;
192
+ }
193
+ }