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/.claude/skills/maverick-wave/references/components.md +5 -0
- package/CHANGELOG.md +16 -0
- package/maverick-wave.min.css +3 -3
- package/package.json +2 -2
- package/src/partials/cards-container.html +17 -0
- package/src/partials/info-container.html +13 -0
- package/src/scss/abstracts/_variables.scss +19 -2
- package/src/scss/base/_base.scss +2 -0
- package/src/scss/components/_avatars.scss +7 -7
- package/src/scss/components/_cards.scss +17 -4
- package/src/scss/components/_info.scss +36 -0
- package/src/scss/components/_pricing.scss +46 -0
- package/src/scss/layout/_footer.scss +1 -2
- package/src/scss/layout/_main.scss +45 -0
- package/src/scss/layout/_section.scss +1 -1
- package/src/scss/utilities/_touch-targets.scss +41 -3
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maverick-wave",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.18.0",
|
|
4
4
|
"config": {
|
|
5
|
-
"version_short": "4.
|
|
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><img></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
|
-
|
|
81
|
-
|
|
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)
|
package/src/scss/base/_base.scss
CHANGED
|
@@ -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:
|
|
61
|
-
height:
|
|
62
|
-
border-width:
|
|
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: -
|
|
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:
|
|
156
|
-
height:
|
|
157
|
-
border-width:
|
|
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: #{$
|
|
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
|
+
}
|
|
@@ -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');
|
|
@@ -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
|
-
|
|
126
|
-
|
|
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
|
}
|