@expressivecss/expressive 0.7.0-rc.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 +1311 -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
@@ -1,34 +1,223 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
- // Carousel. Two layouts, one class:
4
- // .carousel 3D coverflow (JS)
5
- // .carousel.flat linear snap track (CSS).
3
+ // Material 3 carousel.
6
4
  //
7
- // Anatomy: direct children move. .carousel-item is optional. Indicators and
8
- // .carousel-fixed-item are chrome, not slides.
5
+ // The default is multi-browse. Add one layout class for the other M3
6
+ // arrangements:
7
+ // .uncontained equal-width items, free scrolling
8
+ // .uncontained.multi-aspect intrinsic widths from each item's aspect ratio
9
+ // .hero one large item and one small preview
10
+ // .hero.center-aligned one centered large item and two small previews
11
+ // .full-screen vertical, edge-to-edge, mandatory snap
9
12
  //
10
- // Tokens:
11
- // --md-comp-carousel-height 400px (--carousel-height still wins)
12
- // --md-comp-carousel-shape 12px
13
- // --md-comp-carousel-gap 0
13
+ // `.flat` remains a full-width compatibility layout for swipeable tabs and
14
+ // older integrations.
15
+ //
16
+ // Material measurements:
17
+ // inline padding 16dp (0 for full-screen)
18
+ // block padding 8dp (0 for full-screen)
19
+ // item gap 8dp (16dp for full-screen)
20
+ // item shape 28dp
21
+ // small item width 40–56dp
14
22
 
