maverick-wave 5.5.0 → 5.6.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.5.0",
3
+ "version": "5.6.0",
4
4
  "config": {
5
- "version_short": "5.5"
5
+ "version_short": "5.6"
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",
@@ -139,4 +139,187 @@
139
139
  <input type="color" id="palette-secondary" value="#bb5710" />
140
140
  </label>
141
141
  </div>
142
+
143
+ <h3 class="palette-heading palette-heading-split">Variants</h3>
144
+ <p class="palette-hint">
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.
147
+ </p>
148
+
149
+ <div id="variant-list">
150
+ <label class="variant-row">
151
+ <span class="variant-label">
152
+ Distinctive card corners
153
+ <small
154
+ >The arc across two corners that gives every surface its shape. Off
155
+ leaves an evenly rounded box.</small
156
+ >
157
+ </span>
158
+ <input
159
+ type="checkbox"
160
+ checked
161
+ data-variant-class="mw-corners-even"
162
+ data-variant-invert
163
+ />
164
+ </label>
165
+
166
+ <label class="variant-row">
167
+ <span class="variant-label">
168
+ Corner radius
169
+ <small
170
+ >How round every box is drawn, from hard edges to fully
171
+ rounded.</small
172
+ >
173
+ </span>
174
+ <select data-variant-prop="--mw-radius-scale">
175
+ <option value="0">Square</option>
176
+ <option value="" selected>Default</option>
177
+ <option value="1.7">Round</option>
178
+ </select>
179
+ </label>
180
+
181
+ <label class="variant-row">
182
+ <span class="variant-label">
183
+ Pill buttons
184
+ <small
185
+ >Buttons rounded all the way. Form fields keep their own shape.</small
186
+ >
187
+ </span>
188
+ <input type="checkbox" data-variant-class="mw-btn-pill" />
189
+ </label>
190
+
191
+ <label class="variant-row">
192
+ <span class="variant-label">
193
+ Single accent color
194
+ <small
195
+ >Drop the second brand colour - hovers, tints and borders all come
196
+ from the first one.</small
197
+ >
198
+ </span>
199
+ <input type="checkbox" data-variant-class="mw-accent-single" />
200
+ </label>
201
+
202
+ <label class="variant-row">
203
+ <span class="variant-label">
204
+ Photos in grey
205
+ <small
206
+ >Pictures stay grey until the pointer reaches them, so only the brand
207
+ carries colour.</small
208
+ >
209
+ </span>
210
+ <input type="checkbox" data-variant-class="mw-media-mono" />
211
+ </label>
212
+
213
+ <label class="variant-row">
214
+ <span class="variant-label">
215
+ Heading font
216
+ <small>The typeface for headlines. Body copy is unaffected.</small>
217
+ </span>
218
+ <select data-variant-prop="--mw-font-family-heading">
219
+ <option value="" selected>System</option>
220
+ <option value="Georgia, 'Times New Roman', serif">Serif</option>
221
+ <option
222
+ value="Optima, Candara, 'Gill Sans', 'Trebuchet MS', sans-serif"
223
+ >
224
+ Humanist
225
+ </option>
226
+ </select>
227
+ </label>
228
+
229
+ <label class="variant-row">
230
+ <span class="variant-label">
231
+ Uppercase headings
232
+ <small>Headlines set in capitals with wider letter spacing.</small>
233
+ </span>
234
+ <input type="checkbox" data-variant-class="mw-headings-caps" />
235
+ </label>
236
+
237
+ <label class="variant-row">
238
+ <span class="variant-label">
239
+ Flat surfaces
240
+ <small
241
+ >Cards and panels lose their shadow and sit flat on the page.
242
+ Dropdowns and dialogs still float.</small
243
+ >
244
+ </span>
245
+ <input type="checkbox" data-variant-class="mw-shadows-flat" />
246
+ </label>
247
+
248
+ <label class="variant-row">
249
+ <span class="variant-label">
250
+ Static on hover
251
+ <small
252
+ >Nothing lifts or zooms under the pointer. Colours and borders still
253
+ respond.</small
254
+ >
255
+ </span>
256
+ <input type="checkbox" data-variant-class="mw-hover-static" />
257
+ </label>
258
+
259
+ <label class="variant-row">
260
+ <span class="variant-label">
261
+ Sticky header
262
+ <small
263
+ >The bar stays put from the top instead of sliding in over the first
264
+ screen.</small
265
+ >
266
+ </span>
267
+ <input
268
+ type="checkbox"
269
+ data-variant-class="mw-header-reveal"
270
+ data-variant-target=".mw-header"
271
+ data-variant-invert
272
+ />
273
+ </label>
274
+
275
+ <label class="variant-row">
276
+ <span class="variant-label">
277
+ Alternating sections
278
+ <small>The fine diagonal hatch behind every second section.</small>
279
+ </span>
280
+ <input
281
+ type="checkbox"
282
+ checked
283
+ data-variant-class="mw-sections-plain"
284
+ data-variant-invert
285
+ />
286
+ </label>
287
+
288
+ <label class="variant-row">
289
+ <span class="variant-label">
290
+ Section spacing
291
+ <small
292
+ >How much air sits above and below each section of the page.</small
293
+ >
294
+ </span>
295
+ <select data-variant-prop="--mw-section-padding-block">
296
+ <option value="2rem">Compact</option>
297
+ <option value="" selected>Default</option>
298
+ <option value="5.5rem">Airy</option>
299
+ </select>
300
+ </label>
301
+
302
+ <label class="variant-row">
303
+ <span class="variant-label">
304
+ Page width
305
+ <small
306
+ >How wide the content runs before it stops growing on a large
307
+ screen.</small
308
+ >
309
+ </span>
310
+ <select data-variant-prop="--mw-container-width">
311
+ <option value="" selected>1200</option>
312
+ <option
313
+ value="min(1440px, calc(100% - 2 * var(--mw-container-gutter)))"
314
+ >
315
+ 1440
316
+ </option>
317
+ <option value="calc(100% - 2 * var(--mw-container-gutter))">
318
+ Full
319
+ </option>
320
+ </select>
321
+ </label>
322
+ </div>
323
+
324
+ <pre class="variant-setup" id="variant-setup"></pre>
142
325
  </div>
@@ -423,3 +423,44 @@
423
423
  </p>
424
424
  </div>
425
425
  </div>
426
+
427
+ <h3 class="mw-mb-4 mw-mt-6">Site-wide Variants</h3>
428
+ <p class="mw-text-muted mw-text-measure">
429
+ Eight classes on <code>&lt;html&gt;</code> retune the whole look without
430
+ touching markup. They stack, and the palette button in the corner switches all
431
+ of them live - the box at the bottom of that panel prints the setup a project
432
+ would carry.
433
+ </p>
434
+ <ul class="mw-list mw-list-dot mw-text-measure">
435
+ <li>
436
+ <code>mw-corners-even</code> - drops the surface signature; every panel
437
+ becomes an evenly rounded box
438
+ </li>
439
+ <li>
440
+ <code>mw-accent-single</code> - one brand colour instead of two,
441
+ <code>mw-btn-secondary</code> turns outline
442
+ </li>
443
+ <li>
444
+ <code>mw-shadows-flat</code> - elevation 1-3 to <code>none</code>, dropdown
445
+ and modal keep theirs
446
+ </li>
447
+ <li>
448
+ <code>mw-hover-static</code> - no hover travels; colour and border still
449
+ respond
450
+ </li>
451
+ <li>
452
+ <code>mw-sections-plain</code> - the hatch behind
453
+ <code>mw-section-alternate</code> collapses into the page colour
454
+ </li>
455
+ <li><code>mw-headings-caps</code> - h1 to h3 in capitals</li>
456
+ <li>
457
+ <code>mw-btn-pill</code> - fully rounded buttons, form fields keep their
458
+ radius
459
+ </li>
460
+ <li><code>mw-media-mono</code> - photos grey until hovered</li>
461
+ </ul>
462
+ <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>.
466
+ </p>
@@ -27,9 +27,18 @@
27
27
  @return map.get(v.$breakpoints, $key);
28
28
  }
