maverick-wave 4.20.0 → 4.22.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/SKILL.md +9 -7
- package/.claude/skills/maverick-wave/references/components.md +85 -0
- package/.claude/skills/maverick-wave/references/layout.md +30 -7
- package/.claude/skills/maverick-wave/references/theming.md +1 -1
- package/CHANGELOG.md +17 -0
- package/index.html +40 -0
- package/package.json +2 -2
- package/src/assets/images/gallery-city.svg +49 -0
- package/src/assets/images/gallery-desert.svg +25 -0
- package/src/assets/images/gallery-forest.svg +46 -0
- package/src/assets/images/gallery-lake.svg +26 -0
- package/src/assets/images/gallery-mountain.svg +19 -0
- package/src/assets/images/gallery-ocean.svg +26 -0
- package/src/assets/images/gallery-oldtown.svg +41 -0
- package/src/assets/images/gallery-snow.svg +39 -0
- package/src/assets/images/slider-field.svg +124 -0
- package/src/assets/images/slider-forest.svg +33 -0
- package/src/assets/images/slider-lake.svg +35 -0
- package/src/assets/images/slider-mountains.svg +29 -0
- package/src/assets/images/slider-trees.svg +41 -0
- package/src/assets/images/tile-analytics.svg +22 -0
- package/src/assets/images/tile-cloud.svg +19 -0
- package/src/assets/images/tile-ecommerce.svg +14 -0
- package/src/assets/images/tile-enterprise.svg +22 -0
- package/src/assets/images/tile-marketing.svg +16 -0
- package/src/assets/media/demo-chime.wav +0 -0
- package/src/partials/announcement-container.html +67 -0
- package/src/partials/content-slider-container.html +5 -17
- package/src/partials/gallery-container.html +8 -8
- package/src/partials/home-container.html +11 -20
- package/src/partials/media-container.html +27 -0
- package/src/partials/prose-container.html +43 -0
- package/src/partials/testimonials-container.html +82 -0
- package/src/partials/tiles-container.html +5 -20
- package/src/scss/base/_base.scss +7 -1
- package/src/scss/components/_announcement.scss +84 -0
- package/src/scss/components/_index.scss +4 -0
- package/src/scss/components/_media.scss +26 -0
- package/src/scss/components/_prose.scss +42 -0
- package/src/scss/components/_testimonial.scss +58 -0
- package/src/scss/layout/_grid.scss +39 -0
- package/src/scss/layout/_main.scss +16 -1
- package/src/scss/layout/_section.scss +24 -0
- package/src/scss/utilities/_index.scss +1 -0
- package/src/scss/utilities/_reveal.scss +34 -0
- package/src/scss/utilities/_touch-targets.scss +10 -2
- package/maverick-wave.min.css +0 -12
- package/maverick-wave.min.js +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<h3 class="mw-section-subtitle">Media</h3>
|
|
2
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
3
|
+
Self-hosted audio and video. The framework ships no player - the native
|
|
4
|
+
controls are the player, and <code>accent-color</code> already brands them.
|
|
5
|
+
The wrapper makes the element a full-width block; a <code>video</code> also
|
|
6
|
+
gets the surface silhouette and a resting shadow, same markup with a
|
|
7
|
+
<code><video></code> element inside.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<div class="mw-grid-2 mw-gap-6">
|
|
11
|
+
<div>
|
|
12
|
+
<h5 class="mw-text-medium mw-mb-2">Audio with caption</h5>
|
|
13
|
+
<div class="mw-card mw-card-simple">
|
|
14
|
+
<div class="mw-media">
|
|
15
|
+
<audio controls preload="metadata" src="media/demo-chime.wav"></audio>
|
|
16
|
+
<p class="mw-media-caption">A short chime, served from this site.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div>
|
|
22
|
+
<h5 class="mw-text-medium mw-mb-2">Bare, without a card</h5>
|
|
23
|
+
<div class="mw-media">
|
|
24
|
+
<audio controls preload="metadata" src="media/demo-chime.wav"></audio>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<h3 class="mw-section-subtitle">Prose</h3>
|
|
2
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
3
|
+
Long-form running text - a legal page, terms, a plain article. One class on
|
|
4
|
+
the container, bare <code>h2</code>/<code>h3</code>/<code>p</code>/<code
|
|
5
|
+
>ul</code
|
|
6
|
+
>
|
|
7
|
+
inside: exactly what a CMS or a JSON-driven page produces. Headings stay
|
|
8
|
+
deliberately small - in a document they structure the reading, they do not
|
|
9
|
+
compete with the page title.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<div class="mw-card mw-card-simple mw-prose">
|
|
13
|
+
<h2>1. Scope</h2>
|
|
14
|
+
<p>
|
|
15
|
+
These terms apply to every order placed through this site. Deviating
|
|
16
|
+
conditions of the customer do not apply unless expressly confirmed in
|
|
17
|
+
writing.
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<h2>2. Contract</h2>
|
|
21
|
+
<p>
|
|
22
|
+
The presentation of products is not a binding offer. A contract comes into
|
|
23
|
+
existence when the order confirmation arrives, at the latest with the
|
|
24
|
+
delivery of the goods.
|
|
25
|
+
</p>
|
|
26
|
+
<h3>2.1 Cancellation</h3>
|
|
27
|
+
<p>
|
|
28
|
+
Consumers may cancel within fourteen days without giving a reason. The
|
|
29
|
+
period starts on the day the goods arrive. For the cancellation to count,
|
|
30
|
+
one of the following is enough:
|
|
31
|
+
</p>
|
|
32
|
+
<ul>
|
|
33
|
+
<li>an informal e-mail naming the order number</li>
|
|
34
|
+
<li>the returned goods with a short note</li>
|
|
35
|
+
<li>the cancellation form from the appendix</li>
|
|
36
|
+
</ul>
|
|
37
|
+
|
|
38
|
+
<h2>3. Prices</h2>
|
|
39
|
+
<p>
|
|
40
|
+
All prices are final prices and include the statutory value-added tax.
|
|
41
|
+
Shipping costs are shown separately before the order is placed.
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<h3 class="mw-section-subtitle">Testimonials</h3>
|
|
2
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
3
|
+
A quote card with its source - a <code>figure</code>, because quote and
|
|
4
|
+
attribution belong together. One voice may stand above the grid as
|
|
5
|
+
<code>mw-testimonial-featured</code>; the rest flow in
|
|
6
|
+
<code>mw-columns-3</code>, the masonry layout for cards of unequal height. The
|
|
7
|
+
cards below also carry <code>mw-reveal</code> - scroll them into view to see
|
|
8
|
+
the entrance.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<figure class="mw-testimonial mw-testimonial-featured mw-mb-6">
|
|
12
|
+
<blockquote>
|
|
13
|
+
We rebuilt our whole customer portal on this in three weeks. The team never
|
|
14
|
+
argued about spacing or shadows again - the framework had already decided,
|
|
15
|
+
and it had decided well.
|
|
16
|
+
</blockquote>
|
|
17
|
+
<figcaption class="mw-testimonial-source">
|
|
18
|
+
Maren K.
|
|
19
|
+
<span class="mw-testimonial-detail">· Head of Engineering</span>
|
|
20
|
+
</figcaption>
|
|
21
|
+
</figure>
|
|
22
|
+
|
|
23
|
+
<div class="mw-columns-3">
|
|
24
|
+
<figure class="mw-testimonial mw-reveal">
|
|
25
|
+
<blockquote>
|
|
26
|
+
The dark theme just worked. We flipped one class and every component
|
|
27
|
+
followed.
|
|
28
|
+
</blockquote>
|
|
29
|
+
<figcaption class="mw-testimonial-source">
|
|
30
|
+
Jonas R.
|
|
31
|
+
<span class="mw-testimonial-detail">· Frontend Developer</span>
|
|
32
|
+
</figcaption>
|
|
33
|
+
</figure>
|
|
34
|
+
|
|
35
|
+
<figure class="mw-testimonial mw-reveal">
|
|
36
|
+
<blockquote>
|
|
37
|
+
I came for the grid and stayed for the corner accents. Our app finally
|
|
38
|
+
looks like one product instead of five teams.
|
|
39
|
+
</blockquote>
|
|
40
|
+
<figcaption class="mw-testimonial-source">
|
|
41
|
+
Priya S.
|
|
42
|
+
<span class="mw-testimonial-detail">· Product Designer</span>
|
|
43
|
+
</figcaption>
|
|
44
|
+
</figure>
|
|
45
|
+
|
|
46
|
+
<figure class="mw-testimonial mw-reveal">
|
|
47
|
+
<blockquote>Small CSS, no build step, readable class names.</blockquote>
|
|
48
|
+
<figcaption class="mw-testimonial-source">
|
|
49
|
+
Tom W. <span class="mw-testimonial-detail">· Freelancer</span>
|
|
50
|
+
</figcaption>
|
|
51
|
+
</figure>
|
|
52
|
+
|
|
53
|
+
<figure class="mw-testimonial mw-reveal">
|
|
54
|
+
<blockquote>
|
|
55
|
+
The form controls line up with the buttons out of the box. That alone
|
|
56
|
+
saved us a week of pixel pushing across three breakpoints - and the touch
|
|
57
|
+
targets grow on their own on mobile.
|
|
58
|
+
</blockquote>
|
|
59
|
+
<figcaption class="mw-testimonial-source">
|
|
60
|
+
Aylin D. <span class="mw-testimonial-detail">· Agency Lead</span>
|
|
61
|
+
</figcaption>
|
|
62
|
+
</figure>
|
|
63
|
+
|
|
64
|
+
<figure class="mw-testimonial mw-reveal">
|
|
65
|
+
<blockquote>
|
|
66
|
+
Swapped one primary color, the whole palette followed.
|
|
67
|
+
</blockquote>
|
|
68
|
+
<figcaption class="mw-testimonial-source">
|
|
69
|
+
Ben L. <span class="mw-testimonial-detail">· Indie Hacker</span>
|
|
70
|
+
</figcaption>
|
|
71
|
+
</figure>
|
|
72
|
+
|
|
73
|
+
<figure class="mw-testimonial mw-reveal">
|
|
74
|
+
<blockquote>
|
|
75
|
+
Our legal pages, our landing page and our admin tables all speak the same
|
|
76
|
+
language now.
|
|
77
|
+
</blockquote>
|
|
78
|
+
<figcaption class="mw-testimonial-source">
|
|
79
|
+
Carla M. <span class="mw-testimonial-detail">· CTO</span>
|
|
80
|
+
</figcaption>
|
|
81
|
+
</figure>
|
|
82
|
+
</div>
|
|
@@ -69,10 +69,7 @@
|
|
|
69
69
|
<div class="mw-tiles mw-tiles-2col">
|
|
70
70
|
<div class="mw-tile">
|
|
71
71
|
<div class="mw-tile-img">
|
|
72
|
-
<img
|
|
73
|
-
src="https://picsum.photos/80/80?random=1"
|
|
74
|
-
alt="E-commerce platform"
|
|
75
|
-
/>
|
|
72
|
+
<img src="images/tile-ecommerce.svg" alt="E-commerce platform" />
|
|
76
73
|
</div>
|
|
77
74
|
<h3 class="mw-tile-header">E-commerce Platform</h3>
|
|
78
75
|
<div class="mw-tile-body">
|
|
@@ -84,10 +81,7 @@
|
|
|
84
81
|
|
|
85
82
|
<div class="mw-tile">
|
|
86
83
|
<div class="mw-tile-img">
|
|
87
|
-
<img
|
|
88
|
-
src="https://picsum.photos/80/80?random=2"
|
|
89
|
-
alt="Marketing automation"
|
|
90
|
-
/>
|
|
84
|
+
<img src="images/tile-marketing.svg" alt="Marketing automation" />
|
|
91
85
|
</div>
|
|
92
86
|
<h3 class="mw-tile-header">Marketing Automation</h3>
|
|
93
87
|
<div class="mw-tile-body">
|
|
@@ -181,10 +175,7 @@
|
|
|
181
175
|
<div class="mw-tiles">
|
|
182
176
|
<div class="mw-tile mw-tile-lg">
|
|
183
177
|
<div class="mw-tile-img">
|
|
184
|
-
<img
|
|
185
|
-
src="https://picsum.photos/80/80?random=3"
|
|
186
|
-
alt="Cloud infrastructure"
|
|
187
|
-
/>
|
|
178
|
+
<img src="images/tile-cloud.svg" alt="Cloud infrastructure" />
|
|
188
179
|
</div>
|
|
189
180
|
<h2 class="mw-tile-header">Cloud Infrastructure</h2>
|
|
190
181
|
<div class="mw-tile-body">
|
|
@@ -201,10 +192,7 @@
|
|
|
201
192
|
|
|
202
193
|
<div class="mw-tile mw-tile-lg">
|
|
203
194
|
<div class="mw-tile-img">
|
|
204
|
-
<img
|
|
205
|
-
src="https://picsum.photos/100/100?random=8"
|
|
206
|
-
alt="Business intelligence"
|
|
207
|
-
/>
|
|
195
|
+
<img src="images/tile-analytics.svg" alt="Business intelligence" />
|
|
208
196
|
</div>
|
|
209
197
|
<h2 class="mw-tile-header">Business Intelligence Suite</h2>
|
|
210
198
|
<div class="mw-tile-body">
|
|
@@ -222,10 +210,7 @@
|
|
|
222
210
|
|
|
223
211
|
<div class="mw-tile mw-tile-lg">
|
|
224
212
|
<div class="mw-tile-img">
|
|
225
|
-
<img
|
|
226
|
-
src="https://picsum.photos/100/100?random=9"
|
|
227
|
-
alt="Enterprise solutions"
|
|
228
|
-
/>
|
|
213
|
+
<img src="images/tile-enterprise.svg" alt="Enterprise solutions" />
|
|
229
214
|
</div>
|
|
230
215
|
<h2 class="mw-tile-header">Enterprise Solutions</h2>
|
|
231
216
|
<div class="mw-tile-body">
|
package/src/scss/base/_base.scss
CHANGED
|
@@ -99,6 +99,12 @@ $_active-prefix: 'dark';
|
|
|
99
99
|
// header height
|
|
100
100
|
--mw-header-height: #{$header-height};
|
|
101
101
|
|
|
102
|
+
// Height the announcement ribbon reserves - the anchor scroll offset is
|
|
103
|
+
// calculated from it, so a project with a taller ribbon raises this token
|
|
104
|
+
// rather than fighting the offset. Below `sm` the component itself steps it
|
|
105
|
+
// up for the wrapped line.
|
|
106
|
+
--mw-announcement-height: 2.7rem;
|
|
107
|
+
|
|
102
108
|
// Layout knobs. A good default is not enough here: what reads as generous on
|
|
103
109
|
// a landing page costs visible content in an application with a sticky
|
|
104
110
|
// header. Same framework, two different right answers.
|
|
@@ -123,7 +129,7 @@ $_active-prefix: 'dark';
|
|
|
123
129
|
1200px,
|
|
124
130
|
calc(100% - 2 * var(--mw-container-gutter))
|
|
125
131
|
);
|
|
126
|
-
--mw-section-padding-block: #{spacing('
|
|
132
|
+
--mw-section-padding-block: #{spacing('10')};
|
|
127
133
|
--mw-section-title-gap: #{spacing('7')};
|
|
128
134
|
--mw-scroll-hint-offset: #{spacing('9')};
|
|
129
135
|
--mw-scroll-hint-inline: var(--mw-container-gutter);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
// The ribbon under the fixed header - a discount, a launch note, a maintenance
|
|
4
|
+
// window. One per page, and always a single line of content with an optional
|
|
5
|
+
// highlight pill in front of it.
|
|
6
|
+
.mw-announcement {
|
|
7
|
+
position: fixed;
|
|
8
|
+
top: var(--mw-header-height);
|
|
9
|
+
right: 0;
|
|
10
|
+
left: 0;
|
|
11
|
+
z-index: z-index('header') - 10;
|
|
12
|
+
background: color-mix(in srgb, var(--mw-primary-color) 75%, transparent);
|
|
13
|
+
backdrop-filter: blur(7px);
|
|
14
|
+
color: var(--mw-primary-accent-text-color);
|
|
15
|
+
box-shadow: var(--mw-elevation-2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The one row inside - usually a link to the thing announced.
|
|
19
|
+
.mw-announcement-content {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
gap: spacing('4');
|
|
24
|
+
min-height: var(--mw-announcement-height);
|
|
25
|
+
padding: spacing('1') spacing('4');
|
|
26
|
+
font-size: font-size('sm');
|
|
27
|
+
text-align: center;
|
|
28
|
+
color: inherit;
|
|
29
|
+
text-decoration: none;
|
|
30
|
+
|
|
31
|
+
&:hover,
|
|
32
|
+
&:focus-visible {
|
|
33
|
+
background: color-mix(in srgb, #000 10%, transparent);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@include focus-ring-inset;
|
|
37
|
+
@include tappable;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The highlight pill - the "-20 %", the "NEW". Darkened glass rather than a
|
|
41
|
+
// color of its own, so it works on every variant without a token per pair.
|
|
42
|
+
.mw-announcement-highlight {
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
padding: spacing('1') spacing('3');
|
|
45
|
+
border-radius: 999px;
|
|
46
|
+
background: color-mix(in srgb, #000 18%, transparent);
|
|
47
|
+
font-weight: font-weight('bold');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The ribbon in the document flow instead of glued under the header - for a
|
|
51
|
+
// notice inside a page, or a demo of the component itself. Static, it scrolls
|
|
52
|
+
// with the content and shifts no anchor offset below.
|
|
53
|
+
.mw-announcement-static {
|
|
54
|
+
position: static;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Anchors scrolled to under the fixed chrome would land under the ribbon too -
|
|
58
|
+
// the same reason the reset reserves the header height. Scoped with :has, so a
|
|
59
|
+
// page without a ribbon keeps the plain offset - and a static ribbon does not
|
|
60
|
+
// count, it scrolls with the content.
|
|
61
|
+
html:has(.mw-announcement:not(.mw-announcement-static)) {
|
|
62
|
+
scroll-padding-top: calc(
|
|
63
|
+
var(--mw-header-height) + var(--mw-announcement-height)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// On a phone the line wraps and the ribbon grows - the height token follows,
|
|
68
|
+
// so the scroll offset above stays honest. Here and not in base, so cherry-
|
|
69
|
+
// picking the component brings the whole behaviour along.
|
|
70
|
+
@include media-down('sm') {
|
|
71
|
+
:root {
|
|
72
|
+
--mw-announcement-height: 3.4rem;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Color variants
|
|
77
|
+
$_mw-announcement-colors: (secondary, success, warning, danger, info);
|
|
78
|
+
|
|
79
|
+
@each $name in $_mw-announcement-colors {
|
|
80
|
+
.mw-announcement-#{$name} {
|
|
81
|
+
background: color-mix(in srgb, var(--mw-#{$name}-color) 75%, transparent);
|
|
82
|
+
color: var(--mw-#{$name}-accent-text-color);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@forward 'accordions';
|
|
2
2
|
@forward 'alerts';
|
|
3
|
+
@forward 'announcement';
|
|
3
4
|
@forward 'avatars';
|
|
4
5
|
@forward 'badge';
|
|
5
6
|
@forward 'blog-post';
|
|
@@ -21,12 +22,14 @@
|
|
|
21
22
|
@forward 'links';
|
|
22
23
|
@forward 'lists';
|
|
23
24
|
@forward 'localhost-indicator';
|
|
25
|
+
@forward 'media';
|
|
24
26
|
@forward 'meta-info';
|
|
25
27
|
@forward 'modals';
|
|
26
28
|
@forward 'pagination';
|
|
27
29
|
@forward 'panels';
|
|
28
30
|
@forward 'pricing';
|
|
29
31
|
@forward 'progress';
|
|
32
|
+
@forward 'prose';
|
|
30
33
|
@forward 'ratings';
|
|
31
34
|
@forward 'segmented';
|
|
32
35
|
@forward 'skeleton';
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
@forward 'tabs';
|
|
37
40
|
@forward 'tags';
|
|
38
41
|
@forward 'techstack-bucket';
|
|
42
|
+
@forward 'testimonial';
|
|
39
43
|
@forward 'theme-toggle';
|
|
40
44
|
@forward 'tiles';
|
|
41
45
|
@forward 'timelines';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
// Self-hosted audio and video. The framework ships no player - the native
|
|
4
|
+
// controls are the player, and `accent-color` on :root already brands them.
|
|
5
|
+
// This only makes the element sit right: full width, block, and a video gets
|
|
6
|
+
// the surface silhouette so it reads as part of the family.
|
|
7
|
+
.mw-media {
|
|
8
|
+
audio,
|
|
9
|
+
video {
|
|
10
|
+
display: block;
|
|
11
|
+
width: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
video {
|
|
15
|
+
height: auto;
|
|
16
|
+
border-radius: radius('sm') radius('xl') radius('sm') radius('xl');
|
|
17
|
+
box-shadow: var(--mw-elevation-2);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.mw-media-caption {
|
|
22
|
+
margin-top: spacing('2');
|
|
23
|
+
font-size: font-size('sm');
|
|
24
|
+
color: var(--mw-text-muted-color);
|
|
25
|
+
text-align: center;
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
// Long-form running text - a legal page, terms, a plain article. One class on
|
|
4
|
+
// the container instead of a utility chain on every heading: the markup inside
|
|
5
|
+
// stays bare h2/h3/p/ul, which is exactly what a CMS or a JSON-driven page
|
|
6
|
+
// produces anyway.
|
|
7
|
+
//
|
|
8
|
+
// Headings are deliberately small: in a document they structure the reading,
|
|
9
|
+
// they do not compete with the page title.
|
|
10
|
+
.mw-prose {
|
|
11
|
+
h2 {
|
|
12
|
+
font-size: font-size('lg');
|
|
13
|
+
color: var(--mw-primary-text-color);
|
|
14
|
+
margin: spacing('8') 0 spacing('3');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
h3 {
|
|
18
|
+
font-size: font-size('md');
|
|
19
|
+
color: var(--mw-primary-text-color);
|
|
20
|
+
margin: spacing('6') 0 spacing('2');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ul,
|
|
24
|
+
ol {
|
|
25
|
+
margin: 0 0 spacing('3') spacing('7');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
li {
|
|
29
|
+
margin-bottom: spacing('1');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The first heading in a card already sits on the card's padding, the last
|
|
33
|
+
// paragraph on its bottom edge - without these the container carries double
|
|
34
|
+
// spacing at both ends.
|
|
35
|
+
> :first-child {
|
|
36
|
+
margin-top: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
> :last-child {
|
|
40
|
+
margin-bottom: 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
// A voice about the product - a quote card with its source. A figure, because
|
|
4
|
+
// quote and attribution belong together and that is the element that says so:
|
|
5
|
+
//
|
|
6
|
+
// <figure class="mw-testimonial">
|
|
7
|
+
// <blockquote>...</blockquote>
|
|
8
|
+
// <figcaption class="mw-testimonial-source">
|
|
9
|
+
// Name <span class="mw-testimonial-detail">· role or occasion</span>
|
|
10
|
+
// </figcaption>
|
|
11
|
+
// </figure>
|
|
12
|
+
.mw-testimonial {
|
|
13
|
+
@include surface;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
padding: spacing('5');
|
|
17
|
+
background: var(--mw-card-background);
|
|
18
|
+
border: 1px solid var(--mw-border);
|
|
19
|
+
box-shadow: var(--mw-elevation-2);
|
|
20
|
+
|
|
21
|
+
// Several of these in a row, each quote on the base blockquote tint, add up
|
|
22
|
+
// to a solid colored wall nobody reads through. The card already carries the
|
|
23
|
+
// surface - inside it the quote keeps only the edge and the quotation marks
|
|
24
|
+
// as its sign.
|
|
25
|
+
blockquote {
|
|
26
|
+
background: color-mix(in srgb, var(--mw-secondary-color) 6%, transparent);
|
|
27
|
+
border-left-color: color-mix(
|
|
28
|
+
in srgb,
|
|
29
|
+
var(--mw-secondary-color) 45%,
|
|
30
|
+
transparent
|
|
31
|
+
);
|
|
32
|
+
padding-block: spacing('2');
|
|
33
|
+
font-size: font-size('base');
|
|
34
|
+
line-height: 1.6;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// margin-top: auto, so in a grid of equal-height cards every source line sits
|
|
39
|
+
// on its card's bottom edge instead of floating under quotes of random length.
|
|
40
|
+
.mw-testimonial-source {
|
|
41
|
+
margin-top: auto;
|
|
42
|
+
padding-top: spacing('5');
|
|
43
|
+
font-size: font-size('sm');
|
|
44
|
+
font-weight: font-weight('bold');
|
|
45
|
+
color: var(--mw-primary-text-color);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.mw-testimonial-detail {
|
|
49
|
+
font-weight: font-weight('normal');
|
|
50
|
+
color: var(--mw-text-muted-color);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The one voice lifted above the grid - it may speak a size louder.
|
|
54
|
+
.mw-testimonial-featured {
|
|
55
|
+
blockquote {
|
|
56
|
+
font-size: font-size('md');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -220,6 +220,45 @@
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
// Masonry columns - for cards of unequal height, where a row grid tears a gap
|
|
224
|
+
// under every short one. CSS multi-column instead: entries flow into columns
|
|
225
|
+
// and close up, the column count steps down with the viewport. break-inside
|
|
226
|
+
// keeps each child whole - without it a page break lands mid-card.
|
|
227
|
+
//
|
|
228
|
+
// Reading order caveat: columns read top to bottom, then across. Right for
|
|
229
|
+
// testimonials and galleries, wrong for anything ranked or chronological -
|
|
230
|
+
// that stays in a grid.
|
|
231
|
+
%columns-base {
|
|
232
|
+
column-gap: spacing('6');
|
|
233
|
+
|
|
234
|
+
> * {
|
|
235
|
+
break-inside: avoid;
|
|
236
|
+
margin-bottom: spacing('6');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.mw-columns-2 {
|
|
241
|
+
@extend %columns-base;
|
|
242
|
+
columns: 2;
|
|
243
|
+
|
|
244
|
+
@include media-down('sm') {
|
|
245
|
+
columns: 1;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.mw-columns-3 {
|
|
250
|
+
@extend %columns-base;
|
|
251
|
+
columns: 3;
|
|
252
|
+
|
|
253
|
+
@include media-down('lg') {
|
|
254
|
+
columns: 2;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@include media-down('sm') {
|
|
258
|
+
columns: 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
223
262
|
// Base stack - items keep their natural width
|
|
224
263
|
.mw-grid-stack {
|
|
225
264
|
@extend %grid-base;
|
|
@@ -97,8 +97,15 @@
|
|
|
97
97
|
// width it has, so both variants get the strip back. Last of the three,
|
|
98
98
|
// because a media query adds no specificity and would otherwise lose to
|
|
99
99
|
// the end rule above.
|
|
100
|
+
//
|
|
101
|
+
// A smaller strip than on the desktop, though: a phone screen is barely
|
|
102
|
+
// taller than the hero text on it, so the full reserve is what tips the
|
|
103
|
+
// container past 100dvh - and everything below that line, the cue first,
|
|
104
|
+
// is off screen. min-height already leaves room here in most cases.
|
|
100
105
|
@include media-down('md') {
|
|
101
106
|
&:has(> .mw-scroll-hint) {
|
|
107
|
+
--mw-scroll-hint-reserve: #{spacing('6')};
|
|
108
|
+
|
|
102
109
|
padding-bottom: calc(
|
|
103
110
|
var(--mw-scroll-hint-offset) + var(--mw-scroll-hint-reserve)
|
|
104
111
|
);
|
|
@@ -124,12 +131,20 @@
|
|
|
124
131
|
flex-direction: column;
|
|
125
132
|
align-items: center;
|
|
126
133
|
gap: spacing('2');
|
|
127
|
-
color: color-mix(in srgb, var(--mw-hero-text-color)
|
|
134
|
+
color: color-mix(in srgb, var(--mw-hero-text-color) 72%, transparent);
|
|
128
135
|
font-size: font-size('xs');
|
|
129
136
|
letter-spacing: 0.08em;
|
|
130
137
|
text-transform: uppercase;
|
|
131
138
|
text-decoration: none;
|
|
132
139
|
animation: mw-scroll-hint-float 2.2s var(--mw-ease-in-out) infinite;
|
|
140
|
+
text-shadow: 0 1px 4px rgb(0 0 0 / 65%);
|
|
141
|
+
|
|
142
|
+
// The arrow carries the meaning, the word only confirms it - so it is the
|
|
143
|
+
// arrow that gets the size, and the label stays quiet at 'xs'.
|
|
144
|
+
> i,
|
|
145
|
+
> svg {
|
|
146
|
+
font-size: font-size('lg');
|
|
147
|
+
}
|
|
133
148
|
|
|
134
149
|
@include hover {
|
|
135
150
|
color: var(--mw-hero-text-color);
|
|
@@ -22,6 +22,20 @@
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// The lead line under a section title. Every landing page writes this
|
|
26
|
+
// paragraph, and it always wants the same four things: centred, muted,
|
|
27
|
+
// narrower than the container so it reads as a line rather than a block, and
|
|
28
|
+
// pulled up toward the title it belongs to - the title's gap is sized for the
|
|
29
|
+
// content below, not for its own subtitle.
|
|
30
|
+
.mw-section-intro {
|
|
31
|
+
max-width: 46rem;
|
|
32
|
+
margin: spacing('-3') auto spacing('9');
|
|
33
|
+
font-size: font-size('md');
|
|
34
|
+
color: var(--mw-text-muted-color);
|
|
35
|
+
text-align: center;
|
|
36
|
+
text-wrap: balance;
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
.mw-section-subtitle {
|
|
26
40
|
font-size: font-size('2xl');
|
|
27
41
|
margin: spacing('8') spacing('0') spacing('6') spacing('0');
|
|
@@ -110,6 +124,16 @@
|
|
|
110
124
|
.mw-section {
|
|
111
125
|
padding: var(--mw-section-padding-block) spacing('0');
|
|
112
126
|
position: relative;
|
|
127
|
+
|
|
128
|
+
// ...except around a hero. The hero container is already 100dvh, so the
|
|
129
|
+
// padding pushes exactly that much of it past the bottom of the screen -
|
|
130
|
+
// and with it the scroll cue sitting on its lower edge. A hero is the full
|
|
131
|
+
// screen by definition and brings its own breathing room.
|
|
132
|
+
@supports (selector(:has(.mw-hero))) {
|
|
133
|
+
&:has(.mw-hero) {
|
|
134
|
+
padding-block: spacing('0');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
// Pattern variant for sections
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
// Scroll entrance: a block rises briefly as it scrolls into view. Driven by
|
|
4
|
+
// the browser's scroll timeline - no JavaScript, no IntersectionObserver
|
|
5
|
+
// recalculating on every scroll step.
|
|
6
|
+
//
|
|
7
|
+
// Both guards matter. `prefers-reduced-motion` is the obvious one. The
|
|
8
|
+
// @supports guard is the safety net: a browser without scroll timelines would
|
|
9
|
+
// otherwise apply `both` fill on an animation that never runs and leave the
|
|
10
|
+
// block invisible forever - guarded, it simply renders in place.
|
|
11
|
+
//
|
|
12
|
+
// The range ends well before the block reaches the middle of the screen, so
|
|
13
|
+
// nothing is still moving while someone already reads it.
|
|
14
|
+
@supports (animation-timeline: view()) {
|
|
15
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
16
|
+
.mw-reveal {
|
|
17
|
+
animation: mw-reveal linear both;
|
|
18
|
+
animation-timeline: view();
|
|
19
|
+
animation-range: entry 2% entry 62%;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@keyframes mw-reveal {
|
|
25
|
+
from {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(26px);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
to {
|
|
31
|
+
opacity: 1;
|
|
32
|
+
transform: translateY(0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -140,10 +140,18 @@
|
|
|
140
140
|
-webkit-overflow-scrolling: touch;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
// Phone layout
|
|
143
|
+
// Phone layout. The section rhythm steps down twice: a tablet keeps a middle
|
|
144
|
+
// value, a phone drops further - every rem here is one the reader has to
|
|
145
|
+
// scroll past on a screen that is already all scrolling.
|
|
144
146
|
@include media-down('md') {
|
|
145
147
|
:root {
|
|
146
|
-
--mw-section-padding-block: #{spacing('
|
|
148
|
+
--mw-section-padding-block: #{spacing('9')};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@include media-down('sm') {
|
|
153
|
+
:root {
|
|
154
|
+
--mw-section-padding-block: #{spacing('7')};
|
|
147
155
|
}
|
|
148
156
|
}
|
|
149
157
|
|