15
23
  .carousel {
16
- --md-comp-carousel-height: 400px;
17
- --md-comp-carousel-shape: 12px;
18
- --md-comp-carousel-gap: 0px;
24
+ --md-comp-carousel-height: 240px;
25
+ --md-comp-carousel-shape: 28px;
26
+ --md-comp-carousel-pressed-shape: 20px;
27
+ --md-comp-carousel-gap: 8px;
28
+ --md-comp-carousel-inline-padding: 16px;
29
+ --md-comp-carousel-block-padding: 8px;
30
+ --md-comp-carousel-large-item-width: clamp(150px, 52%, 480px);
31
+ --md-comp-carousel-medium-item-width: clamp(
32
+ 64px,
33
+ calc(
34
+ 100% - var(--md-comp-carousel-large-item-width) - clamp(
35
+ var(--md-comp-carousel-small-item-min-width),
36
+ 12%,
37
+ var(--md-comp-carousel-small-item-max-width)
38
+ ) -
39
+ 16px
40
+ ),
41
+ 240px
42
+ );
43
+ --md-comp-carousel-small-item-min-width: 40px;
44
+ --md-comp-carousel-small-item-max-width: 56px;
45
+ --md-comp-carousel-uncontained-item-width: min(78%, 320px);
46
+ --md-comp-carousel-indicator-allowance: 40px;
47
+ --md-comp-carousel-state-layer-color: var(--md-sys-color-on-surface);
48
+ --md-comp-carousel-hover-state-layer-opacity: var(--md-sys-state-hover-state-layer-opacity);
49
+ --md-comp-carousel-focus-state-layer-opacity: var(--md-sys-state-focus-state-layer-opacity);
50
+ --md-comp-carousel-pressed-state-layer-opacity: var(--md-sys-state-pressed-state-layer-opacity);
51
+ --md-comp-carousel-disabled-opacity: 0.38;
52
+ --md-comp-carousel-focus-indicator-color: var(--md-sys-color-secondary);
53
+ --md-comp-carousel-focus-indicator-thickness: 3px;
54
+ --md-comp-carousel-focus-indicator-offset: 2px;
19
55
 
20
- overflow: hidden;
21
56
  position: relative;
22
57
  width: 100%;
23
58
  height: var(--carousel-height, var(--md-comp-carousel-height));
24
- border-radius: var(--md-comp-carousel-shape);
59
+ overflow: hidden;
60
+ background-color: var(--md-sys-color-surface);
61
+ color: var(--md-sys-color-on-surface);
25
62
 
26
- .carousel-item > img,
27
- .carousel-item > video {
28
- display: block;
63
+ > .carousel-track {
64
+ display: flex;
65
+ align-items: stretch;
66
+ gap: var(--md-comp-carousel-gap);
67
+ box-sizing: border-box;
29
68
  width: 100%;
30
69
  height: 100%;
31
- object-fit: cover;
70
+ margin: 0;
71
+ padding: var(--md-comp-carousel-block-padding)
72
+ var(--md-comp-carousel-inline-padding);
73
+ overflow-x: auto;
74
+ overflow-y: hidden;
75
+ scroll-padding-inline: var(--md-comp-carousel-inline-padding);
76
+ scroll-snap-type: x mandatory;
77
+ scroll-behavior: smooth;
78
+ overscroll-behavior-inline: contain;
79
+ touch-action: pan-x;
80
+ scrollbar-width: none;
81
+
82
+ &::-webkit-scrollbar {
83
+ display: none;
84
+ }
85
+
86
+ img,
87
+ video {
88
+ -webkit-user-drag: none;
89
+ }
90
+ }
91
+
92
+ .carousel-track > .carousel-item {
93
+ isolation: isolate;
94
+ flex: 0 0 var(--md-comp-carousel-large-item-width);
95
+ width: var(--md-comp-carousel-large-item-width);
96
+ min-width: 0;
97
+ height: auto;
98
+ position: relative;
99
+ overflow: hidden;
100
+ border-radius: var(--md-comp-carousel-shape);
101
+ scroll-snap-align: start;
102
+ scroll-snap-stop: always;
103
+ transition:
104
+ flex-basis 300ms cubic-bezier(0.2, 0, 0, 1),
105
+ width 300ms cubic-bezier(0.2, 0, 0, 1),
106
+ border-radius 200ms cubic-bezier(0.2, 0, 0, 1);
107
+
108
+ &[data-carousel-size="medium"] {
109
+ flex-basis: var(--md-comp-carousel-medium-item-width);
110
+ width: var(--md-comp-carousel-medium-item-width);
111
+ }
112
+
113
+ &[data-carousel-size="small"] {
114
+ flex-basis: clamp(
115
+ var(--md-comp-carousel-small-item-min-width),
116
+ 12%,
117
+ var(--md-comp-carousel-small-item-max-width)
118
+ );
119
+ width: clamp(
120
+ var(--md-comp-carousel-small-item-min-width),
121
+ 12%,
122
+ var(--md-comp-carousel-small-item-max-width)
123
+ );
124
+ }
125
+
126
+ &[data-carousel-size="small"] > .carousel-item-content {
127
+ display: none;
128
+ }
129
+
130
+ &[data-carousel-size="medium"] > .carousel-item-content p {
131
+ display: none;
132
+ }
133
+
134
+ > img,
135
+ > video,
136
+ > picture,
137
+ > picture > img {
138
+ display: block;
139
+ width: calc(100% + 48px);
140
+ max-width: none;
141
+ height: 100%;
142
+ object-fit: cover;
143
+ transform: translateX(var(--md-comp-carousel-item-parallax, 0px));
144
+ transition: transform 100ms linear;
145
+ }
146
+
147
+ > .carousel-item-content {
148
+ position: absolute;
149
+ inset: auto 12px 12px;
150
+ z-index: 2;
151
+ width: fit-content;
152
+ max-width: calc(100% - 24px);
153
+ padding: 8px 12px;
154
+ border-radius: 12px;
155
+ background-color: var(--md-sys-color-surface);
156
+ color: var(--md-sys-color-on-surface);
157
+
158
+ h2,
159
+ h3,
160
+ p {
161
+ margin: 0;
162
+ }
163
+
164
+ h2,
165
+ h3 {
166
+ @include typescale("title-medium");
167
+ }
168
+
169
+ p {
170
+ @include typescale("body-medium");
171
+ display: -webkit-box;
172
+ overflow: hidden;
173
+ -webkit-box-orient: vertical;
174
+ -webkit-line-clamp: 2;
175
+ }
176
+ }
177
+
178
+ // Cards already own an M3 state layer. Other interactive carousel items
179
+ // get the same state feedback without covering their content controls.
180
+ &:not(article)::after {
181
+ content: "";
182
+ position: absolute;
183
+ inset: 0;
184
+ z-index: 3;
185
+ border-radius: inherit;
186
+ background-color: var(--md-comp-carousel-state-layer-color);
187
+ opacity: 0;
188
+ pointer-events: none;
189
+ transition: opacity 200ms ease-out;
190
+ }
191
+
192
+ &:not(article):hover::after {
193
+ opacity: var(--md-comp-carousel-hover-state-layer-opacity);
194
+ }
195
+
196
+ &:not(article):focus-visible::after,
197
+ &:not(article):focus-within::after {
198
+ opacity: var(--md-comp-carousel-focus-state-layer-opacity);
199
+ }
200
+
201
+ &:focus-visible,
202
+ &:focus-within {
203
+ z-index: 4;
204
+ outline: var(--md-comp-carousel-focus-indicator-thickness) solid
205
+ var(--md-comp-carousel-focus-indicator-color);
206
+ outline-offset: var(--md-comp-carousel-focus-indicator-offset);
207
+ }
208
+
209
+ &:not(article):active {
210
+ border-radius: var(--md-comp-carousel-pressed-shape);
211
+ }
212
+
213
+ &:not(article):active::after {
214
+ opacity: var(--md-comp-carousel-pressed-state-layer-opacity);
215
+ }
216
+
217
+ &:is(.disabled, [aria-disabled="true"]) {
218
+ opacity: var(--md-comp-carousel-disabled-opacity);
219
+ pointer-events: none;
220
+ }
32
221
  }
33
222
 
34
223
  .indicators {
@@ -39,9 +228,9 @@
39
228
  list-style: none;
40
229
  position: absolute;
41
230
  inset: auto 0 0;
42
- z-index: 2;
231
+ z-index: 4;
43
232
  margin: 0;
44
- padding: 16px 0;
233
+ padding: 8px 0;
45
234
  border: 0;
46
235
  background: none;
47
236
 
@@ -55,9 +244,13 @@
55
244
  padding: 0;
56
245
  border: 0;
57
246
  border-radius: 50%;
58
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 45%, transparent);
247
+ background-color: color-mix(
248
+ in oklab,
249
+ var(--md-sys-color-on-surface) 45%,
250
+ transparent
251
+ );
59
252
  cursor: pointer;
60
- transition: background-color 0.2s ease-out;
253
+ transition: background-color 200ms ease-out;
61
254
 
62
255
  &:is(.active, [aria-current="true"]) {
63
256
  background-color: var(--md-sys-color-primary);
@@ -66,90 +259,391 @@
66
259
  @include focus-visible;
67
260
  }
68
261
  }
262
+
263
+ // Fixed height: the declared height sizes the track itself and the paging
264
+ // dots take a row underneath it, rather than covering the media.
265
+ &.fixed-height:has(> .indicators) {
266
+ height: calc(
267
+ var(--carousel-height, var(--md-comp-carousel-height)) + var(
268
+ --md-comp-carousel-indicator-allowance
269
+ )
270
+ );
271
+
272
+ > .carousel-track {
273
+ height: calc(100% - var(--md-comp-carousel-indicator-allowance));
274
+ }
275
+
276
+ > .indicators {
277
+ height: var(--md-comp-carousel-indicator-allowance);
278
+ }
279
+ }
69
280
  }
