maverick-wave 5.38.0 → 6.0.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 (69) hide show
  1. package/README.md +63 -47
  2. package/maverick-wave.min.css +94 -68
  3. package/maverick-wave.min.js +1 -1
  4. package/package.json +7 -7
  5. package/src/js/main.js +382 -57
  6. package/src/scss/abstracts/_mixins.scss +35 -24
  7. package/src/scss/abstracts/_variables.scss +15 -0
  8. package/src/scss/base/_base.scss +107 -64
  9. package/src/scss/base/_index.scss +1 -0
  10. package/src/scss/base/_page-transitions.scss +29 -0
  11. package/src/scss/base/_reset.scss +0 -8
  12. package/src/scss/components/_accordions.scss +23 -18
  13. package/src/scss/components/_action-bar.scss +143 -0
  14. package/src/scss/components/_alerts.scss +13 -1
  15. package/src/scss/components/_avatars.scss +17 -0
  16. package/src/scss/components/_badge.scss +12 -0
  17. package/src/scss/components/_breadcrumbs.scss +10 -2
  18. package/src/scss/components/_chat.scss +250 -0
  19. package/src/scss/components/_code.scss +3 -1
  20. package/src/scss/components/_compare.scss +135 -0
  21. package/src/scss/components/_deck.scss +8 -14
  22. package/src/scss/components/_devices.scss +323 -0
  23. package/src/scss/components/_dropdown.scss +43 -2
  24. package/src/scss/components/_empty-state.scss +43 -0
  25. package/src/scss/components/_flag.scss +16 -0
  26. package/src/scss/components/_index.scss +6 -0
  27. package/src/scss/components/_lightbox.scss +1 -1
  28. package/src/scss/components/_marquee.scss +132 -0
  29. package/src/scss/components/_modals.scss +112 -83
  30. package/src/scss/components/_occasions.scss +62 -11
  31. package/src/scss/components/_pagination.scss +91 -0
  32. package/src/scss/components/_portrait-gallery.scss +1 -1
  33. package/src/scss/components/_progress.scss +122 -2
  34. package/src/scss/components/_segmented.scss +65 -2
  35. package/src/scss/components/_skeleton.scss +8 -0
  36. package/src/scss/components/_spinners.scss +38 -0
  37. package/src/scss/components/_stat.scss +232 -0
  38. package/src/scss/components/_stepper.scss +42 -2
  39. package/src/scss/components/_stories.scss +36 -22
  40. package/src/scss/components/_tables.scss +160 -17
  41. package/src/scss/components/_tabs.scss +42 -4
  42. package/src/scss/components/_tags.scss +39 -4
  43. package/src/scss/components/_tiles.scss +1 -1
  44. package/src/scss/components/_timelines.scss +138 -1
  45. package/src/scss/components/_toasts.scss +5 -1
  46. package/src/scss/components/_tooltip.scss +13 -0
  47. package/src/scss/form-elements/_checkbox.scss +40 -7
  48. package/src/scss/form-elements/_form.scss +2 -4
  49. package/src/scss/form-elements/_input.scss +9 -6
  50. package/src/scss/form-elements/_radio.scss +32 -3
  51. package/src/scss/form-elements/_select.scss +44 -1
  52. package/src/scss/form-elements/_slider.scss +18 -0
  53. package/src/scss/form-elements/_textarea.scss +9 -6
  54. package/src/scss/form-elements/_toggle.scss +37 -4
  55. package/src/scss/layout/_app-shell.scss +661 -0
  56. package/src/scss/layout/_bento.scss +43 -0
  57. package/src/scss/layout/_footer.scss +114 -2
  58. package/src/scss/layout/_grid.scss +7 -2
  59. package/src/scss/layout/_header-reveal.scss +30 -17
  60. package/src/scss/layout/_header.scss +21 -4
  61. package/src/scss/layout/_index.scss +2 -0
  62. package/src/scss/layout/_parallax.scss +13 -11
  63. package/src/scss/utilities/_accessibility.scss +4 -19
  64. package/src/scss/utilities/_index.scss +1 -0
  65. package/src/scss/utilities/_print.scss +7 -3
  66. package/src/scss/utilities/_reveal.scss +25 -17
  67. package/src/scss/utilities/_shape.scss +24 -0
  68. package/src/scss/utilities/_touch-targets.scss +23 -0
  69. package/src/scss/utilities/_variants.scss +25 -6
