maverick-wave 5.3.0 → 5.5.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/mw-maverick-wave/SKILL.md +13 -4
- package/.claude/skills/mw-maverick-wave/examples/angular-services.md +91 -0
- package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +5 -4
- package/.claude/skills/mw-maverick-wave/references/components.md +7 -0
- package/.claude/skills/mw-maverick-wave/references/javascript.md +20 -2
- package/.claude/skills/mw-maverick-wave/references/layout.md +81 -10
- package/.claude/skills/mw-maverick-wave/references/theming.md +10 -0
- package/CHANGELOG.md +18 -0
- package/CLAUDE.md +1 -1
- package/README.md +3 -4
- package/index.html +291 -3
- package/maverick-wave.min.css +6 -4
- package/maverick-wave.min.js +1 -1
- package/package.json +2 -2
- package/scripts/verify.js +4 -7
- package/src/js/main.js +237 -11
- package/src/partials/documentation-container.html +4 -0
- package/src/partials/footer-container.html +3 -0
- package/src/partials/history-container.html +67 -0
- package/src/partials/home-container.html +2 -0
- package/src/partials/palette-container.html +142 -0
- package/src/partials/parallax-container.html +114 -0
- package/src/scss/abstracts/_mixins.scss +6 -5
- package/src/scss/abstracts/_variables.scss +4 -0
- package/src/scss/base/_base.scss +1 -0
- package/src/scss/base/_reset.scss +4 -1
- package/src/scss/components/_accordions.scss +31 -7
- package/src/scss/components/_toasts.scss +1 -1
- package/src/scss/form-elements/_slider.scss +5 -23
- package/src/scss/layout/_header-reveal.scss +133 -0
- package/src/scss/layout/_index.scss +4 -0
- package/src/scss/layout/_main.scss +17 -2
- package/src/scss/layout/_parallax.scss +178 -0
- package/src/scss/utilities/_reveal.scss +22 -0
- package/.impeccable/config.local.json +0 -5
- package/.impeccable/hook.cache.json +0 -75
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
<div class="palette-panel" id="palette-panel" hidden>
|
|
13
|
+
<h3 class="palette-heading">Palette</h3>
|
|
14
|
+
<p class="palette-hint">
|
|
15
|
+
Two colors. Everything else - bar, ink, borders, tints - is derived from
|
|
16
|
+
them at runtime.
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<div class="palette-grid">
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
class="palette-item mw-active"
|
|
23
|
+
data-primary="#1031bb"
|
|
24
|
+
data-secondary="#bb5710"
|
|
25
|
+
data-dark="#171f30"
|
|
26
|
+
data-light="#e7e7f0"
|
|
27
|
+
>
|
|
28
|
+
Maverick
|
|
29
|
+
</button>
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
class="palette-item"
|
|
33
|
+
data-primary="#0f6f8c"
|
|
34
|
+
data-secondary="#e8862a"
|
|
35
|
+
data-dark="#0f1f26"
|
|
36
|
+
data-light="#e2edf1"
|
|
37
|
+
>
|
|
38
|
+
Deep Sea
|
|
39
|
+
</button>
|
|
40
|
+
<button
|
|
41
|
+
type="button"
|
|
42
|
+
class="palette-item"
|
|
43
|
+
data-primary="#c2321f"
|
|
44
|
+
data-secondary="#256f83"
|
|
45
|
+
data-dark="#13161a"
|
|
46
|
+
data-light="#f4e9e6"
|
|
47
|
+
>
|
|
48
|
+
Ember
|
|
49
|
+
</button>
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
class="palette-item"
|
|
53
|
+
data-primary="#2f7a3f"
|
|
54
|
+
data-secondary="#7f4dbd"
|
|
55
|
+
data-dark="#141f16"
|
|
56
|
+
data-light="#e6eee7"
|
|
57
|
+
>
|
|
58
|
+
Moss
|
|
59
|
+
</button>
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
class="palette-item"
|
|
63
|
+
data-primary="#8e3bd4"
|
|
64
|
+
data-secondary="#2fa38a"
|
|
65
|
+
data-dark="#1d1530"
|
|
66
|
+
data-light="#ece8f5"
|
|
67
|
+
>
|
|
68
|
+
Orchid
|
|
69
|
+
</button>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="palette-item"
|
|
73
|
+
data-primary="#46617f"
|
|
74
|
+
data-secondary="#c08a2e"
|
|
75
|
+
data-dark="#1a1f26"
|
|
76
|
+
data-light="#e8ebef"
|
|
77
|
+
>
|
|
78
|
+
Slate
|
|
79
|
+
</button>
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
class="palette-item"
|
|
83
|
+
data-primary="#00b3ff"
|
|
84
|
+
data-secondary="#ff2d95"
|
|
85
|
+
data-dark="#0d1117"
|
|
86
|
+
data-light="#e6f3fb"
|
|
87
|
+
>
|
|
88
|
+
Neon
|
|
89
|
+
</button>
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
class="palette-item"
|
|
93
|
+
data-primary="#9a5f23"
|
|
94
|
+
data-secondary="#3b6ea5"
|
|
95
|
+
data-dark="#241c15"
|
|
96
|
+
data-light="#f2eadd"
|
|
97
|
+
>
|
|
98
|
+
Sand
|
|
99
|
+
</button>
|
|
100
|
+
<button
|
|
101
|
+
type="button"
|
|
102
|
+
class="palette-item"
|
|
103
|
+
data-primary="#12a37a"
|
|
104
|
+
data-secondary="#e2699b"
|
|
105
|
+
data-dark="#101f1b"
|
|
106
|
+
data-light="#e2f1ec"
|
|
107
|
+
>
|
|
108
|
+
Mint
|
|
109
|
+
</button>
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
class="palette-item"
|
|
113
|
+
data-primary="#4b2fc4"
|
|
114
|
+
data-secondary="#d9a72e"
|
|
115
|
+
data-dark="#171331"
|
|
116
|
+
data-light="#eae8f7"
|
|
117
|
+
>
|
|
118
|
+
Royal
|
|
119
|
+
</button>
|
|
120
|
+
<button
|
|
121
|
+
type="button"
|
|
122
|
+
class="palette-item"
|
|
123
|
+
data-primary="#9b1b4a"
|
|
124
|
+
data-secondary="#23a08d"
|
|
125
|
+
data-dark="#1e1220"
|
|
126
|
+
data-light="#f3e7ec"
|
|
127
|
+
>
|
|
128
|
+
Wine
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="palette-custom">
|
|
133
|
+
<label class="palette-field">
|
|
134
|
+
<span>Primary</span>
|
|
135
|
+
<input type="color" id="palette-primary" value="#1031bb" />
|
|
136
|
+
</label>
|
|
137
|
+
<label class="palette-field">
|
|
138
|
+
<span>Secondary</span>
|
|
139
|
+
<input type="color" id="palette-secondary" value="#bb5710" />
|
|
140
|
+
</label>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<h3 class="mw-section-subtitle">Parallax</h3>
|
|
2
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
3
|
+
Two ways to play a picture against the scroll, both on the browser's scroll
|
|
4
|
+
timeline - no listener, and no
|
|
5
|
+
<code>background-attachment: fixed</code>, which iOS ignores. The image lives
|
|
6
|
+
in its own <code>mw-parallax-media</code> layer, as an
|
|
7
|
+
<code><img></code> or as a div with a background image. Where scroll
|
|
8
|
+
timelines are missing, or with <code>prefers-reduced-motion</code>, the
|
|
9
|
+
picture stands still and nothing else changes.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<h4 class="mw-text-medium mw-mb-2">
|
|
13
|
+
Drift - <code>mw-parallax</code>, the picture moves slower than the page
|
|
14
|
+
</h4>
|
|
15
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
16
|
+
How far it travels is <code>--mw-parallax-depth</code> (<code>10vh</code> by
|
|
17
|
+
default); the layer overhangs the block by exactly that much, so no edge is
|
|
18
|
+
ever uncovered. <code>mw-parallax-dimmed</code> turns the picture down and the
|
|
19
|
+
text light.
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<div class="mw-grid-1 mw-gap-6 mw-mb-6">
|
|
23
|
+
<div class="mw-parallax mw-parallax-dimmed">
|
|
24
|
+
<img
|
|
25
|
+
class="mw-parallax-media"
|
|
26
|
+
src="images/slider-field.svg"
|
|
27
|
+
alt=""
|
|
28
|
+
loading="lazy"
|
|
29
|
+
/>
|
|
30
|
+
<div class="mw-parallax-content mw-text-center">
|
|
31
|
+
<h3>Scroll past me</h3>
|
|
32
|
+
<p class="mw-mb-0">The field moves slower than the page does.</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="mw-parallax" style="--mw-parallax-depth: 20vh">
|
|
37
|
+
<img
|
|
38
|
+
class="mw-parallax-media"
|
|
39
|
+
src="images/gallery-mountain.svg"
|
|
40
|
+
alt="Mountain range"
|
|
41
|
+
loading="lazy"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<h4 class="mw-text-medium mw-mb-2">
|
|
47
|
+
Reveal - <code>mw-parallax-sticky</code>, the picture stays and the page rides
|
|
48
|
+
over it
|
|
49
|
+
</h4>
|
|
50
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
51
|
+
The home section of this page does it: the block pins itself to the top of the
|
|
52
|
+
screen and everything after it slides up over the picture. It goes on whatever
|
|
53
|
+
element the page content is a <strong>sibling</strong> of - the section around
|
|
54
|
+
a hero, not the hero itself - because that is what sticky and the sibling
|
|
55
|
+
selector need. Everything after it gets the page background and a layer above,
|
|
56
|
+
or the pinned picture shows through. Pair it with
|
|
57
|
+
<code>mw-header-reveal</code> on the header and the page opens on the picture
|
|
58
|
+
alone: the bar stays away for the first 130px of scroll and has ridden in by
|
|
59
|
+
420px.
|
|
60
|
+
</p>
|
|
61
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
62
|
+
Add <code>mw-parallax-rise</code> next to it and the picture lifts by
|
|
63
|
+
<code>12vh</code> while the text stays where it is - a pinned block does not
|
|
64
|
+
travel through the viewport, so that layer runs on the document's own scroll
|
|
65
|
+
instead. <code>--mw-parallax-depth</code> is the dial, here as everywhere.
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
<h4 class="mw-text-medium mw-mb-2">
|
|
69
|
+
Pattern - <code>mw-parallax-pattern</code>, a second layer that makes the
|
|
70
|
+
movement visible
|
|
71
|
+
</h4>
|
|
72
|
+
<p class="mw-text-muted mw-text-measure mw-mb-6">
|
|
73
|
+
The travel is vertical, so only an edge across it - a rule, a grid, a hatch -
|
|
74
|
+
ever shows it. A gradient has none and slides without looking like it moves.
|
|
75
|
+
Add a second <code>mw-parallax-media</code> carrying
|
|
76
|
+
<code>mw-parallax-pattern</code>: it travels a third of the distance, and it
|
|
77
|
+
is the difference between the two layers that reads as depth. In a hero the
|
|
78
|
+
pattern comes from <code>$mw-hero-pattern</code>, the way the picture comes
|
|
79
|
+
from <code>$mw-hero-image</code>; anywhere else set
|
|
80
|
+
<code>--mw-parallax-pattern-image</code> on it.
|
|
81
|
+
</p>
|
|
82
|
+
|
|
83
|
+
<div class="mw-parallax mw-parallax-dimmed mw-mb-6">
|
|
84
|
+
<div class="mw-parallax-media parallax-demo-ground"></div>
|
|
85
|
+
<div class="mw-parallax-media mw-parallax-pattern parallax-demo-lines"></div>
|
|
86
|
+
<div class="mw-parallax-content mw-text-center">
|
|
87
|
+
<h3>The grid drifts, the gradient holds</h3>
|
|
88
|
+
<p class="mw-mb-0">
|
|
89
|
+
Both layers move. Only the one with edges tells you so.
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="mw-code-block">
|
|
95
|
+
<div class="mw-code-block-header">
|
|
96
|
+
<span class="mw-code-block-filename">index.html</span>
|
|
97
|
+
<span class="mw-code-block-language">HTML</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="mw-code-container">
|
|
100
|
+
<pre><code><header class="mw-header mw-header-reveal">...</header>
|
|
101
|
+
|
|
102
|
+
<main class="mw-main">
|
|
103
|
+
<section class="mw-section mw-parallax-sticky mw-parallax-rise">
|
|
104
|
+
<div class="mw-container mw-parallax">
|
|
105
|
+
<div class="mw-parallax-media"></div>
|
|
106
|
+
<div class="mw-parallax-media mw-parallax-pattern"></div>
|
|
107
|
+
<div class="mw-hero">...</div>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
|
|
111
|
+
<section class="mw-section">...</section>
|
|
112
|
+
</main></code></pre>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
@@ -253,11 +253,12 @@
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
// The reveal zone as the range of the n-th column
|
|
257
|
-
//
|
|
258
|
-
//
|
|
256
|
+
// The reveal zone as the range of the n-th column, measured along the block's own
|
|
257
|
+
// entry and not in viewport lengths: a zone in dvh is the same length whatever the
|
|
258
|
+
// block measures, so a card shorter than the zone stands fully in place while it is
|
|
259
|
+
// still transparent. Each further column starts a tenth of that entry later.
|
|
259
260
|
@mixin reveal-range($column: 1) {
|
|
260
|
-
$shift:
|
|
261
|
+
$shift: 10% * ($column - 1);
|
|
261
262
|
|
|
262
|
-
animation-range: entry #{
|
|
263
|
+
animation-range: entry #{$shift} entry #{60% + $shift};
|
|
263
264
|
}
|
|
@@ -4,6 +4,10 @@ $mw-theme-mode: 'switchable' !default; // 'dark', 'light', or 'switchable'
|
|
|
4
4
|
|
|
5
5
|
$mw-hero-image: url('https://images.unsplash.com/photo-1591638462868-19cfad9c49d1?q=80&w=1300&auto=format&fit=crop') !default;
|
|
6
6
|
|
|
7
|
+
// A second, flatter layer over the image - see mw-parallax-pattern. Fine lines
|
|
8
|
+
// or a grid, because a gradient has no edge across the travel to show it moving.
|
|
9
|
+
$mw-hero-pattern: none !default;
|
|
10
|
+
|
|
7
11
|
// Fixed to the light end rather than theme-bound: the photo does not change with
|
|
8
12
|
// the theme, so its ink must not either. Use --mw-light-text-color on a bright one.
|
|
9
13
|
$mw-hero-text-color: var(--mw-dark-text-color) !default;
|
package/src/scss/base/_base.scss
CHANGED
|
@@ -24,9 +24,12 @@
|
|
|
24
24
|
-webkit-font-smoothing: antialiased;
|
|
25
25
|
-moz-osx-font-smoothing: grayscale;
|
|
26
26
|
|
|
27
|
+
// `dvh` and not `vh`: on iOS `100vh` is the tall viewport behind the
|
|
28
|
+
// address bar, so the locked body stays taller than the screen and keeps a
|
|
29
|
+
// strip of rubber-band scroll under the modal.
|
|
27
30
|
&:has(.mw-modal-open) {
|
|
28
31
|
overflow: hidden;
|
|
29
|
-
height:
|
|
32
|
+
height: 100dvh;
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -83,27 +83,51 @@
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// A grid row from `0fr` to `1fr`, and not `height: 0` to `auto`: `auto` is
|
|
87
|
+
// not an animatable value, and `interpolate-size`, which would make it one,
|
|
88
|
+
// is Chrome's alone - Firefox and Safari jumped the panel open instead of
|
|
89
|
+
// running the 900ms. A fraction interpolates in all three.
|
|
86
90
|
&-content {
|
|
87
|
-
|
|
91
|
+
display: grid;
|
|
92
|
+
grid-template-rows: 0fr;
|
|
88
93
|
overflow: hidden;
|
|
89
|
-
transition:
|
|
94
|
+
transition: grid-template-rows var(--mw-duration-slower)
|
|
95
|
+
var(--mw-ease-out);
|
|
90
96
|
background: var(--mw-card-background);
|
|
91
|
-
interpolate-size: allow-keywords;
|
|
92
97
|
|
|
93
98
|
&-inner {
|
|
94
|
-
padding
|
|
95
|
-
border-
|
|
99
|
+
// The row is never shorter than what the item contributes, and padding
|
|
100
|
+
// and a border do not collapse with it - so both travel along, or the
|
|
101
|
+
// shut panel keeps a 25px strip of itself on screen. No `overflow` here
|
|
102
|
+
// on purpose: the panel above already clips, and clipping twice would
|
|
103
|
+
// hide the overflow that makes a long panel scrollable.
|
|
104
|
+
min-height: 0;
|
|
105
|
+
padding: spacing('0') spacing('4');
|
|
106
|
+
border-top: 0 solid var(--mw-border-accent);
|
|
107
|
+
transition:
|
|
108
|
+
padding-block var(--mw-duration-slower) var(--mw-ease-out),
|
|
109
|
+
border-top-width var(--mw-duration-slower) var(--mw-ease-out);
|
|
110
|
+
|
|
96
111
|
@include media-down('sm') {
|
|
97
112
|
font-size: font-size('sm');
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
@include active() {
|
|
102
|
-
|
|
117
|
+
grid-template-rows: 1fr;
|
|
103
118
|
max-height: 500px;
|
|
104
119
|
overflow-y: auto;
|
|
105
120
|
|
|
106
|
-
|
|
121
|
+
> .mw-accordion-content-inner {
|
|
122
|
+
padding-block: spacing('4');
|
|
123
|
+
border-top-width: 1px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Both spellings: the standard pair is what Firefox reads, the webkit
|
|
127
|
+
// pseudos are for Safari below 18.2.
|
|
128
|
+
scrollbar-width: thin;
|
|
129
|
+
scrollbar-color: var(--mw-primary-color-hover) var(--mw-card-background);
|
|
130
|
+
|
|
107
131
|
&::-webkit-scrollbar {
|
|
108
132
|
width: 6px;
|
|
109
133
|
}
|
|
@@ -38,10 +38,14 @@
|
|
|
38
38
|
transform: scale(1.1);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Same fill and ring as the webkit thumb above - Firefox paints its own
|
|
42
|
+
// grey thumb unless `background` is set, so leaving it out is not the same
|
|
43
|
+
// declaration set, it is a different slider.
|
|
41
44
|
&::-moz-range-thumb {
|
|
42
45
|
width: spacing('5');
|
|
43
46
|
height: spacing('5');
|
|
44
|
-
|
|
47
|
+
background: var(--fill);
|
|
48
|
+
border: 2px solid var(--mw-border);
|
|
45
49
|
border-radius: radius('full');
|
|
46
50
|
transition:
|
|
47
51
|
transform var(--mw-duration-fast) var(--mw-ease-out),
|
|
@@ -55,20 +59,6 @@
|
|
|
55
59
|
background: transparent;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
// IE
|
|
59
|
-
&::-ms-fill-lower {
|
|
60
|
-
background: var(--fill);
|
|
61
|
-
}
|
|
62
|
-
&::-ms-fill-upper {
|
|
63
|
-
background: var(--track);
|
|
64
|
-
}
|
|
65
|
-
&::-ms-thumb {
|
|
66
|
-
width: spacing('5');
|
|
67
|
-
height: spacing('5');
|
|
68
|
-
border: 2px solid var(--fill);
|
|
69
|
-
border-radius: radius('full');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
62
|
&-sm {
|
|
73
63
|
height: 6px;
|
|
74
64
|
&::-webkit-slider-thumb {
|
|
@@ -79,10 +69,6 @@
|
|
|
79
69
|
width: spacing('4');
|
|
80
70
|
height: spacing('4');
|
|
81
71
|
}
|
|
82
|
-
&::-ms-thumb {
|
|
83
|
-
width: spacing('4');
|
|
84
|
-
height: spacing('4');
|
|
85
|
-
}
|
|
86
72
|
}
|
|
87
73
|
&-lg {
|
|
88
74
|
height: 16px;
|
|
@@ -95,10 +81,6 @@
|
|
|
95
81
|
width: spacing('6');
|
|
96
82
|
height: spacing('6');
|
|
97
83
|
}
|
|
98
|
-
&::-ms-thumb {
|
|
99
|
-
width: spacing('6');
|
|
100
|
-
height: spacing('6');
|
|
101
|
-
}
|
|
102
84
|
}
|
|
103
85
|
&:disabled {
|
|
104
86
|
opacity: 0.3;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
@layer mw.layout {
|
|
2
|
+
// For a page that opens on a full-bleed picture and wants nothing on top of
|
|
3
|
+
// it: the bar starts above the screen and rides in over the first stretch of
|
|
4
|
+
// scroll. Inside the @supports, so a browser without scroll timelines gets a
|
|
5
|
+
// normal header rather than none at all.
|
|
6
|
+
//
|
|
7
|
+
// Its own file, and not next to .mw-header: that one uses the `animation`
|
|
8
|
+
// shorthand, which a consumer bundler folds together with a timeline into a
|
|
9
|
+
// declaration no browser accepts.
|
|
10
|
+
@supports (animation-timeline: scroll()) {
|
|
11
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
12
|
+
.mw-header-reveal {
|
|
13
|
+
animation-name: mw-header-reveal;
|
|
14
|
+
animation-timing-function: var(--mw-ease-out);
|
|
15
|
+
animation-fill-mode: both;
|
|
16
|
+
animation-timeline: scroll(root);
|
|
17
|
+
animation-range: 0 420px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The ribbon is glued under the bar, so it has to leave and arrive with
|
|
21
|
+
// it - left behind it would hang alone at the top of an otherwise clean
|
|
22
|
+
// hero. No class of its own: a ribbon on a page with a revealing header
|
|
23
|
+
// has no other sensible behaviour.
|
|
24
|
+
body:has(.mw-header-reveal)
|
|
25
|
+
.mw-announcement:not(.mw-announcement-static) {
|
|
26
|
+
animation-name: mw-announcement-reveal;
|
|
27
|
+
animation-timing-function: var(--mw-ease-out);
|
|
28
|
+
animation-fill-mode: both;
|
|
29
|
+
animation-timeline: scroll(root);
|
|
30
|
+
animation-range: 0 420px;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Both cover the height of the pair, or the shorter traveller arrives first
|
|
36
|
+
// and the two come in as two things instead of one block. Without a ribbon
|
|
37
|
+
// that height is just the bar's.
|
|
38
|
+
.mw-header-reveal {
|
|
39
|
+
--mw-header-reveal-offset: var(--mw-header-height);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
body:has(.mw-announcement:not(.mw-announcement-static)) .mw-header-reveal {
|
|
43
|
+
--mw-header-reveal-offset: calc(
|
|
44
|
+
var(--mw-header-height) + var(--mw-announcement-height)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// A keyboard reaches the chrome long before the first scroll does, and a focus
|
|
49
|
+
// ring on something that is off screen is WCAG 2.4.11 - so focus brings it
|
|
50
|
+
// back, either half of the pair pulling in the other. `!important` is the one
|
|
51
|
+
// author declaration that outranks a running animation; dropping
|
|
52
|
+
// `animation-name` instead leaves the element stuck on its last value, because
|
|
53
|
+
// a restarted scroll timeline only catches up on the next scroll.
|
|
54
|
+
body:has(.mw-header-reveal:focus-within, .mw-announcement:focus-within) {
|
|
55
|
+
.mw-header-reveal {
|
|
56
|
+
top: 0 !important;
|
|
57
|
+
opacity: 1 !important;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.mw-announcement:not(.mw-announcement-static) {
|
|
61
|
+
transform: none !important;
|
|
62
|
+
opacity: 1 !important;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Firefox ships no scroll timelines, so there the script toggles these
|
|
67
|
+
// classes instead (main.js) - the same two positions, triggered instead of
|
|
68
|
+
// scrubbed. State and transition are separate because the script sets the
|
|
69
|
+
// state a frame earlier: together they would send the bar sliding away in
|
|
70
|
+
// front of the reader instead of having it gone already.
|
|
71
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
72
|
+
.mw-header-reveal.mw-header-away {
|
|
73
|
+
top: calc(-1 * var(--mw-header-reveal-offset));
|
|
74
|
+
opacity: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.mw-header-driven {
|
|
78
|
+
// The shared list plus `top`, which is not in it - writing only `top`
|
|
79
|
+
// here would drop the header's own colour and shadow transitions.
|
|
80
|
+
transition:
|
|
81
|
+
var(--mw-transition),
|
|
82
|
+
top var(--mw-duration-slow) var(--mw-ease-out);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.mw-announcement.mw-announcement-away {
|
|
86
|
+
transform: translateY(
|
|
87
|
+
calc(-1 * (var(--mw-header-height) + var(--mw-announcement-height)))
|
|
88
|
+
);
|
|
89
|
+
opacity: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.mw-announcement-driven {
|
|
93
|
+
transition:
|
|
94
|
+
transform var(--mw-duration-slow) var(--mw-ease-out),
|
|
95
|
+
opacity var(--mw-duration-slow) var(--mw-ease-out);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// `top` and not a transform: the off-canvas drawer inside the bar is
|
|
100
|
+
// position: fixed, and a transform on the header would make it the drawer's
|
|
101
|
+
// containing block - at every value, the resting one included.
|
|
102
|
+
// The wait sits in the keyframes and not in the range, because a range that
|
|
103
|
+
// starts past 0 leaves the bar in its resting state below that point - so the
|
|
104
|
+
// header would be visible for exactly the stretch it should stay away for.
|
|
105
|
+
@keyframes mw-header-reveal {
|
|
106
|
+
0%,
|
|
107
|
+
30% {
|
|
108
|
+
top: calc(-1 * var(--mw-header-reveal-offset));
|
|
109
|
+
opacity: 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
100% {
|
|
113
|
+
top: 0;
|
|
114
|
+
opacity: 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The ribbon holds nothing fixed, so it can use the cheaper transform
|
|
119
|
+
@keyframes mw-announcement-reveal {
|
|
120
|
+
0%,
|
|
121
|
+
30% {
|
|
122
|
+
transform: translateY(
|
|
123
|
+
calc(-1 * (var(--mw-header-height) + var(--mw-announcement-height)))
|
|
124
|
+
);
|
|
125
|
+
opacity: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
100% {
|
|
129
|
+
transform: translateY(0);
|
|
130
|
+
opacity: 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
@forward 'footer';
|
|
2
2
|
@forward 'grid';
|
|
3
3
|
@forward 'header';
|
|
4
|
+
@forward 'header-reveal';
|
|
4
5
|
@forward 'home';
|
|
5
6
|
@forward 'main';
|
|
6
7
|
@forward 'page-header';
|
|
7
8
|
@forward 'section';
|
|
9
|
+
|
|
10
|
+
// Last on purpose: mw-parallax-sticky has to beat the `position: relative`
|
|
11
|
+
@forward 'parallax';
|
|
@@ -41,12 +41,27 @@
|
|
|
41
41
|
transition: var(--mw-transition);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// ...so the content has to sit above it
|
|
45
|
-
|
|
44
|
+
// ...so the content has to sit above it - except the parallax layer,
|
|
45
|
+
// which is the one thing that belongs underneath
|
|
46
|
+
> *:not(.mw-parallax-media) {
|
|
46
47
|
position: relative;
|
|
47
48
|
z-index: z-index('card');
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
// Add `mw-parallax` here and a `.mw-parallax-media` child and the picture
|
|
52
|
+
// becomes its own layer, free to drift or to stay pinned. It reads the
|
|
53
|
+
// same token, so the image is still configured in one place - and a
|
|
54
|
+
// second media element carrying `mw-parallax-pattern` takes the other.
|
|
55
|
+
&:has(> .mw-parallax-media) {
|
|
56
|
+
background-image: none;
|
|
57
|
+
|
|
58
|
+
--mw-parallax-pattern-image: var(--mw-hero-pattern);
|
|
59
|
+
|
|
60
|
+
> .mw-parallax-media:not(.mw-parallax-pattern) {
|
|
61
|
+
background-image: var(--mw-hero-background);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
// The same shadow the scroll cue already carries: the light theme lifts
|
|
51
66
|
// the photo to brightness(1.1) and pale ink alone stops holding its edge
|
|
52
67
|
h1,
|