70
281
 
71
- // Coverflow. JS writes transform / opacity / visibility on each item.
72
- .carousel:not(.flat) {
73
- perspective: 500px;
74
- transform-style: preserve-3d;
75
- transform-origin: 0% 50%;
76
- touch-action: pan-y;
282
+ // Contained layouts keep every visible item complete. These surface guards
283
+ // reserve the 16dp edge padding so the following item cannot peek through it.
284
+ .carousel:not(.uncontained, .full-screen, .flat)::before,
285
+ .carousel:not(.uncontained, .full-screen, .flat)::after,
286
+ .carousel.full-screen-horizontal::before,
287
+ .carousel.full-screen-horizontal::after {
288
+ content: "";
289
+ position: absolute;
290
+ inset-block: 0;
291
+ z-index: 3;
292
+ width: var(--md-comp-carousel-inline-padding);
293
+ background-color: var(--md-sys-color-surface);
294
+ pointer-events: none;
295
+ }
77
296
 
78
- > .carousel-item {
79
- visibility: hidden;
80
- position: absolute;
81
- top: 0;
82
- left: 0;
83
- width: calc(var(--carousel-height, var(--md-comp-carousel-height)) * 0.5);
84
- height: calc(var(--carousel-height, var(--md-comp-carousel-height)) * 0.5);
85
- overflow: hidden;
86
- border-radius: var(--md-comp-carousel-shape);
297
+ .carousel:not(.uncontained, .full-screen, .flat)::before,
298
+ .carousel.full-screen-horizontal::before {
299
+ inset-inline-start: 0;
300
+ }
301
+
302
+ .carousel:not(.uncontained, .full-screen, .flat)::after,
303
+ .carousel.full-screen-horizontal::after {
304
+ inset-inline-end: 0;
305
+ }
306
+
307
+ // At 600dp or more, multi-browse adds a second large item. The remaining
308
+ // medium width is solved from the container so every visible item is complete.
309
+ .carousel.multi-wide {
310
+ --md-comp-carousel-large-item-width: clamp(180px, 36%, 480px);
311
+ --md-comp-carousel-medium-item-width: min(
312
+ 240px,
313
+ max(
314
+ 64px,
315
+ calc(
316
+ 100% - var(--md-comp-carousel-large-item-width) - var(
317
+ --md-comp-carousel-large-item-width
318
+ ) - clamp(
319
+ var(--md-comp-carousel-small-item-min-width),
320
+ 8%,
321
+ var(--md-comp-carousel-small-item-max-width)
322
+ ) -
323
+ 24px
324
+ )
325
+ )
326
+ );
327
+
328
+ .carousel-track > .carousel-item[data-carousel-size="small"] {
329
+ flex-basis: clamp(
330
+ var(--md-comp-carousel-small-item-min-width),
331
+ 8%,
332
+ var(--md-comp-carousel-small-item-max-width)
333
+ );
334
+ width: clamp(
335
+ var(--md-comp-carousel-small-item-min-width),
336
+ 8%,
337
+ var(--md-comp-carousel-small-item-max-width)
338
+ );
87
339
  }
88
340
  }
89
341
 
90
- // Linear track. The outer box clips and holds chrome (dots, fixed item).
91
- // Slides live in .carousel-track so they can scroll without taking the
92
- // controls with them — abspos children of an overflow:auto box scroll too.
93
- .carousel.flat {
94
- overflow: hidden;
95
- perspective: none;
96
- transform: none;
342
+ // Large and extra-large containers fit additional large items while keeping
343
+ // the medium item smaller than its large neighbours and the final item whole.
344
+ .carousel.multi-large {
345
+ --md-comp-carousel-large-item-width: clamp(180px, 27%, 480px);
346
+ --md-comp-carousel-medium-item-width: min(
347
+ 240px,
348
+ max(
349
+ 64px,
350
+ calc(
351
+ 100% - var(--md-comp-carousel-large-item-width) - var(
352
+ --md-comp-carousel-large-item-width
353
+ ) - var(--md-comp-carousel-large-item-width) - clamp(
354
+ var(--md-comp-carousel-small-item-min-width),
355
+ 6%,
356
+ var(--md-comp-carousel-small-item-max-width)
357
+ ) -
358
+ 32px
359
+ )
360
+ )
361
+ );
362
+
363
+ .carousel-track > .carousel-item[data-carousel-size="small"] {
364
+ flex-basis: clamp(
365
+ var(--md-comp-carousel-small-item-min-width),
366
+ 6%,
367
+ var(--md-comp-carousel-small-item-max-width)
368
+ );
369
+ width: clamp(
370
+ var(--md-comp-carousel-small-item-min-width),
371
+ 6%,
372
+ var(--md-comp-carousel-small-item-max-width)
373
+ );
374
+ }
375
+ }
97
376
 
377
+ .carousel.multi-extra-large {
378
+ --md-comp-carousel-large-item-width: clamp(180px, 22%, 480px);
379
+ --md-comp-carousel-medium-item-width: min(
380
+ 240px,
381
+ max(
382
+ 64px,
383
+ calc(
384
+ 100% - var(--md-comp-carousel-large-item-width) - var(
385
+ --md-comp-carousel-large-item-width
386
+ ) - var(--md-comp-carousel-large-item-width) - var(
387
+ --md-comp-carousel-large-item-width
388
+ ) - clamp(
389
+ var(--md-comp-carousel-small-item-min-width),
390
+ 4%,
391
+ var(--md-comp-carousel-small-item-max-width)
392
+ ) -
393
+ 40px
394
+ )
395
+ )
396
+ );
397
+
398
+ .carousel-track > .carousel-item[data-carousel-size="small"] {
399
+ flex-basis: clamp(
400
+ var(--md-comp-carousel-small-item-min-width),
401
+ 4%,
402
+ var(--md-comp-carousel-small-item-max-width)
403
+ );
404
+ width: clamp(
405
+ var(--md-comp-carousel-small-item-min-width),
406
+ 4%,
407
+ var(--md-comp-carousel-small-item-max-width)
408
+ );
409
+ }
410
+ }
411
+
412
+ // Uncontained items keep one width and may stop anywhere. `.snap` is an
413
+ // optional, still-conforming alternative for products that prefer snapping.
414
+ .carousel.uncontained {
98
415
  > .carousel-track {
99
- display: flex;
100
- gap: var(--md-comp-carousel-gap);
101
- box-sizing: border-box;
416
+ padding-inline-end: 0;
417
+ scroll-snap-type: none;
418
+ }
419
+
420
+ .carousel-track > .carousel-item {
421
+ flex-basis: var(--md-comp-carousel-uncontained-item-width);
422
+ width: var(--md-comp-carousel-uncontained-item-width);
423
+ scroll-snap-stop: normal;
424
+ }
425
+
426
+ &.snap > .carousel-track {
427
+ scroll-snap-type: x mandatory;
428
+ }
429
+ }
430
+
431
+ .carousel.uncontained.multi-aspect .carousel-track > .carousel-item {
432
+ flex-basis: auto;
433
+ width: auto;
434
+ min-width: calc(
435
+ (var(--carousel-height, var(--md-comp-carousel-height)) - 16px) * 0.5625
436
+ );
437
+ max-width: calc(
438
+ (var(--carousel-height, var(--md-comp-carousel-height)) - 16px) * 1.7778
439
+ );
440
+ aspect-ratio: var(--md-comp-carousel-item-aspect-ratio, auto);
441
+ }
442
+
443
+ // Hero always previews the next item. Center-aligned adds the leading preview
444
+ // by aligning the active large item to the center of the container.
445
+ .carousel.hero {
446
+ --md-comp-carousel-large-item-width: min(calc(100% - 96px), 720px);
447
+
448
+ .carousel-track > .carousel-item[data-carousel-size="small"] {
449
+ flex-basis: clamp(
450
+ var(--md-comp-carousel-small-item-min-width),
451
+ 12%,
452
+ var(--md-comp-carousel-small-item-max-width)
453
+ );
454
+ }
455
+
456
+ &.center-aligned {
457
+ --md-comp-carousel-large-item-width: min(calc(100% - 176px), 720px);
458
+
459
+ > .carousel-track {
460
+ scroll-padding-inline: 50%;
461
+ }
462
+
463
+ .carousel-track > .carousel-item {
464
+ scroll-snap-align: center;
465
+ }
466
+ }
467
+ }
468
+
469
+ // Full-screen is the one vertical M3 layout. It is intended for portrait
470
+ // compact and medium windows; authors should choose a different layout in
471
+ // landscape or at expanded breakpoints.
472
+ .carousel.full-screen {
473
+ --md-comp-carousel-gap: 16px;
474
+ --md-comp-carousel-inline-padding: 0px;
475
+ --md-comp-carousel-block-padding: 0px;
476
+ --md-comp-carousel-shape: 0px;
477
+
478
+ height: 100dvh;
479
+
480
+ > .carousel-track {
481
+ flex-direction: column;
482
+ overflow-x: hidden;
483
+ overflow-y: auto;
484
+ scroll-padding: 0;
485
+ scroll-snap-type: y mandatory;
486
+ overscroll-behavior-inline: auto;
487
+ overscroll-behavior-block: contain;
488
+ touch-action: pan-y;
489
+ }
490
+
491
+ .carousel-track > .carousel-item {
492
+ flex: 0 0 100%;
102
493
  width: 100%;
103
494
  height: 100%;
495
+ border-radius: 0;
496
+
497
+ > img,
498
+ > video,
499
+ > picture,
500
+ > picture > img {
501
+ width: 100%;
502
+ height: calc(100% + 48px);
503
+ transform: translateY(var(--md-comp-carousel-item-parallax, 0px));
504
+ }
505
+ }
506
+ }
507
+
508
+ // The immersive feed adapts to a horizontal hero on landscape and expanded
509
+ // windows. JS adds this internal class from the rendered container geometry.
510
+ .carousel.full-screen.full-screen-horizontal {
511
+ --md-comp-carousel-gap: 8px;
512
+ --md-comp-carousel-inline-padding: 16px;
513
+ --md-comp-carousel-block-padding: 8px;
514
+ --md-comp-carousel-shape: 28px;
515
+ // Fill the content box with one large item, one 8dp gap, and one 56dp
516
+ // preview. Track padding sits outside the percentage basis.
517
+ --md-comp-carousel-large-item-width: calc(100% - 64px);
518
+
519
+ height: var(--carousel-height, var(--md-comp-carousel-height));
520
+
521
+ > .carousel-track {
522
+ flex-direction: row;
104
523
  overflow-x: auto;
105
524
  overflow-y: hidden;
525
+ scroll-padding: var(--md-comp-carousel-block-padding)
526
+ var(--md-comp-carousel-inline-padding);
106
527
  scroll-snap-type: x mandatory;
107
- scroll-behavior: smooth;
528
+ overscroll-behavior-inline: contain;
529
+ overscroll-behavior-block: auto;
108
530
  touch-action: pan-x;
109
- scrollbar-width: none;
531
+ }
110
532
 
111
- &::-webkit-scrollbar {
112
- display: none;
533
+ .carousel-track > .carousel-item {
534
+ flex: 0 0 var(--md-comp-carousel-large-item-width);
535
+ width: var(--md-comp-carousel-large-item-width);
536
+ height: auto;
537
+ border-radius: var(--md-comp-carousel-shape);
538
+
539
+ &[data-carousel-size="small"] {
540
+ flex-basis: clamp(
541
+ var(--md-comp-carousel-small-item-min-width),
542
+ 12%,
543
+ var(--md-comp-carousel-small-item-max-width)
544
+ );
545
+ width: clamp(
546
+ var(--md-comp-carousel-small-item-min-width),
547
+ 12%,
548
+ var(--md-comp-carousel-small-item-max-width)
549
+ );
550
+ }
551
+
552
+ > img,
553
+ > video,
554
+ > picture,
555
+ > picture > img {
556
+ width: calc(100% + 48px);
557
+ height: 100%;
558
+ transform: translateX(var(--md-comp-carousel-item-parallax, 0px));
559
+ }
560
+ }
561
+ }
562
+
563
+ @media (hover: hover) and (pointer: fine) {
564
+ .carousel > .carousel-track {
565
+ cursor: grab;
566
+ }
567
+
568
+ .carousel.dragging {
569
+ > .carousel-track {
570
+ cursor: grabbing;
571
+ scroll-behavior: auto;
572
+ scroll-snap-type: none;
573
+ user-select: none;
574
+ }
575
+
576
+ .carousel-item {
577
+ pointer-events: none;
113
578
  }
114
579
  }
580
+ }
581
+
582
+ // Full-width compatibility mode for tabs and existing integrations.
583
+ .carousel.flat {
584
+ --md-comp-carousel-gap: 0px;
585
+ --md-comp-carousel-inline-padding: 0px;
586
+ --md-comp-carousel-block-padding: 0px;
587
+ --md-comp-carousel-shape: 0px;
115
588
 
116
589
  .carousel-track > .carousel-item {
117
590
  flex: 0 0 100%;
118
591
  width: 100%;
119
592
  height: 100%;
120
- min-height: 0;
121
- position: relative;
122
- visibility: visible;
123
- scroll-snap-align: start;
124
- scroll-snap-stop: always;
125
593
  }
126
594
 
127
595
  .carousel-fixed-item {
128
596
  position: absolute;
129
597
  inset: auto 0 20px;
130
- z-index: 1;
598
+ z-index: 4;
131
599
 
132
600
  &.with-indicators {
133
601
  bottom: 68px;
134
602
  }
135
603
  }
604
+ }
136
605
 
137
- .carousel-item {
138
- h2 {
139
- @include typescale("headline-small");
140
- margin: 0 0 8px;
141
- }
606
+ @include bp-up("medium") {
607
+ .carousel {
608
+ --md-comp-carousel-height: 320px;
609
+ }
610
+ }
142
611
 
143
- p {
144
- @include typescale("body-medium");
145
- margin: 0;
146
- }
612
+ @include bp-up("expanded") {
613
+ .carousel {
614
+ --md-comp-carousel-height: 360px;
147
615
  }
148
616
  }
149
617
 
150
618
  @media (prefers-reduced-motion: reduce) {
151
- .carousel.flat > .carousel-track {
152
- scroll-behavior: auto;
619
+ .carousel {
620
+ --md-comp-carousel-inline-padding: 0px;
621
+
622
+ > .carousel-track {
623
+ scroll-behavior: auto;
624
+ }
625
+
626
+ .carousel-track > .carousel-item {
627
+ flex-basis: var(--md-comp-carousel-uncontained-item-width);
628
+ width: var(--md-comp-carousel-uncontained-item-width);
629
+ transition: none;
630
+
631
+ > img,
632
+ > video,
633
+ > picture,
634
+ > picture > img {
635
+ width: 100%;
636
+ transform: none;
637
+ transition: none;
638
+ }
639
+ }
640
+ }
641
+
642
+ .carousel.full-screen:not(.full-screen-horizontal)
643
+ .carousel-track
644
+ > .carousel-item {
645
+ flex-basis: 100%;
646
+ width: 100%;
153
647
  }
154
648
  }
155
649