@@ -1,5 +1,7 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
+ $_segment-on: ':is([aria-pressed="true"], [aria-checked="true"], [aria-selected="true"], :has(> input:checked))';
4
+
3
5
  @layer mw.components {
4
6
  // Two or three positions of *one* switch - income/expense on a booking form,
5
7
  // offer/quote on a document form.
@@ -40,7 +42,14 @@
40
42
  cursor: pointer;
41
43
  transition: var(--mw-transition);
42
44
 
43
- &:hover:not(:disabled):not(.mw-active):not(.active) {
45
+ &:hover:not(
46
+ :disabled,
47
+ [aria-disabled='true'],
48
+ :has(> input:disabled),
49
+ .mw-active,
50
+ .active,
51
+ #{$_segment-on}
52
+ ) {
44
53
  color: var(--mw-text-color);
45
54
  }
46
55
 
@@ -54,15 +63,19 @@
54
63
  // Inset, because an outline outside the item would be clipped by the frame
55
64
  @include focus-ring-inset;
56
65
 
57
- &:disabled {
66
+ &:is(:disabled, [aria-disabled='true']),
67
+ &:has(> input:disabled) {
58
68
  cursor: not-allowed;
59
69
  opacity: 0.6;
60
70
  }
61
71
 
72
+ // The state from ARIA or from a native radio inside, as much as from the class
62
73
  @include active() {
63
74
  background: var(--mw-primary-color);
64
75
  border-color: var(--mw-primary-text-color);
65
76
  color: var(--mw-primary-accent-text-color);
77
+ // The ring ink is the fill's own tone; the label's ink stands out on it
78
+ --mw-focus-ring-color: currentColor;
66
79
 
67
80
  // A tag inside an item - the saving on a billing cycle, a count on a
68
81
  // filter. Its colour variants mix their background from the *page*, so on
@@ -101,4 +114,54 @@
101
114
  flex: 0 1 auto;
102
115
  }
103
116
  }
117
+
118
+ .mw-segmented-item#{$_segment-on} {
119
+ background: var(--mw-primary-color);
120
+ border-color: var(--mw-primary-text-color);
121
+ color: var(--mw-primary-accent-text-color);
122
+ --mw-focus-ring-color: currentColor;
123
+
124
+ .mw-tag {
125
+ background: transparent;
126
+ border-color: currentColor;
127
+ color: inherit;
128
+ }
129
+ }
130
+
131
+ .mw-segmented-secondary .mw-segmented-item#{$_segment-on} {
132
+ background: var(--mw-secondary-color);
133
+ border-color: var(--mw-secondary-text-color);
134
+ color: var(--mw-secondary-accent-text-color);
135
+ }
136
+
137
+ // A native radio group: unseen, but kept for the keyboard and the form value
138
+ .mw-segmented-item:has(> input) {
139
+ position: relative;
140
+ }
141
+
142
+ .mw-segmented-item > input[type='radio'] {
143
+ position: absolute;
144
+ inset: 0;
145
+ width: 100%;
146
+ height: 100%;
147
+ margin: 0;
148
+ opacity: 0;
149
+ cursor: inherit;
150
+ }
151
+
152
+ .mw-segmented-item:has(> input:focus-visible) {
153
+ outline: var(--mw-focus-ring-width) solid var(--mw-focus-ring-color);
154
+ outline-offset: -2px;
155
+ }
156
+
157
+ // Forced colours drop the fill that is the whole state
158
+ @media (forced-colors: active) {
159
+ .mw-segmented-item:is(.mw-active, .active),
160
+ .mw-segmented-item#{$_segment-on} {
161
+ forced-color-adjust: none;
162
+ background: Highlight;
163
+ border-color: Highlight;
164
+ color: HighlightText;
165
+ }
166
+ }
104
167
  }
@@ -81,4 +81,12 @@
81
81
  gap: spacing('3');
