maverick-wave 5.6.0 → 5.7.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.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "config": {
5
- "version_short": "5.6"
5
+ "version_short": "5.7"
6
6
  },
7
7
  "description": "A lightweight, modern CSS framework for building responsive websites with elegance and speed.",
8
8
  "main": "src/js/main.js",
@@ -56,7 +56,7 @@
56
56
  <div class="mw-timeline-simple-date">2025-04-30</div>
57
57
  <div class="mw-timeline-simple-content">
58
58
  <div class="mw-card mw-card-simple">
59
- <h3 class="mw-card-title">v1.3.0</h3>
59
+ <h3 class="mw-card-title">v1.0.0</h3>
60
60
  <p>
61
61
  Let's say initial release - grid, header and a first handful of
62
62
  components, published as they were.
@@ -143,7 +143,8 @@
143
143
  <h3 class="palette-heading palette-heading-split">Variants</h3>
144
144
  <p class="palette-hint">
145
145
  Every switch is one class on &lt;html&gt; or one custom property - no build,
146
- no rebuild. The setup at the bottom is what a project would carry.
146
+ no rebuild. The box at the bottom is what a project would carry, and it
147
+ reads back: paste someone else's setup in to see it.
147
148
  </p>
148
149
 
149
150
  <div id="variant-list">
@@ -190,24 +191,24 @@
190
191
 
191
192
  <label class="variant-row">
192
193
  <span class="variant-label">
193
- Single accent color
194
+ Glass buttons
194
195
  <small
195
- >Drop the second brand colour - hovers, tints and borders all come
196
- from the first one.</small
196
+ >Filled buttons become tinted glass - the colour drops to a
197
+ translucent wash with a lit top edge.</small
197
198
  >
198
199
  </span>
199
- <input type="checkbox" data-variant-class="mw-accent-single" />
200
+ <input type="checkbox" data-variant-class="mw-btn-glass" />
200
201
  </label>
201
202
 
202
203
  <label class="variant-row">
203
204
  <span class="variant-label">
204
- Photos in grey
205
+ Single accent color
205
206
  <small
206
- >Pictures stay grey until the pointer reaches them, so only the brand
207
- carries colour.</small
207
+ >Drop the second brand colour - hovers, tints and borders all come
208
+ from the first one.</small
208
209
  >
209
210
  </span>
210
- <input type="checkbox" data-variant-class="mw-media-mono" />
211
+ <input type="checkbox" data-variant-class="mw-accent-single" />
211
212
  </label>
212
213
 
213
214
  <label class="variant-row">
@@ -256,6 +257,22 @@
256
257
  <input type="checkbox" data-variant-class="mw-hover-static" />
257
258
  </label>
258
259
 
260
+ <label class="variant-row">
261
+ <span class="variant-label">
262
+ Scroll reveal
263
+ <small
264
+ >Cards and sections rise into place as they scroll in. Off has
265
+ everything sit where it lands.</small
266
+ >
267
+ </span>
268
+ <input
269
+ type="checkbox"
270
+ checked
271
+ data-variant-class="mw-scroll-static"
272
+ data-variant-invert
273
+ />
274
+ </label>
275
+
259
276
  <label class="variant-row">
260
277
  <span class="variant-label">
261
278
  Sticky header
@@ -321,5 +338,15 @@
321
338
  </label>
322
339
  </div>
323
340
 
324
- <pre class="variant-setup" id="variant-setup"></pre>
341
+ <label class="variant-setup-label" for="variant-setup">
342
+ Setup - editable, paste one in to load it
343
+ </label>
344
+ <textarea
345
+ class="variant-setup"
346
+ id="variant-setup"
347
+ rows="4"
348
+ spellcheck="false"
349
+ autocapitalize="off"
350
+ autocorrect="off"
351
+ ></textarea>
325
352
  </div>
@@ -19,6 +19,14 @@
19
19
  <blockquote>
20
20
  In the complex world of code, simplicity is the ultimate sophistication.
21
21
  </blockquote>
22
+ <p>
23
+ <span class="mw-text-xs">Extra Small Text</span><br />
24
+ <span class="mw-text-sm">Small Text</span><br />
25
+ <span class="mw-text-base">Base Text</span><br />
26
+ <span class="mw-text-md">Medium Text</span><br />
27
+ <span class="mw-text-lg">Large Text</span><br />
28
+ <span class="mw-text-xl">Extra Large Text</span>
29
+ </p>
22
30
  </div>
23
31
 
24
32
  <div class="mw-grid-stack mw-gap-2">
@@ -59,14 +67,5 @@
59
67
  <p class="mw-text-color-light">
60
68
  This always remains the text color of the light theme.
61
69
  </p>
