maverick-wave 5.6.0 → 5.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.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "5.6.0",
3
+ "version": "5.8.0",
4
4
  "config": {
5
- "version_short": "5.6"
5
+ "version_short": "5.8"
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",
package/scripts/verify.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * 1. every var(--mw-*) has a definition
7
7
  * 2. every --mw-* definition is used somewhere
8
8
  * 3. every mw-* class in the markup exists in the built CSS
9
- * 4. the committed root release files exist and are not empty
9
+ * 4. the committed root release files match a fresh build
10
10
  * 5. the CDN version pinned in the docs matches package.json
11
11
  * 6. no `animation:` shorthand in a file that uses a scroll timeline
12
12
  *
@@ -135,6 +135,16 @@ for (const f of ['maverick-wave.min.css', 'maverick-wave.min.js']) {
135
135
  errors.push(
136
136
  `release file missing or empty: ${f} - run \`npm run prepack\` and commit it`
137
137
  );
138
+ continue;
139
+ }
140
+ // `gulp release` runs the same sass/postcss/terser pipeline as the dist tasks
141
+ // and only writes elsewhere, so dist/ is what the committed file has to equal.
142
+ // Existing-and-not-empty was not enough: 5.7.0 shipped a root bundle four
143
+ // commits behind src/, and every CDN consumer got that one.
144
+ if (read(p) !== read(path.join(ROOT, 'dist', f))) {
145
+ errors.push(
146
+ `release file stale: ${f} differs from the fresh build in dist/ - run \`npm run prepack\` and commit it`
147
+ );
138
148
  }
139
149
  }
140
150
 
@@ -41,14 +41,16 @@
41
41
  </div>
42
42
  </button>
43
43
 
44
- <!-- Login Button -->
44
+ <!-- Palette Switcher, showcase only - the panel lives in palette-container -->
45
45
  <button
46
46
  type="button"
47
- class="mw-login-btn"
48
- id="login-button"
49
- aria-label="Log in"
47
+ class="palette-toggle"
48
+ id="palette-toggle"
49
+ aria-expanded="false"
50
+ aria-controls="palette-panel"
51
+ aria-label="Try another palette"
50
52
  >
51
- <i class="fas fa-lock"></i>
53
+ <i class="fas fa-palette"></i>
52
54
  </button>
53
55
 
54
56
  <!-- Burger Menu Button -->
@@ -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.
@@ -1,14 +1,3 @@
1
- <button
2
- type="button"
3
- class="palette-fab"
4
- id="palette-fab"
5
- aria-expanded="false"
6
- aria-controls="palette-panel"
7
- aria-label="Try another palette"
8
- >
9
- <i class="fas fa-palette"></i>
10
- </button>
11
-
12
1
  <div class="palette-panel" id="palette-panel" hidden>
13
2
  <h3 class="palette-heading">Palette</h3>
14
3
  <p class="palette-hint">
@@ -25,7 +14,7 @@
25
14
  data-dark="#171f30"
26
15
  data-light="#e7e7f0"
27
16
  >
28
- Maverick
17
+ MaverickWave
29
18
  </button>
30
19
  <button
31
20
  type="button"
@@ -127,6 +116,46 @@
127
116
  >
128
117
  Wine
129
118
  </button>
119
+ <button
120
+ type="button"
121
+ class="palette-item"
122
+ data-primary="#cc8a04"
123
+ data-secondary="#2b5f9e"
124
+ data-dark="#231c0f"
125
+ data-light="#f3ecdc"
126
+ >
127
+ Amber
128
+ </button>
129
+ <button
130
+ type="button"
131
+ class="palette-item"
132
+ data-primary="#6da31c"
133
+ data-secondary="#a3319b"
134
+ data-dark="#171e10"
135
+ data-light="#ecf1e1"
136
+ >
137
+ Lime
138
+ </button>
139
+ <button
140
+ type="button"
141
+ class="palette-item"
142
+ data-primary="#5e2c6e"
143
+ data-secondary="#c9853a"
144
+ data-dark="#1c1221"
145
+ data-light="#ece5f0"
146
+ >
147
+ Aubergine
148
+ </button>
149
+ <button
150
+ type="button"
151
+ class="palette-item"
152
+ data-primary="#b0543a"
153
+ data-secondary="#3d7160"
154
+ data-dark="#231613"
155
+ data-light="#f3e8e4"
156
+ >
157
+ Clay
158
+ </button>
130
159
  </div>
131
160
 