82
82
  }
83
83
  }
84
+
85
+ // The placeholder is nothing but a fill, which forced colours would drop
86
+ @media (forced-colors: active) {
87
+ .mw-skeleton {
88
+ forced-color-adjust: none;
89
+ background: GrayText;
90
+ }
91
+ }
84
92
  }
@@ -201,4 +201,42 @@
201
201
  opacity: 0;
202
202
  }
203
203
  }
204
+
205
+ // In a button or a line of text: sized and coloured by the text around it
206
+ .mw-spinner-inline {
207
+ display: inline-block;
208
+ flex-shrink: 0;
209
+ width: 1em;
210
+ height: 1em;
211
+ vertical-align: -0.125em;
212
+ border: 0.14em solid color-mix(in srgb, currentColor 25%, transparent);
213
+ border-top-color: currentColor;
214
+ border-radius: radius('full');
215
+ animation-name: mw-spin;
216
+ animation-duration: motion(0.8s);
217
+ animation-timing-function: linear;
218
+ animation-iteration-count: infinite;
219
+ }
220
+
221
+ // One pass at 1ms would leave the dots at their scale(0) end, i.e. gone
222
+ .mw-spinner-dots > div {
223
+ @include motion-reduced {
224
+ animation-name: none;
225
+ }
226
+ }
227
+
228
+ // Ring, arcs and dots are borders and fills that forced colours flatten
229
+ @media (forced-colors: active) {
230
+ .mw-spinner-border,
231
+ .mw-spinner-dots,
232
+ .mw-spinner-dual-ring,
233
+ .mw-spinner-inline {
234
+ forced-color-adjust: none;
235
+ --mw-primary-color: CanvasText;
236
+ --mw-secondary-color: CanvasText;
237
+ --mw-primary-text-color: CanvasText;
238
+ --mw-secondary-text-color: CanvasText;
239
+ --mw-border: GrayText;
240
+ }
241
+ }
204
242
  }
