@expressivecss/expressive 0.2.0 → 0.3.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 (37) hide show
  1. package/README.md +8 -8
  2. package/dist/css/expressive.css +1038 -361
  3. package/dist/css/expressive.css.map +1 -1
  4. package/dist/css/expressive.min.css +1 -1
  5. package/dist/css/expressive.min.css.map +1 -1
  6. package/dist/js/expressive.cjs +708 -746
  7. package/dist/js/expressive.cjs.map +3 -3
  8. package/dist/js/expressive.js +708 -746
  9. package/dist/js/expressive.js.map +3 -3
  10. package/dist/js/expressive.min.js +10 -10
  11. package/dist/js/expressive.min.js.map +3 -3
  12. package/dist/js/expressive.mjs +708 -746
  13. package/dist/js/expressive.mjs.map +3 -3
  14. package/dist/types/components/buttons.d.ts +17 -41
  15. package/dist/types/components/carousel.d.ts +40 -46
  16. package/dist/types/components/chips.d.ts +2 -2
  17. package/dist/types/components/collapsible.d.ts +35 -13
  18. package/dist/types/components/parallax.d.ts +7 -31
  19. package/dist/types/components/tapTarget.d.ts +23 -24
  20. package/dist/types/index.d.ts +1 -1
  21. package/package.json +1 -1
  22. package/src/sass/README.md +4 -3
  23. package/src/sass/components/_badges.scss +5 -4
  24. package/src/sass/components/_breadcrumb.scss +93 -16
  25. package/src/sass/components/_buttons.scss +195 -96
  26. package/src/sass/components/_carousel.scss +130 -63
  27. package/src/sass/components/_chips.scss +1 -1
  28. package/src/sass/components/_collapsible.scss +160 -60
  29. package/src/sass/components/_collection.scss +161 -77
  30. package/src/sass/components/_icons-material-design.scss +77 -4
  31. package/src/sass/components/_lightbox.scss +4 -0
  32. package/src/sass/components/_navbar.scss +15 -0
  33. package/src/sass/components/_pagination.scss +131 -25
  34. package/src/sass/components/_parallax.scss +116 -20
  35. package/src/sass/components/_sidenav.scss +10 -5
  36. package/src/sass/components/_slider.scss +8 -0
  37. package/src/sass/components/_tapTarget.scss +134 -47
@@ -232,12 +232,12 @@ $_btn: "button:not(#{$_not-btn}), a.button, .btn, .btn-flat, .btn-large, .btn-sm
232
232
 
233
233
  &.halfway-fab {
234
234
  position: absolute;
235
- right: 24px;
235
+ inset-inline-end: 24px;
236
236
  bottom: -28px;
237
237
 
238
238
  &.left {
239
- right: auto;
240
- left: 24px;
239
+ inset-inline-end: auto;
240
+ inset-inline-start: 24px;
241
241
  }
242
242
  }
243
243
  }
@@ -262,70 +262,166 @@ $_btn: "button:not(#{$_not-btn}), a.button, .btn, .btn-flat, .btn-large, .btn-sm
262
262
  @include btn-disabled;
263
263
  }
264
264
 