132
161
  <div class="palette-custom">
@@ -143,7 +172,8 @@
143
172
  <h3 class="palette-heading palette-heading-split">Variants</h3>
144
173
  <p class="palette-hint">
145
174
  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.
175
+ no rebuild. The box at the bottom is what a project would carry, and it
176
+ reads back: paste someone else's setup in to see it.
147
177
  </p>
148
178
 
149
179
  <div id="variant-list">
@@ -180,34 +210,43 @@
180
210
 
181
211
  <label class="variant-row">
182
212
  <span class="variant-label">
183
- Pill buttons
213
+ Button shape
184
214
  <small
185
- >Buttons rounded all the way. Form fields keep their own shape.</small
215
+ >From cut corners to fully rounded. Form fields keep their own
216
+ shape.</small
186
217
  >
187
218
  </span>
188
- <input type="checkbox" data-variant-class="mw-btn-pill" />
219
+ <select data-variant-classes>
220
+ <option value="mw-btn-square">Square</option>
221
+ <option value="" selected>Default</option>
222
+ <option value="mw-btn-pill">Pill</option>
223
+ </select>
189
224
  </label>
190
225
 
191
226
  <label class="variant-row">
192
227
  <span class="variant-label">
193
- Single accent color
228
+ Button style
194
229
  <small
195
- >Drop the second brand colour - hovers, tints and borders all come
196
- from the first one.</small
230
+ >How a filled button is painted. Outline, ghost and link buttons keep
231
+ their own treatment.</small
197
232
  >
198
233
  </span>
199
- <input type="checkbox" data-variant-class="mw-accent-single" />
234
+ <select data-variant-classes>
235
+ <option value="" selected>Default</option>
236
+ <option value="mw-btn-glass">Glass</option>
237
+ <option value="mw-btn-tactile">Tactile</option>
238
+ </select>
200
239
  </label>
201
240
 
202
241
  <label class="variant-row">
203
242
  <span class="variant-label">
204
- Photos in grey
243
+ Single accent color
205
244
  <small
206
- >Pictures stay grey until the pointer reaches them, so only the brand
207
- carries colour.</small
245
+ >Drop the second brand colour - hovers, tints and borders all come
246
+ from the first one.</small
208
247
  >
209
248
  </span>
210
- <input type="checkbox" data-variant-class="mw-media-mono" />
249
+ <input type="checkbox" data-variant-class="mw-accent-single" />
211
250
  </label>
212
251
 
213
252
  <label class="variant-row">
@@ -245,6 +284,17 @@
245
284
  <input type="checkbox" data-variant-class="mw-shadows-flat" />
246
285
  </label>
247
286
 
287
+ <label class="variant-row">
288
+ <span class="variant-label">
289
+ Flush surfaces
290
+ <small
291
+ >Cards, panels and the footer drop their own tone and sit on the page
292
+ colour, separated by their border alone.</small
293
+ >
294
+ </span>
295
+ <input type="checkbox" data-variant-class="mw-surfaces-flush" />
296
+ </label>
297
+
248
298
  <label class="variant-row">
249
299
  <span class="variant-label">
250
300
  Static on hover
@@ -256,6 +306,22 @@
256
306
  <input type="checkbox" data-variant-class="mw-hover-static" />
257
307
  </label>
258
308
 
309
+ <label class="variant-row">
310
+ <span class="variant-label">
311
+ Scroll reveal
312
+ <small
313
+ >Cards and sections rise into place as they scroll in. Off has
314
+ everything sit where it lands.</small
315
+ >
316
+ </span>
317
+ <input
318
+ type="checkbox"
319
+ checked
320
+ data-variant-class="mw-scroll-static"
321
+ data-variant-invert
322
+ />
323
+ </label>
324
+
259
325
  <label class="variant-row">
260
326
  <span class="variant-label">
261
327
  Sticky header
@@ -285,6 +351,21 @@
285
351
  />
286
352
  </label>
287
353
 
354
+ <label class="variant-row">
355
+ <span class="variant-label">
356
+ Text size
357
+ <small
358
+ >The base the whole scale is built on - type, padding and control
359
+ heights move together.</small
360
+ >
361
+ </span>
362
+ <select data-variant-prop="--mw-root-font-size">
363
+ <option value="93.75%">Compact</option>
364
+ <option value="" selected>Default</option>
365
+ <option value="106.25%">Large</option>
366
+ </select>
367
+ </label>
368
+
288
369
  <label class="variant-row">
289
370
  <span class="variant-label">