@@ -0,0 +1,232 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ // `width: 100%` because inline-size containment otherwise sizes it as if empty
5
+ .mw-stat {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: spacing('1');
9
+ width: 100%;
10
+ min-width: 0;
11
+ container: mw-stat / inline-size;
12
+ }
13
+
14
+ .mw-stat-label {
15
+ font-size: font-size('sm');
16
+ font-weight: font-weight('medium');
17
+ color: var(--mw-text-muted-color);
18
+ }
19
+
20
+ .mw-stat-value {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ align-items: baseline;
24
+ gap: 0 spacing('1');
25
+ font-size: fluid-container('2xl', '4xl', 180px, 420px);
26
+ font-weight: font-weight('bold');
27
+ line-height: 1.1;
28
+ font-variant-numeric: tabular-nums lining-nums;
29
+ color: var(--mw-text-color);
30
+ }
31
+
32
+ .mw-stat-unit {
33
+ font-size: 0.5em;
34
+ font-weight: font-weight('medium');
35
+ color: var(--mw-text-muted-color);
36
+ }
37
+
38
+ .mw-stat-footer {
39
+ display: flex;
40
+ flex-wrap: wrap;
41
+ align-items: center;
42
+ gap: spacing('1') spacing('3');
43
+ margin-top: spacing('1');
44
+ }
45
+
46
+ .mw-stat-note {
47
+ font-size: font-size('xs');
48
+ color: var(--mw-text-muted-color);
49
+ }
50
+
51
+ // The shape carries the direction, the colour only repeats it
52
+ .mw-stat-delta {
53
+ --mw-stat-delta-ink: var(--mw-text-muted-color);
54
+ --mw-stat-delta-tint: var(--mw-gray-background);
55
+
56
+ display: inline-flex;
57
+ align-items: center;
58
+ gap: spacing('1');
59
+ padding: spacing('1') spacing('3');
60
+ border-radius: radius('xl');
61
+ background: var(--mw-stat-delta-tint);
62
+ color: var(--mw-text-color);
63
+ font-size: font-size('xs');
64
+ font-weight: font-weight('medium');
65
+ font-variant-numeric: tabular-nums lining-nums;
66
+ white-space: nowrap;
67
+ }
68
+
69
+ :is(.mw-stat-delta-up, .mw-stat-delta-down, .mw-stat-delta-flat)::before {
70
+ content: '';
71
+ width: 0.6em;
72
+ height: 0.6em;
73
+ background: var(--mw-stat-delta-ink);
74
+ }
75
+
76
+ .mw-stat-delta-flat::before {
77
+ clip-path: inset(38% 0);
78
+ }
79
+
80
+ .mw-stat-delta-up,
81
+ .mw-stat-delta-invert.mw-stat-delta-down {
82
+ --mw-stat-delta-ink: var(--mw-success-text-color);
83
+ --mw-stat-delta-tint: var(--mw-success-info-background);
84
+ }
85
+
86
+ .mw-stat-delta-down,
87
+ .mw-stat-delta-invert.mw-stat-delta-up {
88
+ --mw-stat-delta-ink: var(--mw-danger-text-color);
89
+ --mw-stat-delta-tint: var(--mw-danger-info-background);
90
+ }
91
+
92
+ .mw-stat-delta-up::before {
93
+ clip-path: polygon(50% 5%, 100% 90%, 0 90%);
94
+ }
95
+
96
+ .mw-stat-delta-down::before {
97
+ clip-path: polygon(0 10%, 100% 10%, 50% 95%);
98
+ }
99
+
100
+ // A drawn shape loses its background under forced colours, text does not
101
+ @media (forced-colors: active) {
102
+ .mw-stat-delta::before {
103
+ background: CanvasText;
104
+ }
105
+ }
106
+
107
+ // For an inline <svg> sparkline - the line takes the ink, whatever the viewBox
108
+ .mw-stat-chart {
109
+ height: 2.5rem;
110
+ margin-top: spacing('2');
111
+ color: var(--mw-primary-text-color);
112
+
113
+ svg {
114
+ display: block;
115
+ width: 100%;
116
+ height: 100%;
117
+ overflow: visible;
118
+ }
119
+
120
+ :is(path, polyline, line) {
121
+ fill: none;
122
+ stroke: currentcolor;
123
+ stroke-width: 2;
124
+ stroke-linecap: round;
125
+ stroke-linejoin: round;
126
+ vector-effect: non-scaling-stroke;
127
+ }
128
+ }
129
+
130
+ .mw-stat-group {
131
+ display: flex;
132
+ // Spelled out: a card on the same element would turn it into a column
133
+ flex-direction: row;
134
+ flex-wrap: wrap;
135
+ width: 100%;
136
+ container: mw-stat-group / inline-size;
137
+
138
+ > .mw-stat {
139
+ flex: 1 1 100%;
140
+ padding-block: spacing('4');
141
+
142
+ & + .mw-stat {
143
+ border-top: 1px solid var(--mw-border);
144
+ }
145
+
146
+ &:first-child {
147
+ padding-top: 0;
148
+ }
149
+
150
+ &:last-child {
151
+ padding-bottom: 0;
152
+ }
153
+ }
154
+ }
155
+
156
+ // A container cannot query itself, so the children decide whether they wrap
157
+ @container mw-stat-group (width >= 30rem) {
158
+ .mw-stat-group > .mw-stat {
159
+ flex: 1 1 0;
160
+ padding: 0 spacing('5');
161
+
162
+ & + .mw-stat {
163
+ border-top: 0;
164
+ border-inline-start: 1px solid var(--mw-border);
165
+ }
166
+
167
+ &:first-child {
168
+ padding-inline-start: 0;
169
+ }
170
+
171
+ &:last-child {
172
+ padding-inline-end: 0;
173
+ }
174
+ }
175
+ }
176
+
177
+ // The native element keeps low / high / optimum, and with them the three tones
178
+ .mw-meter {
179
+ appearance: none;
180
+ display: block;
181
+ width: 100%;
182
+ height: 10px;
183
+ border: 0;
184
+ border-radius: radius('sm');
185
+ background: var(--mw-gray-background);
186
+ overflow: hidden;
187
+
188
+ &::-webkit-meter-bar {
189
+ height: 100%;
190
+ border: 0;
191
+ border-radius: radius('sm');
192
+ background: var(--mw-gray-background);
193
+ }
194
+
195
+ &::-webkit-meter-optimum-value {
196
+ border-radius: radius('sm');
197
+ background: var(--mw-success-color);
198
+ }
199
+
200
+ &::-webkit-meter-suboptimum-value {
201
+ border-radius: radius('sm');
202
+ background: var(--mw-warning-color);
203
+ }
204
+
205
+ &::-webkit-meter-even-less-good-value {
206
+ border-radius: radius('sm');
207
+ background: var(--mw-danger-color);
208
+ }
209
+
210
+ &::-moz-meter-bar {
211
+ border-radius: radius('sm');
212
+ background: var(--mw-success-color);
213
+ }
214
+
215
+ &:-moz-meter-sub-optimum::-moz-meter-bar {
216
+ background: var(--mw-warning-color);
217
+ }
218
+
219
+ &:-moz-meter-sub-sub-optimum::-moz-meter-bar {
220
+ background: var(--mw-danger-color);
221
+ }
222
+ }
223
+
224
+ .mw-meter-sm {
225
+ height: 6px;
226
+ }
227
+
228
+ .mw-meter-lg {
229
+ height: 15px;
230
+ border-radius: radius('lg');
231
+ }
232
+ }
@@ -49,7 +49,9 @@
49
49
  // filled circle and so is every finished one, which leaves the hue as the
