@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
@@ -1,46 +1,152 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
+ // Pagination. Anatomy is the HTML: a nav.pagination whose child is an
4
+ // <ol> (or <ul>) of page links. The current page is aria-current="page".
5
+ // A bare <ul class="pagination"> stays as an alias.
6
+ //
7
+ // .active / .disabled / .prev / .next / .pages still work.
8
+ // .nowrap (or the old li.pages nest) scrolls a long run instead of wrapping.
9
+ //
10
+ // Tokens (defaults match the original compact pager):
11
+ // --md-comp-pagination-size 2rem
12
+ // --md-comp-pagination-shape 2rem (stadium / disc)
13
+ // --md-comp-pagination-gap 0
14
+ // --md-comp-pagination-padding 0.66rem
15
+ // --md-comp-pagination-icon-size 2rem
16
+ // --md-comp-pagination-selected-color primary
17
+ // --md-comp-pagination-selected-on-color on-primary
18
+ //
19
+ // Nest descendants inside this block.
20
+
3
21
  .pagination {
22
+ --md-comp-pagination-size: 2rem;
23
+ --md-comp-pagination-shape: 2rem;
24
+ --md-comp-pagination-gap: 0px;
25
+ --md-comp-pagination-padding: 0.66rem;
26
+ --md-comp-pagination-icon-size: 2rem;
27
+ --md-comp-pagination-selected-color: var(--md-sys-color-primary);
28
+ --md-comp-pagination-selected-on-color: var(--md-sys-color-on-primary);
29
+
30
+ box-sizing: border-box;
31
+ margin: 0;
32
+ padding: 0;
33
+ color: var(--md-sys-color-on-surface);
34
+ font-size: 1.2rem;
35
+ line-height: 1;
36
+
37
+ &,
38
+ > :is(ul, ol) {
39
+ display: flex;
40
+ flex-wrap: wrap;
41
+ align-items: center;
42
+ gap: var(--md-comp-pagination-gap);
43
+ list-style: none;
44
+ margin: 0;
45
+ padding: 0;
46
+ }
47
+
48
+ &.nowrap,
49
+ &.nowrap > :is(ul, ol) {
50
+ flex-wrap: nowrap;
51
+ min-width: 0;
52
+ overflow-x: auto;
53
+ scrollbar-width: none;
54
+
55
+ &::-webkit-scrollbar {
56
+ display: none;
57
+ }
58
+ }
59
+
4
60
  li {
5
- display: inline-block;
6
- vertical-align: top;
61
+ display: flex;
62
+ flex-shrink: 0;
63
+ margin: 0;
64
+ padding: 0;
65
+ }
7
66
 
8
- a {
9
- @include btn($height: 2rem, $padding-left: .66rem, $padding-right: .66rem, $font-size: 1.2rem);
67
+ :is(a, button) {
68
+ box-sizing: border-box;
69
+ display: inline-flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ gap: 4px;
73
+ min-width: var(--md-comp-pagination-size);
74
+ height: var(--md-comp-pagination-size);
75
+ padding: 0 var(--md-comp-pagination-padding);
76
+ border: 0;
77
+ border-radius: var(--md-comp-pagination-shape);
78
+ background: transparent;
79
+ color: inherit;
80
+ font: inherit;
81
+ line-height: inherit;
82
+ text-decoration: none;
83
+ cursor: pointer;
84
+ -webkit-tap-highlight-color: transparent;
85
+ transition: background-color 0.2s ease-out;
86
+ @include focus-visible;
87
+
88
+ &:hover {
89
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
10
90
  }
11
91
 
12
- &.active a {
13
- @include btn-filled;
92
+ &:has(> span) {
93
+ width: auto;
14
94
  }
15
95
 
16
- &.disabled a {
17
- @include btn-disabled;
96
+ > i {
97
+ font-size: var(--md-comp-pagination-icon-size);
98
+ width: var(--md-comp-pagination-icon-size);
99
+ height: var(--md-comp-pagination-icon-size);
100
+ line-height: 1;
18
101
  }
102
+ }
19
103
 
20
- i {
21
- font-size: 2rem;
104
+ :is(a, button)[aria-current="page"],
105
+ .active > :is(a, button),
106
+ li:has(> [aria-current="page"]) > :is(a, button) {
107
+ background-color: var(--md-comp-pagination-selected-color);
108
+ color: var(--md-comp-pagination-selected-on-color);
109
+
110
+ &:hover {
111
+ background-color: color-mix(
112
+ in oklab,
113
+ var(--md-comp-pagination-selected-on-color) 8%,
114
+ var(--md-comp-pagination-selected-color)
115
+ );
22
116
  }
23
117
  }
24
118
 
25
- li.pages ul li {
26
- display: inline-block;
27
- float: none;
119
+ :is(a, button)[aria-disabled="true"],
120
+ .disabled > :is(a, button) {
121
+ color: color-mix(in oklab, var(--md-sys-color-on-surface) 38%, transparent);
122
+ background-color: transparent;
123
+ pointer-events: none;
124
+ cursor: default;
28
125
  }
29
- }
30
126
 
31
- @include bp-down("large") {
32
- .pagination {
33
- width: 100%;
127
+ li > span {
128
+ box-sizing: border-box;
129
+ display: inline-flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ min-width: var(--md-comp-pagination-size);
133
+ height: var(--md-comp-pagination-size);
134
+ color: var(--md-sys-color-on-surface-variant);
135
+ }
34
136
 
35
- li.prev,
36
- li.next {
37
- width: 10%;
38
- }
137
+ // Old nested-pages markup. Scroll, do not clip to 80%.
138
+ .pages {
139
+ min-width: 0;
39
140
 
40
- li.pages {
41
- width: 80%;
42
- overflow: hidden;
43
- white-space: nowrap;
141
+ > :is(ul, ol) {
142
+ display: flex;
143
+ flex-wrap: inherit;
144
+ align-items: center;
145
+ gap: var(--md-comp-pagination-gap);
146
+ list-style: none;
147
+ min-width: 0;
148
+ margin: 0;
149
+ padding: 0;
44
150
  }
45
151
  }
46
152
  }
@@ -1,29 +1,125 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
- // Paired with components/parallax.ts, which positions the image on scroll.
3
+ // Parallax. Anatomy is the HTML: a .parallax (or <figure class="parallax">)
4
+ // clips an <img>, <picture>, or <video>. A heading / <p> / <figcaption> is
5
+ // the overlay. .parallax-container is the old wrapper and still works.
6
+ //
7
+ // Motion is CSS. A named view timeline on the clip drives the image; without
8
+ // animation-timeline the image is just object-fit: cover. JS does not scroll.
9
+ //
10
+ // Tokens:
11
+ // --md-comp-parallax-height visible window (500px)
12
+ // --md-comp-parallax-travel how far the image moves (64px)
13
+ // --md-comp-parallax-shape 12px, same as media / lightbox / slider
4
14
 
15
+ .parallax,
5
16
  .parallax-container {
17
+ --md-comp-parallax-height: 500px;
18
+ --md-comp-parallax-travel: 120px;
19
+ --md-comp-parallax-shape: 12px;
20
+
6
21
  position: relative;
22
+ isolation: isolate;
7
23
  overflow: hidden;
8
- height: 500px;
9
-
10
- .parallax {
11
- position: absolute;
12
- top: 0;
13
- left: 0;
14
- right: 0;
15
- bottom: 0;
16
- z-index: -1;
17
-
18
- img {
19
- opacity: 0;
20
- position: absolute;
21
- left: 50%;
22
- bottom: 0;
23
- min-width: 100%;
24
- min-height: 100%;
25
- transform: translate3d(0, 0, 0);
26
- transform: translateX(-50%);
24
+ box-sizing: border-box;
25
+ height: var(--md-comp-parallax-height);
26
+ margin: 0;
27
+ border-radius: var(--md-comp-parallax-shape);
28
+ view-timeline-name: --parallax;
29
+ view-timeline-axis: block;
30
+ }
31
+
32
+ // Old markup: container > .parallax > img. The outer box is the clip;
33
+ // the inner .parallax is just a fill, not a second 500px window.
34
+ .parallax-container > .parallax {
35
+ position: absolute;
36
+ inset: 0;
37
+ height: auto;
38
+ margin: 0;
39
+ border-radius: 0;
40
+ overflow: visible;
41
+ view-timeline-name: none;
42
+ }
43
+
44
+ .parallax > :is(img, video),
45
+ .parallax > picture,
46
+ .parallax-container .parallax > :is(img, video),
47
+ .parallax-container .parallax > picture {
48
+ position: absolute;
49
+ inset: 0;
50
+ display: block;
51
+ width: 100%;
52
+ height: 100%;
53
+ border: 0;
54
+ }
55
+
56
+ .parallax > picture img,
57
+ .parallax-container .parallax > picture img {
58
+ display: block;
59
+ width: 100%;
60
+ height: 100%;
61
+ border: 0;
62
+ }
63
+
64
+ .parallax > :is(img, video),
65
+ .parallax > picture img,
66
+ .parallax-container .parallax > :is(img, video),
67
+ .parallax-container .parallax > picture img {
68
+ object-fit: cover;
69
+ object-position: center;
70
+ }
71
+
72
+ // Overlay. A heading or <p> is a centered hero; figcaption docks to the
73
+ // bottom on a translucent surface, same idea as a card figure.
74
+ .parallax > :is(h1, h2, h3, h4, h5, h6),
75
+ .parallax > p {
76
+ position: absolute;
77
+ inset: 0;
78
+ z-index: 1;
79
+ display: grid;
80
+ place-items: center;
81
+ margin: 0;
82
+ padding: 24px;
83
+ color: var(--md-sys-color-on-surface);
84
+ text-align: center;
85
+ }
86
+
87
+ .parallax > figcaption {
88
+ position: absolute;
89
+ inset: auto 0 0;
90
+ z-index: 1;
91
+ margin: 0;
92
+ padding: 16px 24px;
93
+ color: var(--md-sys-color-on-surface);
94
+ background: color-mix(in oklab, var(--md-sys-color-surface) 72%, transparent);
95
+ }
96
+
97
+ // Full-bleed hero. Meets the viewport edges; no corners.
98
+ .parallax.max,
99
+ .parallax-container.max {
100
+ height: 100vh;
101
+ height: 100dvh;
102
+ border-radius: 0;
103
+ }
104
+
105
+ // Progressive enhancement: without animation-timeline the image stays a
106
+ // static cover. Named timeline is the clip, not the (taller) image.
107
+ @supports (animation-timeline: view()) {
108
+ @media (prefers-reduced-motion: no-preference) {
109
+ .parallax > :is(img, video),
110
+ .parallax > picture img,
111
+ .parallax-container .parallax > :is(img, video),
112
+ .parallax-container .parallax > picture img {
113
+ height: calc(100% + 2 * var(--md-comp-parallax-travel));
114
+ inset: calc(-1 * var(--md-comp-parallax-travel)) 0 auto;
115
+ animation: parallax-shift linear both;
116
+ animation-timeline: --parallax;
117
+ animation-range: cover;
27
118
  }
28
119
  }
29
120
  }
121
+
122
+ @keyframes parallax-shift {
123
+ from { transform: translateY(var(--md-comp-parallax-travel)); }
124
+ to { transform: translateY(calc(-1 * var(--md-comp-parallax-travel))); }
125
+ }
@@ -97,8 +97,7 @@
97
97
  }
98
98
 
99
99
  /* TODO: Use special class in future like "mw-icon" */
100
- & > .material-icons, & > .material-symbols-outlined,
101
- & > .material-symbols-rounded, & > .material-symbols-sharp {
100
+ & > :is(.material-symbols, .material-symbols-outlined, .material-symbols-rounded, .material-symbols-sharp, .material-icons) {
102
101
  display: inline-flex;
103
102
  vertical-align: middle;
104
103
  margin-right: 12px;
@@ -214,7 +213,8 @@
214
213
  }
215
214
  }
216
215
 
217
- .sidenav .collapsible-body {
216
+ .sidenav .collapsible-body,
217
+ .sidenav details::details-content {
218
218
  padding: 0;
219
219
  }
220
220
 
@@ -234,13 +234,18 @@
234
234
  .sidenav,
235
235
  .sidenav.sidenav-fixed {
236
236
  .collapsible {
237
+ overflow: visible;
237
238
  border: none;
239
+ border-radius: 0;
240
+ background: transparent;
238
241
  box-shadow: none;
239
242
  }
240
- .collapsible-header {
243
+ .collapsible-header,
244
+ summary {
241
245
  border: none;
242
246
  }
243
- .collapsible-body {
247
+ .collapsible-body,
248
+ details::details-content {
244
249
  border: none;
245
250
  }
246
251
  }
@@ -4,6 +4,14 @@
4
4
  position: relative;
5
5
  height: 400px;
6
6
  width: 100%;
7
+ // Same 12px as .responsive-img / .lightboxed. Applied to the slide
8
+ // stack, not the slider: init() adds 40px under the slides for the
9
+ // indicators, so a radius on the outer box never meets the photo.
10
+ // Fullscreen stays square so it can meet the viewport edges.
11
+ &:not(.fullscreen) .slides,
12
+ &:not(.fullscreen) .slides li {
13
+ border-radius: 12px;
14
+ }
7
15
 
8
16
  // Fullscreen slider
9
17
  &.fullscreen {
@@ -1,104 +1,191 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
+ // Tap target (Feature Discovery). A circular spotlight centered on an
4
+ // origin. JS writes --md-comp-tap-target-x/y/origin-size and data-edge;
5
+ // this file does the rest.
6
+ //
7
+ // Tokens:
8
+ // --md-comp-tap-target-size 480px
9
+ // --md-comp-tap-target-z 1000
10
+ // --md-comp-tap-target-container-color
11
+ // --md-comp-tap-target-supporting-color
12
+ // --md-comp-tap-target-duration 300ms
13
+ // --md-comp-tap-target-easing soft ease-in-out
14
+ //
15
+ // .feature-discovery is an alias of .tap-target.
16
+
3
17
  .tap-target-wrapper {
4
- width: 800px;
5
- height: 800px;
18
+ --md-comp-tap-target-size: 480px;
19
+ --md-comp-tap-target-z: 1000;
20
+ --md-comp-tap-target-container-color: var(--md-sys-color-primary-container);
21
+ --md-comp-tap-target-supporting-color: var(--md-sys-color-on-primary-container);
22
+ --md-comp-tap-target-duration: 300ms;
23
+ --md-comp-tap-target-easing: cubic-bezier(0.65, 0, 0.35, 1);
24
+ --md-comp-tap-target-x: 50%;
25
+ --md-comp-tap-target-y: 50%;
26
+ --md-comp-tap-target-origin-size: 56px;
27
+
28
+ box-sizing: border-box;
6
29
  position: fixed;
7
- z-index: 1000;
30
+ inset: auto;
31
+ left: var(--md-comp-tap-target-x);
32
+ top: var(--md-comp-tap-target-y);
33
+ translate: -50% -50%;
34
+ width: var(--md-comp-tap-target-size);
35
+ height: var(--md-comp-tap-target-size);
36
+ margin: 0;
37
+ padding: 0;
38
+ overflow: visible;
39
+ border: 0;
40
+ background: transparent;
41
+ color: var(--md-comp-tap-target-supporting-color);
8
42
  visibility: hidden;
9
- transition: visibility 0s .3s;
43
+ pointer-events: none;
44
+ z-index: var(--md-comp-tap-target-z);
45
+ transition:
46
+ visibility 0s var(--md-comp-tap-target-duration),
47
+ overlay var(--md-comp-tap-target-duration) allow-discrete,
48
+ display var(--md-comp-tap-target-duration) allow-discrete;
10
49
  }
11
50
 
12
- .tap-target-wrapper.open {
51
+ .tap-target-wrapper:is(.open, :popover-open) {
13
52
  visibility: visible;
14
- transition: visibility 0s;
53
+ pointer-events: none;
54
+ transition-delay: 0s;
15
55
 
16
- .tap-target {
56
+ .tap-target,
57
+ .feature-discovery {
17
58
  transform: scale(1);
18
- opacity: .95;
19
- transition:
20
- transform .3s cubic-bezier(.42,0,.58,1),
21
- opacity .3s cubic-bezier(.42,0,.58,1);
59
+ opacity: 1;
22
60
  }
23
61
 
24
62
  .tap-target-wave::before {
25
63
  transform: scale(1);
26
64
  }
65
+
27
66
  .tap-target-wave::after {
28
67
  visibility: visible;
29
68
  animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;
30
- transition:
31
- opacity .3s,
32
- transform .3s,
33
- visibility 0s 1s;
34
69
  }
35
70
  }
36
71
 
37
- .tap-target {
72
+ .tap-target,
73
+ .feature-discovery {
38
74
  position: absolute;
39
- font-size: 1rem;
40
- border-radius: 50%;
41
- background-color: var(--md-sys-color-primary-container);
42
- color: var(--md-sys-color-primary);
43
- box-shadow: 0 20px 20px 0 rgba(0, 0, 0, 0.14), 0 10px 50px 0 rgba(0, 0, 0, 0.12), 0 30px 10px -20px rgba(0, 0, 0, 0.2);
75
+ inset: 0;
76
+ display: grid;
77
+ box-sizing: border-box;
44
78
  width: 100%;
45
79
  height: 100%;
80
+ overflow: hidden;
81
+ border-radius: 50%;
82
+ background-color: var(--md-comp-tap-target-container-color);
83
+ color: var(--md-comp-tap-target-supporting-color);
46
84
  opacity: 0;
47
85
  transform: scale(0);
48
- transition: transform .3s cubic-bezier(.42, 0, .58, 1),
49
- opacity .3s cubic-bezier(.42, 0, .58, 1);
86
+ transition:
87
+ transform var(--md-comp-tap-target-duration) var(--md-comp-tap-target-easing),
88
+ opacity var(--md-comp-tap-target-duration) var(--md-comp-tap-target-easing);
89
+ @include z-depth("3");
90
+ @include typescale("body-medium");
50
91
  }
51
92
 
52
93
  .tap-target-content {
53
94
  position: relative;
54
- display: table-cell;
95
+ z-index: 1;
96
+ display: grid;
97
+ align-content: start;
98
+ gap: 8px;
99
+ box-sizing: border-box;
100
+ max-width: min(18rem, 52%);
101
+ padding: calc(var(--md-comp-tap-target-origin-size) * 0.5 + 20px);
102
+ pointer-events: auto;
103
+
104
+ > :is(h1, h2, h3, h4, h5, h6) {
105
+ margin: 0;
106
+ color: inherit;
107
+ @include typescale("title-medium");
108
+ }
109
+
110
+ > p {
111
+ margin: 0;
112
+ color: inherit;
113
+ }
114
+ }
115
+
116
+ .tap-target-wrapper[data-edge^="top"] .tap-target-content {
117
+ align-self: end;
118
+ }
119
+
120
+ .tap-target-wrapper[data-edge^="bottom"] .tap-target-content {
121
+ align-self: start;
122
+ }
123
+
124
+ .tap-target-wrapper[data-edge$="left"] .tap-target-content {
125
+ justify-self: end;
126
+ }
127
+
128
+ .tap-target-wrapper[data-edge$="right"] .tap-target-content {
129
+ justify-self: start;
130
+ }
131
+
132
+ .tap-target-wrapper[data-edge$="center"] .tap-target-content {
133
+ justify-self: center;
134
+ max-width: min(22rem, 80%);
135
+ text-align: center;
55
136
  }
56
137
 
57
138
  .tap-target-wave {
58
139
  position: absolute;
140
+ top: 50%;
141
+ left: 50%;
142
+ z-index: 0;
143
+ width: calc(var(--md-comp-tap-target-origin-size) * 2);
144
+ height: calc(var(--md-comp-tap-target-origin-size) * 2);
145
+ translate: -50% -50%;
59
146
  border-radius: 50%;
60
- z-index: 10001;
147
+ pointer-events: none;
61
148
 
62
149
  &::before,
63
150
  &::after {
64
- content: '';
65
- display: block;
151
+ content: "";
66
152
  position: absolute;
67
- width: 100%;
68
- height: 100%;
153
+ inset: 0;
154
+ display: block;
69
155
  border-radius: 50%;
70
- background-color: var(--md-sys-color-surface)
156
+ background-color: var(--md-sys-color-surface);
71
157
  }
158
+
72
159
  &::before {
73
160
  transform: scale(0);
74
- transition: transform .3s;
161
+ transition: transform var(--md-comp-tap-target-duration)
162
+ var(--md-comp-tap-target-easing);
75
163
  }
164
+
76
165
  &::after {
77
166
  visibility: hidden;
78
- transition:
79
- opacity .3s,
80
- transform .3s,
81
- visibility 0s;
82
167
  z-index: -1;
83
168
  }
84
169
  }
85
170
 
171
+ // Raised only on the non-popover path so a static origin stays above the
172
+ // circle. Never set position here — a FAB may already be fixed.
86
173
  .tap-target-origin {
87
- top: 50%;
88
- left: 50%;
89
- z-index: 10002;
90
- position: absolute !important;
91
- transform: translate(-50%,-50%);
174
+ z-index: calc(var(--md-comp-tap-target-z, 1000) + 2);
175
+ }
92
176
 
93
- &:not(.btn),
94
- &:not(.btn):hover {
95
- background: none;
177
+ @include bp-down("small") {
178
+ .tap-target-wrapper {
179
+ --md-comp-tap-target-size: 360px;
96
180
  }
97
181
  }
98
182
 
99
- @include bp-down("small") {
100
- .tap-target, .tap-target-wrapper {
101
- width: 600px;
102
- height: 600px;
183
+ @media (prefers-reduced-motion: reduce) {
184
+ .tap-target-wrapper {
185
+ --md-comp-tap-target-duration: 0s;
186
+ }
187
+
188
+ .tap-target-wrapper:is(.open, :popover-open) .tap-target-wave::after {
189
+ animation: none;
103
190
  }
104
191
  }