290
371
  Section spacing
@@ -308,7 +389,13 @@
308
389
  >
309
390
  </span>
310
391
  <select data-variant-prop="--mw-container-width">
311
- <option value="" selected>1200</option>
392
+ <option value="min(800px, calc(100% - 2 * var(--mw-container-gutter)))">
393
+ 800
394
+ </option>
395
+ <option value="min(960px, calc(100% - 2 * var(--mw-container-gutter)))">
396
+ 960
397
+ </option>
398
+ <option value="" selected>1200 (default)</option>
312
399
  <option
313
400
  value="min(1440px, calc(100% - 2 * var(--mw-container-gutter)))"
314
401
  >
@@ -321,5 +408,15 @@
321
408
  </label>
322
409
  </div>
323
410
 
324
- <pre class="variant-setup" id="variant-setup"></pre>
411
+ <label class="variant-setup-label" for="variant-setup">
412
+ Setup - editable, paste one in to load it
413
+ </label>
414
+ <textarea
415
+ class="variant-setup"
416
+ id="variant-setup"
417
+ rows="4"
418
+ spellcheck="false"
419
+ autocapitalize="off"
420
+ autocorrect="off"
421
+ ></textarea>
325
422
  </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
+ Twelve 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>
@@ -444,10 +444,18 @@
444
444
  <code>mw-shadows-flat</code> - elevation 1-3 to <code>none</code>, dropdown
445
445
  and modal keep theirs
446
446
  </li>
447
+ <li>
448
+ <code>mw-surfaces-flush</code> - cards, panels and footer on the page
449
+ colour, held by their border
450
+ </li>
447
451
  <li>
448
452
  <code>mw-hover-static</code> - no hover travels; colour and border still
449
453
  respond
450
454
  </li>
455
+ <li>
456
+ <code>mw-scroll-static</code> - no scroll entrance;
457
+ <code>mw-reveal</code> blocks sit where they land
458
+ </li>
451
459
  <li>
452
460
  <code>mw-sections-plain</code> - the hatch behind
453
461
  <code>mw-section-alternate</code> collapses into the page colour
@@ -457,10 +465,22 @@
457
465
  <code>mw-btn-pill</code> - fully rounded buttons, form fields keep their
458
466
  radius
459
467
  </li>
460
- <li><code>mw-media-mono</code> - photos grey until hovered</li>
468
+ <li>
469
+ <code>mw-btn-square</code> - the other end of that axis: buttons cut to a
470
+ hard corner while the page keeps its radius
471
+ </li>
472
+ <li>
473
+ <code>mw-btn-tactile</code> - filled buttons stand on a darker edge and sink
474
+ onto it when pressed
475
+ </li>
476
+ <li>
477
+ <code>mw-btn-glass</code> - filled buttons become tinted glass with a lit
478
+ top edge
479
+ </li>
461
480
  </ul>
462
481
  <p class="mw-text-muted mw-text-measure">
463
- Four properties do the rest: <code>--mw-radius-scale</code> multiplies every
464
- radius, plus <code>--mw-font-family-heading</code>,
465
- <code>--mw-container-width</code> and <code>--mw-section-padding-block</code>.
482
+ Five properties do the rest: <code>--mw-radius-scale</code> multiplies every
483
+ radius and <code>--mw-root-font-size</code> the whole rem scale, plus
484
+ <code>--mw-font-family-heading</code>, <code>--mw-container-width</code> and
485
+ <code>--mw-section-padding-block</code>.
466
486
  </p>
@@ -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
@@ -54,6 +54,10 @@
54
54
  // Same list, for a state that should feel instant rather than animated
55
55
  --mw-transition-fast: #{transition-list('--mw-duration-fast')};
56
56
 
57
+ // The whole scale is rem, so this one percentage moves type, spacing and
58
+ // control heights together. Breakpoints are px and stay where they are.
59
+ --mw-root-font-size: 100%;
60
+
57
61
  // Custom properties, so an application can tighten its forms without recompiling
58
62
  --mw-control-height-sm: #{$control-height-sm};
59
63
  --mw-control-height: #{$control-height};
@@ -10,6 +10,8 @@
10
10
  }
11
11
 
12
12
  html {
13
+ // Percent and not px, or a reader who raised their browser font loses it
14
+ font-size: var(--mw-root-font-size);
13
15
  scroll-behavior: smooth;
14
16
  min-width: 320px;
15
17
  scroll-padding-top: var(--mw-header-height);
@@ -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 {