maverick-wave 4.16.0 → 4.18.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": "4.16.0",
3
+ "version": "4.18.0",
4
4
  "config": {
5
- "version_short": "4.16"
5
+ "version_short": "4.18"
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",
@@ -115,6 +115,23 @@
115
115
  </p>
116
116
  </div>
117
117
  </div>
118
+
119
+ <div class="mw-card">
120
+ <div class="mw-card-body">
121
+ <h3 class="mw-card-title">
122
+ <span class="mw-card-title-icon mw-card-title-icon-lg">
123
+ <img src="https://m1well.com/images/header_logo.svg" alt="" />
124
+ </span>
125
+ A logo instead of an icon
126
+ </h3>
127
+ <hr class="mw-card-title-divider" />
128
+ <p class="mw-card-text">
129
+ The same slot takes an <code>&lt;img&gt;</code> - a product logo, a
130
+ portrait. Add <code>mw-card-title-icon-lg</code> there: a wordmark
131
+ inside the default 38px is a smudge.
132
+ </p>
133
+ </div>
134
+ </div>
118
135
  </div>
119
136
 
120
137
  <h3 class="mw-mb-4 mw-mt-6">Tech-Stack Cards</h3>
@@ -26,6 +26,19 @@
26
26
  </div>
27
27
  </div>
28
28
 
29
+ <h4 class="mw-mb-2 mw-mt-5">Block</h4>
30
+ <div class="mw-card mw-card-simple">
31
+ <div class="mw-info mw-info-primary mw-info-block">
32
+ <strong>The same frame, carrying a paragraph</strong>
33
+ <span
34
+ >The base is built for a short figure and centres it in a narrow column.
35
+ The block variant ranges its text left up to a measure, sets it in body
36
+ weight and leading, and stays put under the pointer - for a note, a
37
+ disclaimer, or a short explanation beside the thing it explains.</span
38
+ >
39
+ </div>
40
+ </div>
41
+
29
42
  <h4 class="mw-mb-2 mw-mt-5">Counters</h4>
30
43
  <div class="mw-card mw-card-simple">
31
44
  <div class="mw-d-flex mw-flex-wrap mw-items-center mw-gap-3">
@@ -77,8 +77,20 @@ $hover-shift: 15% !default;
77
77
  // dark before it reads as ink on a light one - OKLch lightness, 0 to 1. A bound,
78
78
  // not a target: a color already inside the range is used untouched, so a bright
79
79
  // brand color stays bright and only one that would drown gets moved.
80
- $ink-lightness-dark: 0.4 !default;
81
- $ink-lightness-light: 0.7 !default;
80
+ //
81
+ // 0.52 is where the default blue clears 3:1 on a dark card - the floor a rule
82
+ // or a large label needs. At 0.4 it sat at 1.9:1: a price nobody reads and an
83
+ // outline button whose border nobody finds. 0.58 does the same job for the
84
+ // yellow on a light card, which was at 2.5:1, and is as low as that end can go
85
+ // without collecting company - the orange and the cyan sit at 0.57, so one
86
+ // step further moves three colors instead of one.
87
+ //
88
+ // Which is the point of a bound: in both directions exactly one of the six root
89
+ // colors is outside it and gets moved. The other five pass through byte for
90
+ // byte, and a project that swaps the palette gets the same treatment for
91
+ // whichever of its own colors needs it.
92
+ $ink-lightness-dark: 0.52 !default;
93
+ $ink-lightness-light: 0.58 !default;
82
94
  // How much of the primary color the muted text gray picks up, so it belongs to
83
95
  // the palette instead of being a stock #888. 0% is a true gray.
84
96
  $muted-tint: 23% !default;
@@ -638,6 +650,11 @@ $spacing: (
638
650
  );
639
651
 
640
652
  // Breakpoints
653
+ // Card width at which a card footer stops being a row. Shared: the footer rule
654
+ // lives in _cards.scss, the exception for a price in it in _pricing.scss, and
655
+ // the two have to agree or a price gets the row back one pixel too late.
656
+ $mw-card-narrow: 360px !default;
657
+
641
658
  $breakpoints: (
642
659
  'xs': 375px,
643
660
  // Extra small devices (smaller phones)
@@ -124,6 +124,8 @@ $_active-prefix: 'dark';
124
124
  calc(100% - 2 * var(--mw-container-gutter))
125
125
  );
126
126
  --mw-section-padding-block: #{spacing('7')};
127
+ --mw-section-title-gap: #{spacing('7')};
128
+ --mw-scroll-hint-offset: #{spacing('9')};
127
129
 
128
130
  // Active theme - aliases onto the dark or light set above
129
131
  @each $name, $value in $_active-theme {
@@ -57,9 +57,9 @@ $_mw-avatar-colors: (
57
57
  }
58
58
 
59
59
  &-xl {
60
- width: 250px;
61
- height: 250px;
62
- border-width: 7px;
60
+ width: 210px;
61
+ height: 210px;
62
+ border-width: 6px;
63
63
  font-size: font-size('6xl');
64
64
  }
65
65
 
@@ -125,7 +125,7 @@ $_mw-avatar-colors: (
125
125
  margin-right: -23px;
126
126
  }
127
127
  .mw-avatar-xl {
128
- margin-right: -40px;
128
+ margin-right: -32px;
129
129
  }
130
130
 
131
131
  .mw-avatar:last-child {
@@ -152,9 +152,9 @@ $_mw-avatar-colors: (
152
152
  }
153
153
 
154
154
  &-xl {
155
- width: 170px;
156
- height: 170px;
157
- border-width: 6px;
155
+ width: 150px;
156
+ height: 150px;
157
+ border-width: 5px;
158
158
  }
159
159
  }
160
160
  }
@@ -1,8 +1,5 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
- // Card width at which the footer stops being a row
4
- $_mw-card-narrow: 360px;
5
-
6
3
  $_mw-card-addon-colors: (
7
4
  primary: var(--mw-primary-color),
8
5
  secondary: var(--mw-secondary-color),
@@ -27,6 +24,13 @@ $_mw-card-addon-colors: (
27
24
  color: var(--mw-card-addon-text-color, var(--mw-primary-accent-text-color));
28
25
  box-shadow: var(--mw-elevation-2);
29
26
  transition: var(--mw-transition);
27
+ overflow: hidden;
28
+
29
+ img {
30
+ width: 100%;
31
+ height: 100%;
32
+ object-fit: cover;
33
+ }
30
34
  }
31
35
 
32
36
  // Fading rule used by the title divider and the stack header underline
@@ -216,9 +220,18 @@ $_mw-card-addon-colors: (
216
220
  // <span class="mw-card-title-icon"><i class="fas fa-file-invoice"></i></span>
217
221
  // Invoices
218
222
  // </h3>
223
+ //
224
+ // An <img> works in the same slot - a product logo or a portrait instead of
225
+ // an icon. Reach for the -lg step there: a wordmark inside 38px is a smudge.
219
226
  &-title-icon {
220
227
  @include _card-icon-circle;
221
228
  font-size: font-size('lg');
229
+
230
+ &-lg {
231
+ width: 62px;
232
+ height: 62px;
233
+ font-size: font-size('2xl');
234
+ }
222
235
  }
223
236
 
224
237
  &-title-divider {
@@ -275,7 +288,7 @@ $_mw-card-addon-colors: (
275
288
  // Stacked once the card is narrow, not once the window is - a card in a
276
289
  // three-column grid on a wide desktop is as narrow as the same card on a
277
290
  // phone and used to keep its actions in a cramped row anyway.
278
- @container mw-card (max-width: #{$_mw-card-narrow}) {
291
+ @container mw-card (max-width: #{$mw-card-narrow}) {
279
292
  flex-direction: column;
280
293
  align-items: stretch;
281
294
  gap: spacing('2');
@@ -64,6 +64,42 @@ $_mw-info-colors: (
64
64
  font-weight: font-weight('medium');
65
65
  }
66
66
 
67
+ // Block variant: the same framed surface, carrying a paragraph instead of a
68
+ // value. The base is built for a short figure - it centres its content in a
69
+ // narrow column, sets it in medium weight and lifts on hover, which turns
70
+ // running text into a ragged stack that twitches under the pointer. This
71
+ // reads as a note instead: ranged left up to a measure, body weight, body
72
+ // leading, and it stays put.
73
+ //
74
+ // <div class="mw-info mw-info-primary mw-info-block">
75
+ // <strong>Why greyscale?</strong>
76
+ // <span>Because the frame is not the stage ...</span>
77
+ // </div>
78
+ &-block {
79
+ display: flex;
80
+ align-items: flex-start;
81
+ width: 100%;
82
+ max-width: 68ch;
83
+ margin-inline: auto;
84
+ padding: spacing('6') spacing('7');
85
+ font-weight: font-weight('normal');
86
+ line-height: 1.7;
87
+ text-align: left;
88
+ text-wrap: pretty;
89
+
90
+ &:hover {
91
+ box-shadow: none;
92
+ }
93
+
94
+ @include hover {
95
+ transform: none;
96
+ }
97
+
98
+ strong {
99
+ margin-bottom: spacing('2');
100
+ }
101
+ }
102
+
67
103
  // counter variant: big number + small label
68
104
  &-counter {
69
105
  min-width: spacing('12');
@@ -179,6 +179,22 @@ $_mw-price-word-scale: 0.68;
179
179
  margin-top: spacing('0');
180
180
  }
181
181
 
182
+ // The one action on an offer card fills its row. A card footer otherwise
183
+ // keeps every button at its label's width - a "Details" link has no business
184
+ // being a band - but this is the button the whole card was built to get
185
+ // pressed, and an 80px target parked in a corner is not that.
186
+ //
187
+ // Only when it stands alone: with a price beside it the row is shared, and
188
+ // that case is at the bottom of this file.
189
+ //
190
+ // The auto margin has to go with it. An auto margin in a flex row eats the
191
+ // free space *before* flex-grow is ever offered any, so growing on its own
192
+ // leaves the button sitting exactly where it was.
193
+ > .mw-card-body > .mw-card-footer > .mw-btn:only-child {
194
+ flex: 1 1 auto;
195
+ margin-left: spacing('0');
196
+ }
197
+
182
198
  // A solid band across the top carrying the name and the price - the layout
183
199
  // for when the price is the thing being compared and the feature list is
184
200
  // only its justification. Colour comes from `mw-card-addon-*` on the card,
@@ -229,3 +245,33 @@ $_mw-price-word-scale: 0.68;
229
245
  .mw-offer:has(> .mw-offer-head)::after {
230
246
  @include corner-accent(1px, 'bottom');
231
247
  }
248
+
249
+ // A price in a card footer is a label next to an action, not a second action.
250
+ //
251
+ // The footer stacks its children to full width once the *card* is narrow, which
252
+ // is right for two buttons that would otherwise squeeze into nothing on a phone
253
+ // - and wrong here: "33 Euro" needs no width, and the one button beside it does
254
+ // not need the whole card either. A full-bleed action under a left-aligned
255
+ // price reads as a banner, not as the end of a card.
256
+ //
257
+ // So the row survives, and the action takes half of it. Lives here rather than
258
+ // in _cards.scss because the price is the guest: the footer's own rule is
259
+ // correct for everything that is not one.
260
+ @container mw-card (max-width: #{$mw-card-narrow}) {
261
+ .mw-card-footer:has(> .mw-price) {
262
+ flex-direction: row;
263
+ align-items: center;
264
+
265
+ // The footer's own rule hands every child `width: 100%` for the stack. Taken
266
+ // back for all of them, not just the price - the second slot is a button on
267
+ // one card and an "Ausgebucht" tag on the next, and a full-width tag is no
268
+ // better than a full-width button.
269
+ > * {
270
+ width: auto;
271
+ }
272
+
273
+ > .mw-btn {
274
+ width: 50%;
275
+ }
276
+ }
277
+ }
@@ -3,8 +3,7 @@
3
3
 
4
4
  .mw-footer {
5
5
  background: var(--mw-footer-background);
6
- padding: spacing('5') spacing('0');
7
- margin-top: spacing('6');
6
+ padding: spacing('7') spacing('0');
8
7
  border-top: 1px solid var(--mw-border-accent);
9
8
 
10
9
  &-top {
@@ -77,6 +77,51 @@
77
77
  }
78
78
  }
79
79
 
80
+ // The "scroll down" cue at the bottom of a hero. It belongs to the hero
81
+ // container and not to the flow below it: a hero fills the viewport, so the
82
+ // cue has to sit on its lower edge no matter how tall the content above is.
83
+ // Selector beats the `> *` rule above it, which would otherwise put the
84
+ // element back into flow.
85
+ .mw-container:has(.mw-hero) > .mw-scroll-hint {
86
+ position: absolute;
87
+ bottom: var(--mw-scroll-hint-offset);
88
+ left: 50%;
89
+ transform: translateX(-50%);
90
+ display: flex;
91
+ flex-direction: column;
92
+ align-items: center;
93
+ gap: spacing('2');
94
+ // Ink on the hero image, stepped back - the cue is an offer, not a headline
95
+ color: color-mix(in srgb, var(--mw-hero-text-color) 45%, transparent);
96
+ font-size: font-size('xs');
97
+ letter-spacing: 0.08em;
98
+ text-transform: uppercase;
99
+ text-decoration: none;
100
+ // A loop keeps its own timing - at any of the duration tokens this would
101
+ // read as nervous rather than as a slow bob
102
+ animation: mw-scroll-hint-float 2.2s var(--mw-ease-in-out) infinite;
103
+
104
+ @include hover {
105
+ color: var(--mw-hero-text-color);
106
+ }
107
+ }
108
+
109
+ @keyframes mw-scroll-hint-float {
110
+ 0%,
111
+ 100% {
112
+ transform: translate(-50%, 0);
113
+ }
114
+ 50% {
115
+ transform: translate(-50%, 7px);
116
+ }
117
+ }
118
+
119
+ @media (prefers-reduced-motion: reduce) {
120
+ .mw-scroll-hint {
121
+ animation: none;
122
+ }
123
+ }
124
+
80
125
  .mw-content {
81
126
  flex: 1;
82
127
  padding-top: spacing('7');
@@ -2,7 +2,7 @@
2
2
 
3
3
  .mw-section-title {
4
4
  font-size: font-size('3xl');
5
- margin-bottom: spacing('7');
5
+ margin-bottom: var(--mw-section-title-gap);
6
6
  text-align: center;
7
7
  position: relative;
8
8
 
@@ -83,6 +83,40 @@
83
83
  min-height: 44px;
84
84
  }
85
85
 
86
+ // A gallery dot is 9px here - the target got *smaller* exactly where the
87
+ // pointer got coarser. Same treatment as the mini button below: the dot keeps
88
+ // its silhouette, only the hit area grows to the 24px floor. The gap has to
89
+ // grow with it, or two neighbouring hit areas overlap and a tap aimed at one
90
+ // slide brings up the next.
91
+ .mw-gallery-dots {
92
+ gap: spacing('6');
93
+ }
94
+
95
+ .mw-gallery-dot {
96
+ position: relative;
97
+
98
+ &::after {
99
+ content: '';
100
+ position: absolute;
101
+ inset: -7.5px;
102
+ }
103
+ }
104
+
105
+ // A range input is as tall as its track - 8px, and 6px in the small variant.
106
+ // That is the whole hit area, thumb included: a slider you can only grab by
107
+ // hitting an 8px band is a slider you drag the page with instead.
108
+ //
109
+ // `content-box` is what keeps the track out of it: the height stays the
110
+ // track's, the padding is the hit area on top, and clipping the background
111
+ // to the content box means the visible bar does not grow with it. No track
112
+ // height is repeated from `_slider.scss` this way - each variant grows off
113
+ // whatever it already has: 8px + 36 = 44, the small one 42, the large 52.
114
+ .mw-slider {
115
+ box-sizing: content-box;
116
+ padding-block: 18px;
117
+ background-clip: content-box;
118
+ }
119
+
86
120
  // The 18px silhouette stays: mini buttons sit in tag rows and table cells,
87
121
  // where growing the circle would shift the layout around them. Only the hit
88
122
  // area grows, to 28px - clear of the 24px floor. The extra margin keeps two
@@ -121,9 +155,13 @@
121
155
  }
122
156
 
123
157
  // A row of actions on a phone stacks, and stacked buttons that are each as
124
- // wide as their label look like a list of unrelated things
125
- .mw-form-actions .mw-btn,
126
- .mw-card-footer .mw-btn {
158
+ // wide as their label look like a list of unrelated things.
159
+ //
160
+ // Not `mw-card-footer`: that one stacks on its *container*, not on the
161
+ // window, so at 390px it is still a row - and a `flex-grow` handed to a row
162
+ // is what turned a card's action into a full-bleed band. Stacked it needs no
163
+ // help either, the footer's own `width: 100%` is what fills the column.
164
+ .mw-form-actions .mw-btn {
127
165
  flex: 1 1 auto;
128
166
  }
129
167
  }