50
50
  // only thing telling them apart - and a stepper is read at a glance, by
51
51
  // people who do not all see blue against green.
52
- &.mw-active {
52
+ // aria-current="step" on the step marks it as well, for an app's markup
53
+ &.mw-active,
54
+ .mw-stepper-step[aria-current='step'] > & {
53
55
  border-color: var(--mw-primary-color);
54
56
  background: var(--mw-primary-color);
55
57
  color: var(--mw-primary-accent-text-color);
@@ -70,7 +72,8 @@
70
72
  line-height: 1.2;
71
73
  transition: color var(--mw-duration-base) var(--mw-ease-out);
72
74
 
73
- &.mw-active {
75
+ &.mw-active,
76
+ .mw-stepper-step[aria-current='step'] > & {
74
77
  color: var(--mw-primary-text-color);
75
78
  font-weight: font-weight('medium');
76
79
  }
@@ -147,4 +150,41 @@
147
150
  }
148
151
  }
149
152
  }
153
+
154
+ // Forced colours drop the fills: current in Highlight, done inverted
155
+ @media (forced-colors: active) {
156
+ .mw-stepper-indicator,
157
+ .mw-stepper-connector,
158
+ .mw-stepper-vertical .mw-stepper-step:not(:last-child)::after {
159
+ forced-color-adjust: none;
160
+ }
161
+
162
+ .mw-stepper-connector,
163
+ .mw-stepper-vertical .mw-stepper-step:not(:last-child)::after {
164
+ background: GrayText;
165
+ }
166
+
167
+ .mw-stepper-connector.mw-done,
168
+ .mw-stepper-vertical .mw-stepper-step.mw-done:not(:last-child)::after {
169
+ background: CanvasText;
170
+ }
171
+
172
+ .mw-stepper-indicator {
173
+ border-color: CanvasText;
174
+ background: Canvas;
175
+ color: CanvasText;
176
+ }
177
+
178
+ .mw-stepper-indicator.mw-done {
179
+ background: CanvasText;
180
+ color: Canvas;
181
+ }
182
+
183
+ .mw-stepper-indicator.mw-active,
184
+ .mw-stepper-step[aria-current='step'] > .mw-stepper-indicator {
185
+ border-color: Highlight;
186
+ background: Highlight;
187
+ color: HighlightText;
188
+ }
189
+ }
150
190
  }
@@ -308,8 +308,8 @@
308
308
  }
309
309
 
310
310
  // Longhands only in this file: the shorthand would reset the scroll timeline