265
- // Fixed Action Button — JS-driven menu around a FAB.
265
+ // Speed dial. .active (and :hover when the pointer can hover) opens the
266
+ // menu. Motion is CSS. .click-to-toggle is the markup switch for click
267
+ // mode. .fixed-action-btn stays the public class.
268
+ //
269
+ // Tokens:
270
+ // --md-comp-fab-offset 16px from the corner
271
+ // --md-comp-fab-z 997
272
+ // --md-comp-fab-menu-gap 16px
273
+ // --md-comp-fab-duration 200ms
274
+ // --md-comp-fab-stagger 40ms
275
+ // --md-comp-fab-easing soft ease-in-out
276
+ // --md-comp-fab-travel center-to-center step into the main disc
277
+ // --md-comp-fab-hidden-transform toward / into the main button
266
278
  .fixed-action-btn {
279
+ --md-comp-fab-offset: 16px;
280
+ --md-comp-fab-z: 997;
281
+ --md-comp-fab-menu-gap: 16px;
282
+ --md-comp-fab-duration: 200ms;
283
+ --md-comp-fab-stagger: 40ms;
284
+ --md-comp-fab-easing: cubic-bezier(0.65, 0, 0.35, 1);
285
+ --md-comp-fab-travel: 64px;
286
+ --md-comp-fab-hidden-transform: scale(0)
287
+ translateY(calc(var(--md-comp-fab-travel) * (var(--i, 0) + 1)));
288
+
267
289
  position: fixed;
268
- right: 23px;
269
- bottom: 23px;
270
- padding-top: 15px;
271
- margin-bottom: 0;
272
- z-index: 997;
273
-
274
- &.active {
275
- ul {
276
- visibility: visible;
277
- padding-left: 0;
278
- list-style-type: none;
290
+ inset-inline-end: var(--md-comp-fab-offset);
291
+ bottom: var(--md-comp-fab-offset);
292
+ z-index: var(--md-comp-fab-z);
293
+ padding-top: var(--md-comp-fab-menu-gap);
294
+ margin: 0;
295
+ overflow: visible;
296
+
297
+ // Main disc stays above retracting actions so they slide under it.
298
+ > :is(a, button) {
299
+ position: relative;
300
+ z-index: 1;
301
+ }
302
+
303
+ :is(ul, menu) {
304
+ --n: 1;
305
+ position: absolute;
306
+ inset-inline: 0;
307
+ bottom: 64px;
308
+ display: flex;
309
+ flex-direction: column;
310
+ align-items: center;
311
+ gap: var(--md-comp-fab-menu-gap);
312
+ margin: 0;
313
+ padding: 0;
314
+ list-style: none;
315
+ visibility: hidden;
316
+ overflow: visible;
317
+ transition: visibility 0s
318
+ calc(var(--md-comp-fab-duration) + var(--md-comp-fab-stagger) * (var(--n) - 1));
319
+
320
+ &:has(> :nth-child(2)) { --n: 2; }
321
+ &:has(> :nth-child(3)) { --n: 3; }
322
+ &:has(> :nth-child(4)) { --n: 4; }
323
+ &:has(> :nth-child(5)) { --n: 5; }
324
+ &:has(> :nth-child(6)) { --n: 6; }
325
+ &:has(> :nth-child(7)) { --n: 7; }
326
+ &:has(> :nth-child(8)) { --n: 8; }
327
+
328
+ li:nth-last-child(1) { --i: 0; }
329
+ li:nth-last-child(2) { --i: 1; }
330
+ li:nth-last-child(3) { --i: 2; }
331
+ li:nth-last-child(4) { --i: 3; }
332
+ li:nth-last-child(5) { --i: 4; }
333
+ li:nth-last-child(6) { --i: 5; }
334
+ li:nth-last-child(7) { --i: 6; }
335
+ li:nth-last-child(8) { --i: 7; }
336
+
337
+ :is(a, button) {
338
+ opacity: 0;
339
+ transform: var(--md-comp-fab-hidden-transform);
340
+ transition:
341
+ opacity var(--md-comp-fab-duration) var(--md-comp-fab-easing),
342
+ transform var(--md-comp-fab-duration) var(--md-comp-fab-easing);
343
+ // Close: farthest first, so they funnel into the disc.
344
+ transition-delay: calc(
345
+ var(--md-comp-fab-stagger) * (var(--n) - 1 - var(--i, 0))
346
+ );
347
+ }
348
+ }
349
+
350
+ &.active :is(ul, menu) {
351
+ visibility: visible;
352
+ transition-delay: 0s;
353
+
354
+ :is(a, button) {
355
+ opacity: 1;
356
+ transform: none;
357
+ transition-delay: calc(var(--md-comp-fab-stagger) * var(--i, 0));
358
+ }
359
+ }
360
+
361
+ // Closest-to-FAB is the first child when the menu is left, right, or
362
+ // down (row-reverse / row / column). Top uses nth-last-child above.
363
+ &.direction-left,
364
+ &.direction-right,
365
+ &.direction-bottom {
366
+ :is(ul, menu) {
367
+ li:nth-child(1) { --i: 0; }
368
+ li:nth-child(2) { --i: 1; }
369
+ li:nth-child(3) { --i: 2; }
370
+ li:nth-child(4) { --i: 3; }
371
+ li:nth-child(5) { --i: 4; }
372
+ li:nth-child(6) { --i: 5; }
373
+ li:nth-child(7) { --i: 6; }
374
+ li:nth-child(8) { --i: 7; }
279
375
  }
280
376
  }
281
377
 
282
378
  &.direction-left,
283
379
  &.direction-right {
284
- padding: 0 0 0 15px;
380
+ padding: 0;
285
381
 
286
- ul {
287
- text-align: right;
288
- right: 64px;
382
+ :is(ul, menu) {
383
+ inset: auto;
289
384
  top: 50%;
290
- transform: translateY(-50%);
291
- height: 100%;
292
- left: auto;
293
- width: 500px;
385
+ translate: 0 -50%;
386
+ flex-direction: row;
387
+ width: max-content;
388
+ height: auto;
389
+ }
390
+ }
294
391
 
295
- li {
296
- display: inline-block;
297
- margin: 7.5px 15px 0 0;
298
- }
392
+ // Hidden offset is toward the main button, one step per index, so
393
+ // close sends every action into the disc.
394
+ &.direction-left {
395
+ --md-comp-fab-hidden-transform: scale(0)
396
+ translateX(calc(var(--md-comp-fab-travel) * (var(--i, 0) + 1)));
397
+ padding-inline-start: var(--md-comp-fab-menu-gap);
398
+
399
+ :is(ul, menu) {
400
+ inset-inline-end: 64px;
401
+ flex-direction: row-reverse;
299
402
  }
300
403
  }
301
404
 
302
405
  &.direction-right {
303
- padding: 0 15px 0 0;
304
-
305
- ul {
306
- text-align: left;
307
- direction: rtl;
308
- left: 64px;
309
- right: auto;
406
+ --md-comp-fab-hidden-transform: scale(0)
407
+ translateX(calc(-1 * var(--md-comp-fab-travel) * (var(--i, 0) + 1)));
408
+ padding-inline-end: var(--md-comp-fab-menu-gap);
310
409
 
311
- li {
312
- margin: 7.5px 0 0 15px;
313
- }
410
+ :is(ul, menu) {
411
+ inset-inline-start: 64px;
314
412
  }
315
413
  }
316
414
 
317
415
  &.direction-bottom {
318
- padding: 0 0 15px 0;
416
+ --md-comp-fab-hidden-transform: scale(0)
417
+ translateY(calc(-1 * var(--md-comp-fab-travel) * (var(--i, 0) + 1)));
418
+ padding: 0 0 var(--md-comp-fab-menu-gap);
319
419
 
320
- ul {
420
+ :is(ul, menu) {
421
+ inset: auto;
321
422
  top: 64px;
322
423
  bottom: auto;
323
- display: flex;
324
- flex-direction: column-reverse;
325
-
326
- li {
327
- margin: 15px 0 0 0;
328
- }
424
+ inset-inline: 0;
329
425
  }
330
426
  }
331
427
 
@@ -333,75 +429,78 @@ $_btn: "button:not(#{$_not-btn}), a.button, .btn, .btn-flat, .btn-large, .btn-sm
333
429
  padding: 0;
334
430
  height: 56px;
335
431
 
336
- &.active {
337
- & > a i,
338
- & > button i {
339
- opacity: 0;
340
- }
432
+ :is(ul, menu) :is(a, button) {
433
+ opacity: 1;
434
+ transform: none;
435
+ transition: opacity var(--md-comp-fab-duration);
341
436
  }
342
437
 
343
- ul {
344
- display: flex;
345
- top: 0;
438
+ &.active {
439
+ inset-inline: 0;
346
440
  bottom: 0;
347
- z-index: 1;
441
+ width: 100%;
442
+ height: 56px;
443
+
444
+ > :is(a, button) > i {
445
+ opacity: 0;
446
+ }
348
447
 
349
- li {
350
- flex: 1;
351
- display: inline-block;
352
- margin: 0;
448
+ :is(ul, menu) {
449
+ visibility: visible;
450
+ position: static;
451
+ display: flex;
452
+ flex-direction: row;
453
+ width: 100%;
353
454
  height: 100%;
354
- transition: none;
355
-
356
- a,
357
- button {
358
- display: block;
359
- overflow: hidden;
360
- position: relative;
361
- width: 100%;
362
- height: 100%;
363
- background-color: transparent;
364
- box-shadow: none;
365
- color: var(--md-sys-color-on-primary-container);
366
- line-height: 56px;
367
- z-index: 1;
368
-
369
- i {
370
- line-height: inherit;
371
- }
372
- }
455
+ gap: 0;
373
456
  }
374
457
  }
375
- }
376
458
 
377
- ul {
378
- left: 0;
379
- right: 0;
380
- text-align: center;
381
- position: absolute;
382
- bottom: 64px;
383
- margin: 0;
384
- visibility: hidden;
385
-
386
- li {
387
- margin-bottom: 15px;
388
- }
459
+ :is(ul, menu) li {
460
+ flex: 1;
461
+ margin: 0;
462
+ height: 100%;
389
463
 
390
- a.btn-floating,
391
- button.btn-floating {
392
- opacity: 0;
464
+ :is(a, button) {
465
+ display: grid;
466
+ place-items: center;
467
+ width: 100%;
468
+ height: 100%;
469
+ background: transparent;
470
+ box-shadow: none;
471
+ color: var(--md-sys-color-on-primary-container);
472
+ }
393
473
  }
394
474
  }
395
475
 
396
476
  .fab-backdrop {
397
477
  position: absolute;
398
- top: 0;
399
- left: 0;
478
+ inset: 0 auto auto 0;
400
479
  z-index: -1;
401
480
  width: 56px;
402
481
  height: 56px;
403
- background-color: var(--md-sys-color-primary-container);
404
482
  border-radius: 16px;
483
+ background-color: var(--md-sys-color-primary-container);
405
484
  transform: scale(0);
406
485
  }
407
486
  }
487
+
488
+ @media (hover: hover) {
489
+ .fixed-action-btn:not(.click-to-toggle):hover :is(ul, menu) {
490
+ visibility: visible;
491
+ transition-delay: 0s;
492
+
493
+ :is(a, button) {
494
+ opacity: 1;
495
+ transform: none;
496
+ transition-delay: calc(var(--md-comp-fab-stagger) * var(--i, 0));
497
+ }
498
+ }
499
+ }
500
+
501
+ @media (prefers-reduced-motion: reduce) {
502
+ .fixed-action-btn {
503
+ --md-comp-fab-duration: 0s;
504
+ --md-comp-fab-stagger: 0s;
505
+ }
506
+ }
@@ -1,95 +1,162 @@
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). .carousel-slider is an alias.
6
+ //
7
+ // Anatomy: direct children move. .carousel-item is optional. Indicators and
8
+ // .carousel-fixed-item are chrome, not slides.
9
+ //
10
+ // Tokens:
11
+ // --md-comp-carousel-height 400px (--carousel-height still wins)
12
+ // --md-comp-carousel-shape 12px
13
+ // --md-comp-carousel-gap 0
14
+
3
15
  .carousel {
4
- --carousel-height: 400px;
16
+ --md-comp-carousel-height: 400px;
17
+ --md-comp-carousel-shape: 12px;
18
+ --md-comp-carousel-gap: 0px;
5
19
 
6
20
  overflow: hidden;
7
21
  position: relative;
8
22
  width: 100%;
9
- height: var(--carousel-height);
10
- perspective: 500px;
11
- transform-style: preserve-3d;
12
- transform-origin: 0% 50%;
23
+ height: var(--carousel-height, var(--md-comp-carousel-height));
24
+ border-radius: var(--md-comp-carousel-shape);
13
25
 
14
- &.carousel-slider {
15
- top: 0;
16
- left: 0;
26
+ .carousel-item > img,
27
+ .carousel-item > video {
28
+ display: block;
29
+ width: 100%;
30
+ height: 100%;
31
+ object-fit: cover;
32
+ }
17
33
 
18
- .carousel-fixed-item {
19
- position: absolute;
20
- left: 0;
21
- right: 0;
22
- bottom: 20px;
23
- z-index: 1;
34
+ .indicators {
35
+ display: flex;
36
+ justify-content: center;
37
+ align-items: center;
38
+ gap: 8px;
39
+ list-style: none;
40
+ position: absolute;
41
+ inset: auto 0 0;
42
+ z-index: 2;
43
+ margin: 0;
44
+ padding: 16px 0;
45
+ border: 0;
46
+ background: none;
24
47
 
25
- &.with-indicators {
26
- bottom: 68px;
27
- }
28
- }
48
+ .indicator-item {
49
+ appearance: none;
50
+ box-sizing: border-box;
51
+ display: block;
52
+ width: 8px;
53
+ height: 8px;
54
+ margin: 0;
55
+ padding: 0;
56
+ border: 0;
57
+ border-radius: 50%;
58
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 45%, transparent);
59
+ cursor: pointer;
60
+ transition: background-color 0.2s ease-out;
29
61
 
30
- .carousel-item {
31
- width: 100%;
32
- height: 100%;
33
- min-height: var(--carousel-height);
34
- position: absolute;
35
- top: 0;
36
- left: 0;
37
-
38
- h2 {
39
- font-size: 24px;
40
- font-weight: 500;
41
- line-height: 32px;
62
+ &:is(.active, [aria-current="true"]) {
63
+ background-color: var(--md-sys-color-primary);
42
64
  }
43
65
 
44
- p {
45
- font-size: 15px;
46
- }
66
+ @include focus-visible;
47
67
  }
48
68
  }
69
+ }
49
70
 
50
- .carousel-item {
71
+ // Coverflow. JS writes transform / opacity / visibility on each item.
72
+ .carousel:not(.flat):not(.carousel-slider) {
73
+ perspective: 500px;
74
+ transform-style: preserve-3d;
75
+ transform-origin: 0% 50%;
76
+ touch-action: pan-y;
77
+
78
+ > .carousel-item {
51
79
  visibility: hidden;
52
- width: calc(var(--carousel-height) * 0.5);
53
- height: calc(var(--carousel-height) * 0.5);
54
80
  position: absolute;
55
81
  top: 0;
56
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);
87
+ }
88
+ }
89
+
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
+ .carousel.carousel-slider {
95
+ overflow: hidden;
96
+ perspective: none;
97
+ transform: none;
98
+
99
+ > .carousel-track {
100
+ display: flex;
101
+ gap: var(--md-comp-carousel-gap);
102
+ box-sizing: border-box;
103
+ width: 100%;
104
+ height: 100%;
105
+ overflow-x: auto;
106
+ overflow-y: hidden;
107
+ scroll-snap-type: x mandatory;
108
+ scroll-behavior: smooth;
109
+ touch-action: pan-x;
110
+ scrollbar-width: none;
57
111
 
58
- & > img {
59
- width: 100%;
112
+ &::-webkit-scrollbar {
113
+ display: none;
60
114
  }
61
115
  }
62
116
 
63
- .indicators {
64
- padding-left: 0;
65
- list-style-type: none;
117
+ .carousel-track > .carousel-item {
118
+ flex: 0 0 100%;
119
+ width: 100%;
120
+ height: 100%;
121
+ min-height: 0;
122
+ position: relative;
123
+ visibility: visible;
124
+ scroll-snap-align: start;
125
+ scroll-snap-stop: always;
126
+ }
127
+
128
+ .carousel-fixed-item {
66
129
  position: absolute;
67
- text-align: center;
68
- left: 0;
69
- right: 0;
70
- bottom: 0;
71
- margin: 0;
130
+ inset: auto 0 20px;
131
+ z-index: 1;
72
132
 
73
- .indicator-item {
74
- display: inline-block;
75
- position: relative;
76
- cursor: pointer;
77
- height: 8px;
78
- width: 8px;
79
- margin: 24px 4px;
80
- background-color: rgba(255, 255, 255, 0.45);
81
- transition: background-color .3s;
82
- border-radius: 50%;
133
+ &.with-indicators {
134
+ bottom: 68px;
135
+ }
136
+ }
83
137
 
84
- &.active {
85
- background-color: var(--md-ref-palette-primary100);
86
- }
138
+ .carousel-item {
139
+ h2 {
140
+ @include typescale("headline-small");
141
+ margin: 0 0 8px;
142
+ }
143
+
144
+ p {
145
+ @include typescale("body-medium");
146
+ margin: 0;
87
147
  }
88
148
  }
149
+ }
89
150
 
90
- // Lightbox compatibility
91
- &.scrolling .carousel-item .lightboxed,
92
- .carousel-item:not(.active) .lightboxed {
93
- pointer-events: none;
151
+ @media (prefers-reduced-motion: reduce) {
152
+ .carousel.flat > .carousel-track,
153
+ .carousel.carousel-slider > .carousel-track {
154
+ scroll-behavior: auto;
94
155
  }
95
156
  }
157
+
158
+ // Lightbox: do not open a boxed image while the track is moving.
159
+ .carousel.scrolling .carousel-item .lightboxed,
160
+ .carousel-item:not(.active) .lightboxed {
161
+ pointer-events: none;
162
+ }
@@ -43,7 +43,7 @@
43
43
  border-radius: 12px;
44
44
  }
45
45
 
46
- .chip > .material-icons {
46
+ .chip > :is(.material-symbols, .material-symbols-outlined, .material-symbols-rounded, .material-symbols-sharp, .material-icons) {
47
47
  font-size: var(--font-size-icon);
48
48
  color: var(--md-sys-color-on-surface-variant);
49
49
  }