29
29
 
30
- // Function to get a radius value from the radius map
30
+ // Emitted through --mw-radius-scale, so one number retunes every rounded box in
31
+ // the framework at once - 0 squares them off, above 1 rounds them further, and
32
+ // the corner arc follows. 'none' and 'full' pass through: a scaled 0 is still 0,
33
+ // and a circle has to stay a circle.
31
34
  @function radius($key) {
32
- @return map.get(v.$radius, $key);
35
+ $value: map.get(v.$radius, $key);
36
+
37
+ @if $key == 'none' or $key == 'full' {
38
+ @return $value;
39
+ }
40
+
41
+ @return calc(#{$value} * var(--mw-radius-scale, 1));
33
42
  }
34
43
 
35
44
  // Function to get a z-index value from the z-index map
@@ -129,6 +129,18 @@
129
129
  }
130
130
  }
131
131
 
132
+ // A hover that *travels*. Separate from `hover`, because movement has two more
133
+ // opt-outs than a colour change does: reduced motion, where a transform still
134
+ // jumps to its end position in one frame however short the duration is, and the
135
+ // static-hover variant. Colour and shadow belong in `hover` and stay either way.
136
+ @mixin hover-move {
137
+ @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
138
+ :root:not(.mw-hover-static) &:hover {
139
+ @content;
140
+ }
141
+ }
142
+ }
143
+
132
144
  // No 300ms double-tap wait, no grey flash - the component draws its own :active