62
-
63
- <p>
64
- <span class="mw-text-xs">Extra Small Text</span><br />
65
- <span class="mw-text-sm">Small Text</span><br />
66
- <span class="mw-text-base">Base Text</span><br />
67
- <span class="mw-text-md">Medium Text</span><br />
68
- <span class="mw-text-lg">Large Text</span><br />
69
- <span class="mw-text-xl">Extra Large Text</span>
70
- </p>
71
70
  </div>
72
71
  </div>
@@ -426,10 +426,10 @@
426
426
 
427
427
  <h3 class="mw-mb-4 mw-mt-6">Site-wide Variants</h3>
428
428
  <p class="mw-text-muted mw-text-measure">
429
- Eight classes on <code>&lt;html&gt;</code> retune the whole look without
429
+ Nine classes on <code>&lt;html&gt;</code> retune the whole look without
430
430
  touching markup. They stack, and the palette button in the corner switches all
431
431
  of them live - the box at the bottom of that panel prints the setup a project
432
- would carry.
432
+ would carry, and reads it back: paste one in to see someone else's.
433
433
  </p>
434
434
  <ul class="mw-list mw-list-dot mw-text-measure">
435
435
  <li>
@@ -448,6 +448,10 @@
448
448
  <code>mw-hover-static</code> - no hover travels; colour and border still
449
449
  respond
450
450
  </li>
451
+ <li>
452
+ <code>mw-scroll-static</code> - no scroll entrance;
453
+ <code>mw-reveal</code> blocks sit where they land
454
+ </li>
451
455
  <li>
452
456
  <code>mw-sections-plain</code> - the hatch behind
453
457
  <code>mw-section-alternate</code> collapses into the page colour
@@ -457,7 +461,10 @@
457
461
  <code>mw-btn-pill</code> - fully rounded buttons, form fields keep their
458
462
  radius
459
463
  </li>
460
- <li><code>mw-media-mono</code> - photos grey until hovered</li>
464
+ <li>
465
+ <code>mw-btn-glass</code> - filled buttons become tinted glass with a lit
466
+ top edge
467
+ </li>
461
468
  </ul>
462
469
  <p class="mw-text-muted mw-text-measure">
463
470
  Four properties do the rest: <code>--mw-radius-scale</code> multiplies every
@@ -133,9 +133,14 @@
133
133
  // opt-outs than a colour change does: reduced motion, where a transform still
134
134
  // jumps to its end position in one frame however short the duration is, and the
135
135
  // static-hover variant. Colour and shadow belong in `hover` and stay either way.
136
+ //
137
+ // A tooltip trigger is a third: a transform makes the element a containing block
138
+ // for its own fixed-position descendants, and the tooltip is exactly that - so a
139
+ // lifted trigger drags the bubble down onto itself. The two cannot both happen,
140
+ // and a label that lands where it belongs beats two pixels of travel.
136
141
  @mixin hover-move {
137
142
  @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
138
- :root:not(.mw-hover-static) &:hover {
143
+ :root:not(.mw-hover-static) &:hover:not([data-tooltip]) {
139
144
  @content;
140
145
  }
141
146
  }
@@ -91,12 +91,12 @@ $shadow-far-dark: 16% !default;
91
91
  $shadow-near-light: 21% !default;
92
92
  $shadow-far-light: 14% !default;
93
93
 
94
- // Control sizes - 32 / 36 / 40px, shared by input, select, textarea and button so
94
+ // Control sizes - 30 / 34 / 38px, shared by input, select, textarea and button so
95
95
  // the row lines up. A minimum, not a fixed height, and it is what sets a field's
96
96
  // height: trimming `padding-block` moves the text inside the box, not the box.
97
- $control-height-sm: 2rem !default;
98
- $control-height: 2.25rem !default;
99
- $control-height-lg: 2.5rem !default;
97
+ $control-height-sm: 1.875rem !default;
98
+ $control-height: 2.125rem !default;
99
+ $control-height-lg: 2.375rem !default;
100
100
 
101
101
  // All three fields share it; buttons stay one step above, because a button
102
102
  // carries a label and a field carries what the user typed
@@ -22,6 +22,35 @@
22
22
  height: 100%;
23
23
  object-fit: cover;
24
24
  display: block;
25
+ transition: transform var(--mw-duration-slow) var(--mw-ease-out);
26
+ }
27
+
28
+ // `:has(img)` keeps the sweep off the initials variant, where it would just
29
+ // flash a white bar across the letters
30
+ &:has(img)::after {
31
+ content: '';
32
+ position: absolute;
33
+ inset: 0;
34
+ background: linear-gradient(
35
+ 115deg,
36
+ transparent 38%,
37
+ rgb(255 255 255 / 0.45) 50%,
38
+ transparent 62%
39
+ );
40
+ transform: translateX(-130%);
41
+ pointer-events: none;
42
+ }
43
+
44
+ @include hover-move {
45
+ img {
46
+ transform: scale(1.12);
47
+ }
48
+
49
+ // Transition only here, so the sweep resets instantly instead of travelling back
50
+ &:has(img)::after {
51
+ transform: translateX(130%);
52
+ transition: transform var(--mw-duration-slower) var(--mw-ease-out);
53
+ }
25
54
  }