311
- @media (prefers-reduced-motion: no-preference) {
312
- .mw-story-reel {
311
+ .mw-story-reel {
312
+ @include motion-safe {
313
313
  overflow: hidden;
314
314
  scroll-snap-type: none;
315
315
 
@@ -338,21 +338,25 @@
338
338
  display: none;
339
339
  }
340
340
  }
341
+ }
341
342
 
342
- .mw-story-reel-head::after {
343
+ .mw-story-reel-head::after {
344
+ @include motion-safe {
343
345
  clip-path: inset(0 100% 0 0);
344
346
  animation-name: mw-story-reel-fill;
345
347
  animation-duration: $frame-time;
346
348
  animation-timing-function: linear;
347
349
  animation-fill-mode: forwards;
348
350
  }
351
+ }
349
352
 
350
- // One segment per frame, and the fill takes as long as all of them
351
- @for $frames from 2 through $max-frames {
352
- .mw-story-reel:has(
353
- > :nth-child(#{$frames} of img, video):nth-last-child(1 of img, video)
354
- )
355
- .mw-story-reel-head {
353
+ // One segment per frame, and the fill takes as long as all of them
354
+ @for $frames from 2 through $max-frames {
355
+ .mw-story-reel:has(
356
+ > :nth-child(#{$frames} of img, video):nth-last-child(1 of img, video)
357
+ )
358
+ .mw-story-reel-head {
359
+ @include motion-safe {
356
360
  &::before {
357
361
  background: segments(rgb(255 255 255 / 35%), $frames);
358
362
  }
@@ -363,10 +367,12 @@
363
367
  }
364
368
  }
365
369
  }
370
+ }
366
371
 
367
- // A longer story ends at the last timed frame
368
- .mw-story-reel:has(> :nth-child(#{$max-frames + 1} of img, video))
369
- .mw-story-reel-head {
372
+ // A longer story ends at the last timed frame
373
+ .mw-story-reel:has(> :nth-child(#{$max-frames + 1} of img, video))
374
+ .mw-story-reel-head {
375
+ @include motion-safe {
370
376
  &::before {
371
377
  background: segments(rgb(255 255 255 / 35%), $max-frames);
372
378
  }
@@ -376,8 +382,10 @@
376
382
  animation-duration: $max-frames * $frame-time;
377
383
  }
378
384
  }
385
+ }
379
386
 
380
- .mw-story-reel:is(:active, :has(.mw-story-reel-pause :checked)) {
387
+ .mw-story-reel:is(:active, :has(.mw-story-reel-pause :checked)) {
388
+ @include motion-safe {
381
389
  > *,
382
390
  .mw-story-reel-head::after {
383
391
  animation-play-state: paused;
@@ -386,29 +394,35 @@
386
394
  }
387
395
 
388
396
  // No autoplay with reduced motion: the frames stay a strip to page through
389
- @media (prefers-reduced-motion: reduce) {
390
- .mw-story-reel-pause,
391
- .mw-story-reel-prev,
392
- .mw-story-reel-next {
397
+ .mw-story-reel-pause,
398
+ .mw-story-reel-prev,
399
+ .mw-story-reel-next {
400
+ @include motion-reduced {
393
401
  display: none;
394
402
  }
403
+ }
395
404
 
396
- .mw-story-reel-head::after {
405
+ .mw-story-reel-head::after {
406
+ @include motion-reduced {
397
407
  transform-origin: left;
398
408
  transform: scaleX(0);
399
409
  }
410
+ }
400
411
 
401
- @supports (animation-timeline: scroll()) {
402
- .mw-story-reel-head::after {
412
+ @supports (animation-timeline: scroll()) {
413
+ .mw-story-reel-head::after {
414
+ @include motion-reduced {
403
415
  animation-name: mw-story-reel-scroll;
404
416
  animation-timing-function: linear;
405
417
  animation-fill-mode: both;
406
418
  animation-timeline: scroll();
407
419
  }
408
420
  }
421
+ }
409
422
 
410
- @supports not (animation-timeline: scroll()) {
411
- .mw-story-reel-head {
423
+ @supports not (animation-timeline: scroll()) {
424
+ .mw-story-reel-head {
425
+ @include motion-reduced {
412
426
  &::before,
413
427
  &::after {
414
428
  display: none;