133
145
  @mixin tappable {
134
146
  touch-action: manipulation;
@@ -219,17 +231,32 @@
219
231
  &::after {
220
232
  @include corner-accent($border-width, $corners);
221
233
  }
234
+
235
+ // Site-wide opt-out for an even rounded box - .mw-corner-plain next door drops
236
+ // the arc on a single element, this drops the whole silhouette on all of them.
237
+ // Generated here and not as a list of the eleven callers, so a new caller is
238
+ // covered the day it is written.
239
+ :root.mw-corners-even & {
240
+ border-radius: radius('md');
241
+
242
+ &::after {
243
+ display: none;
244
+ }
245
+ }
222
246
  }
223
247
 
224
248
  // The arc alone, for a variant that redraws it without restating the shape
225
249
  @mixin corner-accent($border-width: 1px, $corners: 'both') {
226
- $sharp: radius('sm') - $border-width;
227
- $round: radius('xl') - $border-width;
250
+ // max(), not a bare subtraction: --mw-radius-scale can take the radius to 0,
251
+ // and while a negative border-radius is clamped for us, a negative circle in
252
+ // the mask below would throw the whole gradient away and lose the arc.
253
+ $sharp: max(0px, calc(#{radius('sm')} - #{$border-width}));
254
+ $round: max(0px, calc(#{radius('xl')} - #{$border-width}));
228
255
  // Opaque to where the curve ends, fading from there, so the arc runs into the
229
256
  // straight edge instead of stopping at the tangent. 1.8 is bounded by the
230
257
  // shortest surface: past about 2 the two arcs meet on a 50px box and read as
231
258
  // a frame.
232
- $reach: $round * 1.8;
259
+ $reach: calc(#{$round} * 1.8);
233
260
  $top: radial-gradient(circle $reach at 100% 0, #000 $round, transparent 100%);
234
261
  $bottom: radial-gradient(
235
262
  circle $reach at 0 100%,
@@ -86,10 +86,10 @@ $header-active-tint: 25% !default;
86
86
  $shadow-cast-dark: 0.3 !default;
87
87
  // Near (contact) and far (ambient) opacity. The dark theme's far layer is a light
88
88
  // rim, so it stays low - a glow carries further than a shadow at the same alpha.
89
- $shadow-near-dark: 55% !default;
90
- $shadow-far-dark: 13% !default;
91
- $shadow-near-light: 14% !default;
92
- $shadow-far-light: 10% !default;
89
+ $shadow-near-dark: 68% !default;
90
+ $shadow-far-dark: 16% !default;
91
+ $shadow-near-light: 21% !default;
92
+ $shadow-far-light: 14% !default;
93
93
 
94
94
  // Control sizes - 32 / 36 / 40px, 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
@@ -88,6 +88,9 @@
88
88
  --mw-hero-pattern: #{$mw-hero-pattern};
89
89
  --mw-hero-text-color: #{$mw-hero-text-color};
90
90
 
91
+ // Read by every radius the framework draws - see the radius() function
92
+ --mw-radius-scale: 1;
93
+
91
94
  // header height
92
95
  --mw-header-height: #{$header-height};
93
96
 
@@ -85,10 +85,13 @@
85
85
  transition: transform var(--mw-duration-base) var(--mw-ease-out);
86
86
 
87
87
  @include hover {
88
- transform: translateY(-3px);
89
88
  z-index: z-index('lift');
90
89
  }
91
90
 
91
+ @include hover-move {
92
+ transform: translateY(-3px);
93
+ }
94
+
92
95
  &:last-child {
93
96
  margin-right: spacing('0');
94
97
  }
@@ -274,14 +274,14 @@
274
274
  transition: var(--mw-transition);
275
275
  color: var(--mw-primary-text-color);
276
276
 
277
- &:hover {
277
+ @include hover-move {
278
278
  transform: translateX(-7px);
279
279
  }
280
280
 
281
281
  &.next {
282
282
  flex-direction: row-reverse;
283
283
 
284
- &:hover {
284
+ @include hover-move {
285
285
  transform: translateX(7px);
286
286
  }
287
287
  }
@@ -81,11 +81,10 @@
81
81
  --mw-corner-accent: var(--mw-corner-accent-hover);
82
82
  }
83
83
 
84
- // The lift moves the box, so it is guarded: on touch `:hover` latches after a
85
- // tap and the card stays raised, visibly out of line with the row it sits in.
86
- // The colour and shadow below are not - a latched border reads as "the one you
87
- // last touched", which is no worse than nothing.
88
- @include hover {
84
+ // Pointer-guarded, unlike the colour and shadow above: on touch `:hover`
85
+ // latches after a tap and the card would stay raised, visibly out of line
86
+ // with the row it sits in. A latched border is no worse than nothing.
87
+ @include hover-move {
89
88
  transform: translateY(-2px);
90
89
  }
91
90
 
@@ -145,7 +144,7 @@
145
144
  height: var(--mw-card-img-height, 210px);
146
145
  overflow: hidden;
147
146
  flex-shrink: 0;
148
- border-radius: (radius('sm') - 1px) (radius('xl') - 1px) 0 0;
147
+ border-radius: calc(#{radius('sm')} - 1px) calc(#{radius('xl')} - 1px) 0 0;
149
148
 
150
149
  img {
151
150
  width: 100%;
@@ -154,8 +153,10 @@
154
153
  transition: transform var(--mw-duration-zoom) var(--mw-ease-out);
155
154
  }
156
155
 
157
- &:hover img {
158
- transform: scale(1.05);
156
+ @include hover-move {
157
+ img {
158
+ transform: scale(1.05);
159
+ }
159
160
  }
160
161
  }
161
162
 
@@ -323,8 +324,10 @@
323
324
  font-size: font-size('md');
324
325
  }
325
326
 
326
- &:hover &-icon {
327
- transform: rotate(25deg);
327
+ @include hover-move {
328
+ .mw-card-stack-icon {
329
+ transform: rotate(25deg);
330
+ }
328
331
  }
329
332
 
330
333
  &-header {
@@ -427,16 +430,4 @@
427
430
  --mw-card-addon-text-color: var(--mw-#{$name}-accent-text-color);
428
431
  }
429
432
  }
430
-
431
- // Only the movement. The durations are already pinned to 1ms globally in
432
- // base/_base.scss; a hover that *travels* has to be taken away outright, or it
433
- // still jumps to its lifted position in one frame.
434
- @media (prefers-reduced-motion: reduce) {
435
- .mw-card:hover,
436
- .mw-card-img:hover img,
437
- .mw-card-img-contain:hover img,
438
- .mw-card-stack:hover .mw-card-stack-icon {
439
- transform: none;
440
- }
441
- }
442
433
  }
@@ -51,7 +51,7 @@
51
51
  transition: opacity var(--mw-duration-slower) var(--mw-ease-out);
52
52
  }
53
53
 
54
- @include hover {
54
+ @include hover-move {
55
55
  transform: translateY(-3px);
56
56
  }
57
57
 
@@ -24,7 +24,7 @@
24
24
  box-shadow: var(--mw-elevation-3);
25
25
  }
26
26
 
27
- @include hover {
27
+ @include hover-move {
28
28
  transform: translateY(-2px);
29
29
  }
30
30
 
@@ -74,7 +74,7 @@
74
74
  box-shadow: none;
75
75
  }
76
76
 
77
- @include hover {
77
+ @include hover-move {
78
78
  transform: none;
79
79
  }
80
80
 
@@ -239,7 +239,7 @@
239
239
  }
240
240
  }
241
241
 
242
- @include hover {
242
+ @include hover-move {
243
243
  transform: translateX(8px);
244
244
  }
245
245
  }
@@ -18,7 +18,7 @@
18
18
  justify-content: space-between;
19
19
  align-items: center;
20
20
  margin: spacing('0');
21
- border-radius: (radius('sm') - 2px) (radius('xl') - 2px) 0 0;
21
+ border-radius: calc(#{radius('sm')} - 2px) calc(#{radius('xl')} - 2px) 0 0;
22
22
  }
23
23
 
24
24
  &-body {
@@ -156,7 +156,7 @@
156
156
  gap: spacing('2');
157
157
  padding: spacing('6') spacing('5');
158
158
  // The card's own radius minus its border - the band sits inside it
159
- border-radius: (radius('sm') - 1px) (radius('xl') - 1px) 0 0;
159
+ border-radius: calc(#{radius('sm')} - 1px) calc(#{radius('xl')} - 1px) 0 0;
160
160
  background: var(--mw-card-addon-color, var(--mw-primary-color));
161
161
  color: var(
162
162
  --mw-card-addon-text-color,
@@ -33,7 +33,7 @@
33
33
  font-size: font-size('xs');
34
34
  }
35
35
 
36
- @include hover {
36
+ @include hover-move {
37
37
  transform: translateY(-2px);
38
38
  }
39
39
 
@@ -81,7 +81,7 @@
81
81
  --mw-corner-accent: var(--mw-corner-accent-hover);
82
82
  }
83
83
 
84
- @include hover {
84
+ @include hover-move {
85
85
  transform: translateY(-2px);
86
86
  }
87
87
 
@@ -115,7 +115,7 @@
115
115
  border-color: var(--mw-primary-color);
116
116
  }
117
117
 
118
- @include hover {
118
+ @include hover-move {
119
119
  transform: translateY(-2px);
120
120
  }
121
121
 
@@ -82,7 +82,12 @@
82
82
 
83
83
  &:hover:not(:disabled) {
84
84
  background: var(--mw-primary-color-hover);
85
- transform: translateY(-1px);
85
+ }
86
+
87
+ @include hover-move {
88
+ &:not(:disabled) {
89
+ transform: translateY(-1px);
90
+ }
86
91
  }
87
92
 
88
93
  @include focus-ring;
@@ -117,6 +122,9 @@
117
122
 
118
123
  &:hover {
119
124
  background: var(--mw-primary-color-hover);
125
+ }
126
+
127
+ @include hover-move {
120
128
  transform: translateY(-1px);
121
129
  }
122
130
 
@@ -8,3 +8,4 @@
8
8
  @forward 'spacing';
9
9
  @forward 'text';
10
10
  @forward 'touch-targets';
11
+ @forward 'variants';
@@ -0,0 +1,90 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.utilities {
4
+ // Site-wide looks, switched with one class on <html>. Two more live elsewhere,
5
+ // because they have to reach every caller of a mixin: .mw-corners-even in
6
+ // surface(), .mw-hover-static in hover-move().
7
+
8
+ // One brand colour instead of two. Every secondary tone is derived from
9
+ // --mw-secondary-color with color-mix() and oklch(from ...), so the two lines
10
+ // below carry through to hovers, tints, borders and both themes' ink.
11
+ :root.mw-accent-single {
12
+ --mw-secondary-color: var(--mw-primary-color);
13
+ --mw-secondary-accent-text-color: var(--mw-primary-accent-text-color);
14
+
15
+ // Or the two solid buttons would be the same button. Outline keeps the
16
+ // emphasis order the markup asked for: primary fills, secondary draws a line.
17
+ .mw-btn-secondary {
18
+ background: transparent;
19
+ border-color: var(--mw-primary-text-color);
20
+ color: var(--mw-primary-text-color);
21
+
22
+ &:hover:not(:disabled) {
23
+ background: var(--mw-primary-color);
24
+ border-color: var(--mw-primary-color);
25
+ color: var(--mw-primary-accent-text-color);
26
+ }
27
+ }
28
+ }
29
+
30
+ // 1 to 3 are surfaces lying on the page and go; 4 and 5 are the dropdown and
31
+ // the modal, which genuinely float above content and keep their shadow - in a
32
+ // flat page they are the only two things that should cast one.
33
+ //
34
+ // The second selector is not optional: elevation is a theme alias, and the
35
+ // light theme re-declares it on <body>. A value written on :root alone is
36
+ // shadowed for the whole body subtree, so the switch would do nothing at all
37
+ // in light mode. Any variant that retunes a theme-bound token needs both.
38
+ :root.mw-shadows-flat,
39
+ :root.mw-shadows-flat .mw-theme-light {
40
+ --mw-elevation-1: none;
41
+ --mw-elevation-2: none;
42
+ --mw-elevation-3: none;
43
+ }
44
+
45
+ // The diagonal hatch behind an alternating section, dropped. Written on the
46
+ // section and not as a token on :root: --mw-page-background is a theme alias
47
+ // that only becomes the light one further down the tree, so a copy taken up
48
+ // here hands the section the *other* theme's colour - the hatch turns dark
49
+ // instead of vanishing.
50
+ :root.mw-sections-plain .mw-section-alternate {
51
+ background: var(--mw-page-background);
52
+ }
53
+
54
+ // Buttons only. Input groups build their radius corner by corner, so a pill
55
+ // there is its own piece of geometry rather than one more override.
56
+ :root.mw-btn-pill .mw-btn {
57
+ border-radius: 999px;
58
+ }
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
+ }
73
+
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;
79
+ }
80
+ }
81
+
82
+ :root.mw-headings-caps {
83
+ h1,
84
+ h2,
85
+ h3 {
86
+ text-transform: uppercase;
87
+ letter-spacing: 0.045em;
88
+ }
89
+ }
90
+ }