26
55
 
27
56
  &-xs {
@@ -132,7 +132,7 @@
132
132
  gap: spacing('3');
133
133
 
134
134
  .mw-btn {
135
- padding: spacing('3') spacing('5');
135
+ padding: spacing('2') spacing('5');
136
136
  font-size: font-size('md');
137
137
  min-height: var(--mw-control-height-lg);
138
138
  }
@@ -242,7 +242,7 @@
242
242
  min-height: var(--mw-control-height-sm);
243
243
  }
244
244
  .mw-btn-lg {
245
- padding: spacing('3') spacing('5');
245
+ padding: spacing('2') spacing('5');
246
246
  font-size: font-size('md');
247
247
  min-height: var(--mw-control-height-lg);
248
248
  }
@@ -3,7 +3,7 @@
3
3
  @layer mw.forms {
4
4
  .mw-input {
5
5
  width: 100%;
6
- padding: spacing('2') spacing('3');
6
+ padding: spacing('1') spacing('3');
7
7
  font-size: var(--mw-control-font);
8
8
  line-height: var(--mw-control-line-height);
9
9
  min-height: var(--mw-control-height);
@@ -3,7 +3,7 @@
3
3
  @layer mw.forms {
4
4
  .mw-select {
5
5
  width: 100%;
6
- padding: spacing('2') spacing('6') spacing('2') spacing('3');
6
+ padding: spacing('1') spacing('6') spacing('1') spacing('3');
7
7
  font-size: var(--mw-control-font);
8
8
  min-height: var(--mw-control-height);
9
9
  background: var(--mw-form-elements-background);
@@ -157,8 +157,23 @@
157
157
  padding: spacing('2') spacing('5');
158
158
  border: 1px solid #{fade('hero-text-color', 22%)};
159
159
  border-radius: 999px;
160
- background: fade('hero-text-color', 8%);
161
- backdrop-filter: blur(4px);
160
+ // A vertical ramp and not a flat tint: glass is lit from above, and the
161
+ // gradient is what gives the pill a thickness instead of a fogged panel
162
+ background: linear-gradient(
163
+ to bottom,
164
+ #{fade('hero-text-color', 16%)},
165
+ #{fade('hero-text-color', 5%)}
166
+ );
167
+ // saturate() is what separates glass from frosted plastic: the blur alone
168
+ // greys out what shows through, and pushing the chroma back up is how a
169
+ // real lens reads. The two inset lines are the specular highlight - light
170
+ // catching the top edge, a fainter bounce off the bottom - and the outer
171
+ // shadow is what lifts the pill off the photo instead of pasting it on.
172
+ backdrop-filter: blur(14px) saturate(180%);
173
+ box-shadow:
174
+ inset 0 1px 0 #{fade('hero-text-color', 40%)},
175
+ inset 0 -1px 0 #{fade('hero-text-color', 12%)},
176
+ 0 2px 12px rgb(0 0 0 / 18%);
162
177
  color: fade('hero-text-color', 88%);
163
178
  font-size: font-size('sm');
164
179
  font-weight: font-weight('medium');
@@ -311,6 +326,20 @@
311
326
  }
312
327
  }
313
328
 
329
+ // A press usually scales the trigger, and a transform makes it a containing
330
+ // block for the fixed-position bubble above - which would drop the label
331
+ // into the element being pressed. So it steps aside for the click instead;
332
+ // by then it has been read anyway. After the hover rule, same weight.
333
+ &:active::before,
334
+ &:active::after {
335
+ opacity: 0;
336
+ display: none;
337
+ // Not transitioned: `allow-discrete` holds `display: block` until the fade
338
+ // ends, and the bubble would spend those 180ms visibly sitting in the
339
+ // element. Going back out on release keeps the fade - that one is wanted.
340
+ transition: none;
341
+ }
342
+
314
343
  // The clipping caveat above, lifted where the browser can do it: `fixed` takes
315
344
  // the bubble out of the trigger's containing block, anchor positioning keeps
316
345
  // it attached. No `anchor-name` needed - a pseudo element is implicitly
@@ -11,8 +11,7 @@
11
11
  // `animation:` that cannot carry a timeline.
12
12
  @supports (animation-timeline: view()) {
13
13
  @media (prefers-reduced-motion: no-preference) {
14
- .mw-reveal,
15
- .mw-reveal-stagger > * {
14
+ :root:not(.mw-scroll-static) :is(.mw-reveal, .mw-reveal-stagger > *) {
16
15
  animation-name: mw-reveal;
17
16
  animation-timing-function: var(--mw-ease-out);
18
17
  animation-fill-mode: backwards;
@@ -47,6 +46,14 @@
47
46
  transform: translateY(40px);
48
47
  }
49
48
 
49
+ // The script has already hidden whatever was below the fold when the class
50
+ // arrives, and it only ever un-hides on intersection - without this the
51
+ // switch would leave those elements invisible instead of static.
52
+ :root.mw-scroll-static .mw-reveal-hidden {
53
+ opacity: 1;
54
+ transform: none;
55
+ }
56
+
50
57
  .mw-reveal-run {
51
58
  // `backwards` for the reason above - a forwards fill would pin the
52
59
  // transform and cost the card its hover lift. The delay comes inline,
@@ -22,7 +22,7 @@
22
22
  min-height: 2.5rem;
23
23
  }
24
24
 
25
- // The base fields sit at 2.35rem from the control scale, which is right under
25
+ // The base fields sit at 2.125rem from the control scale, which is right under
26
26
  // a pointer and short of a thumb. Same floor as the button beside them.
27
27
  .mw-input,
28
28
  .mw-select {
@@ -57,25 +57,55 @@
57
57
  border-radius: 999px;
58
58
  }
59
59
 
60
- // Photos drained of colour until pointed at - the palette then comes only from
61
- // the brand tones. Avatars stay out: a greyed-out face reads as disabled, and
62
- // so does mw-media, which is the audio player and holds no picture at all.
63
- :root.mw-media-mono {
64
- .mw-card-img img,
65
- .mw-card-img-contain img,
66
- .mw-tile-img img,
67
- .mw-gallery img,
68
- .mw-image-slider img,
69
- img.mw-blog-post-featured-image {
70
- filter: grayscale(1);
71
- transition: filter var(--mw-duration-zoom) var(--mw-ease-out);
72
- }
60
+ // Tinted glass: a translucent wash with a vertical ramp and a specular line
61
+ // along the top edge, which is what gives the button its thickness.
62
+ //
63
+ // Deliberately without `backdrop-filter`, and that omission has to stay. The
64
+ // filter makes the button a containing block for its own fixed-position
65
+ // descendants, which drops a [data-tooltip] bubble inside the button instead
66
+ // of above it; and nested inside the header's own blur it flickers while the
67
+ // page scrolls. At the tint below there was next to nothing to see through.
68
+ //
69
+ // The tint stays heavy on purpose, and that is the other trade-off: a 20%
70
+ // wash reads as real glass, but the label then rides on whatever is behind
71
+ // it, and over a light card that contrast is gone. At 88/70 the label keeps
72
+ // roughly the fill's contrast.
73
+ //
74
+ // Hover and active are restated rather than inherited: this sits in
75
+ // mw.utilities, which outranks every rule in mw.components whatever its
76
+ // specificity, so the states would otherwise be flattened along with the fill.
77
+ :root.mw-btn-glass {
78
+ @each $name in ('primary', 'secondary', 'danger', 'success') {
79
+ .mw-btn-#{$name} {
80
+ background: linear-gradient(
81
+ to bottom,
82
+ color-mix(in srgb, var(--mw-#{$name}-color) 88%, transparent),
83
+ color-mix(in srgb, var(--mw-#{$name}-color) 70%, transparent)
84
+ );
85
+ border-color: color-mix(
86
+ in srgb,
87
+ var(--mw-#{$name}-color) 70%,
88
+ transparent
89
+ );
90
+ box-shadow:
91
+ inset 0 1px 0 rgb(255 255 255 / 28%),
92
+ 0 1px 3px rgb(0 0 0 / 14%);
73
93
 
74
- // The blog image carries the class itself rather than sitting in a wrapper,
75
- // so it is the one that has to answer for its own hover
76
- :is(.mw-card, .mw-tile, .mw-gallery, .mw-image-slider):hover img,
77
- img.mw-blog-post-featured-image:hover {
78
- filter: none;
94
+ &:hover:not(:disabled),
95
+ &.mw-active:not(:disabled),
96
+ &.active:not(:disabled) {
97
+ background: linear-gradient(
98
+ to bottom,
99
+ color-mix(in srgb, var(--mw-#{$name}-color) 97%, transparent),
100
+ color-mix(in srgb, var(--mw-#{$name}-color) 82%, transparent)
101
+ );
102
+ border-color: color-mix(
103
+ in srgb,
104
+ var(--mw-#{$name}-color) 85%,
105
+ transparent
106
+ );
107
+ }
108
+ }
79
109
  }
80
110
  }
81
111