maverick-wave 6.7.0 → 6.9.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/README.md +2 -2
- package/maverick-wave.min.css +9 -9
- package/package.json +2 -2
- package/src/scss/abstracts/_functions.scss +26 -3
- package/src/scss/abstracts/_mixins.scss +9 -0
- package/src/scss/abstracts/_variables.scss +10 -4
- package/src/scss/components/_action-bar.scss +29 -0
- package/src/scss/components/_code.scss +1 -1
- package/src/scss/components/_techstack-bucket.scss +3 -3
- package/src/scss/layout/_header-reveal.scss +133 -135
- package/src/scss/layout/_header.scss +23 -39
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maverick-wave",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.9.0",
|
|
4
4
|
"config": {
|
|
5
|
-
"version_short": "6.
|
|
5
|
+
"version_short": "6.9"
|
|
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",
|
|
@@ -114,12 +114,21 @@
|
|
|
114
114
|
// rather than off the end.
|
|
115
115
|
$_radius-floor: map.get(v.$radius, 'xs');
|
|
116
116
|
|
|
117
|
+
// The sharp half of the signature on a value of its own, so the scale can round
|
|
118
|
+
// controls up without softening the one corner meant to read as sharp
|
|
119
|
+
$_radius-sharp-base: 5px;
|
|
120
|
+
|
|
117
121
|
// Emitted through --mw-radius-scale, so one number retunes every rounded box in
|
|
118
122
|
// the framework at once - 0 squares them off, above 1 rounds them further, and
|
|
119
123
|
// the corner arc follows. 'none' and 'full' pass through: an explicit 0 stays
|
|
120
|
-
// 0, and a circle has to stay a circle.
|
|
124
|
+
// 0, and a circle has to stay a circle. A length instead of a key takes the
|
|
125
|
+
// same scale and floor, for the few values that sit off the scale.
|
|
121
126
|
@function radius($key) {
|
|
122
|
-
$value:
|
|
127
|
+
$value: $key;
|
|
128
|
+
|
|
129
|
+
@if meta.type-of($key) != 'number' {
|
|
130
|
+
$value: map.get(v.$radius, $key);
|
|
131
|
+
}
|
|
123
132
|
|
|
124
133
|
@if $key == 'none' or $key == 'full' {
|
|
125
134
|
@return $value;
|
|
@@ -137,7 +146,21 @@ $_radius-floor: map.get(v.$radius, 'xs');
|
|
|
137
146
|
|
|
138
147
|
@return max(
|
|
139
148
|
#{$_radius-floor},
|
|
140
|
-
calc(#{
|
|
149
|
+
calc(#{$_radius-sharp-base} * #{$scale} * #{$scale})
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Where a floating island sits: percentages on a fixed box are the viewport's,
|
|
154
|
+
// so this puts the header's content box on the page container,
|
|
155
|
+
// $header-island-reach wider on either side - and the bar at the foot on the
|
|
156
|
+
// same edges. Logo and links stay put while the header docks around them.
|
|
157
|
+
@function island-edge() {
|
|
158
|
+
@return max(
|
|
159
|
+
calc(var(--mw-header-inset) - #{v.$header-island-reach}),
|
|
160
|
+
calc(
|
|
161
|
+
(100% - var(--mw-container-width)) / 2 - var(--mw-header-pad) -
|
|
162
|
+
#{v.$header-island-reach}
|
|
163
|
+
)
|
|
141
164
|
);
|
|
142
165
|
}
|
|
143
166
|
|
|
@@ -276,6 +276,15 @@
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
// The floating chrome, shared by the header island and the action bar island
|
|
280
|
+
@mixin island-surface {
|
|
281
|
+
background: var(--mw-header-glass);
|
|
282
|
+
backdrop-filter: blur(20px) saturate(1.6);
|
|
283
|
+
border: 1px solid var(--mw-header-island-border);
|
|
284
|
+
border-radius: radius('xl');
|
|
285
|
+
box-shadow: var(--mw-elevation-4);
|
|
286
|
+
}
|
|
287
|
+
|
|
279
288
|
// The MaverickWave silhouette: sharp corners on one diagonal, round on the other,
|
|
280
289
|
// with an accent arc on the round pair - the asymmetry alone reads as an accident.
|
|
281
290
|
//
|
|
@@ -434,9 +434,9 @@ $header-colors: (
|
|
|
434
434
|
);
|
|
435
435
|
// The room the header takes from the top of the viewport - island plus the gap
|
|
436
436
|
// above it - so every offset that reads it clears the island without knowing it
|
|
437
|
-
// floats. The island itself is
|
|
437
|
+
// floats. The island itself is 63px: a 44px touch control + 2 × spacing('2') +
|
|
438
438
|
// 2px border, with room to spare around the 42px logo.
|
|
439
|
-
$header-height:
|
|
439
|
+
$header-height: 77px;
|
|
440
440
|
// Between the island and the viewport edge
|
|
441
441
|
$header-inset: 14px;
|
|
442
442
|
// How far the island reaches past the page container on either side, and in
|
|
@@ -537,14 +537,20 @@ $breakpoints: (
|
|
|
537
537
|
$radius: (
|
|
538
538
|
'none': 0,
|
|
539
539
|
'xs': 2px,
|
|
540
|
-
'sm':
|
|
541
|
-
'md':
|
|
540
|
+
'sm': 7px,
|
|
541
|
+
'md': 12px,
|
|
542
542
|
'lg': 15px,
|
|
543
543
|
'xl': 20px,
|
|
544
544
|
'2xl': 30px,
|
|
545
545
|
'full': 50%,
|
|
546
546
|
);
|
|
547
547
|
|
|
548
|
+
// Off the scale on purpose: a chip next to a tag stays tighter than the buttons
|
|
549
|
+
// and fields it would otherwise be mistaken for. Outside the map, or every key
|
|
550
|
+
// would turn into a mw-radius-* utility.
|
|
551
|
+
$chip-radius: 10px;
|
|
552
|
+
$chip-radius-sm: 5px;
|
|
553
|
+
|
|
548
554
|
// Z-index
|
|
549
555
|
$z-index: (
|
|
550
556
|
'lift': 3,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
@use '../abstracts' as *;
|
|
2
2
|
|
|
3
|
+
// Clear of the home indicator where there is one, the header's gap where not
|
|
4
|
+
$_island-bottom: max(var(--mw-header-inset), env(safe-area-inset-bottom, 0px));
|
|
5
|
+
// Lighter than the flat bar, and as low as it goes: the items keep their
|
|
6
|
+
// 2.75rem touch floor, so the rest is only the padding around them
|
|
7
|
+
$_island-height: calc(var(--mw-action-bar-height) - 4px);
|
|
8
|
+
|
|
3
9
|
@layer mw.components {
|
|
4
10
|
:root {
|
|
5
11
|
--mw-action-bar-height: 3.75rem;
|
|
@@ -18,6 +24,10 @@
|
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
26
|
|
|
27
|
+
:root:has(.mw-action-bar-island:not(.mw-action-bar-embedded)) {
|
|
28
|
+
--mw-action-bar-reserve: calc(#{$_island-height} + #{$_island-bottom});
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
// Chrome like the header, so it takes the header's colours in both themes
|
|
22
32
|
.mw-action-bar {
|
|
23
33
|
--mw-focus-ring-color: var(--mw-header-navbar-list-active-color);
|
|
@@ -44,6 +54,19 @@
|
|
|
44
54
|
position: absolute;
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
&-island {
|
|
58
|
+
inset-inline: island-edge();
|
|
59
|
+
bottom: $_island-bottom;
|
|
60
|
+
min-height: $_island-height;
|
|
61
|
+
padding: spacing('1');
|
|
62
|
+
@include island-surface;
|
|
63
|
+
|
|
64
|
+
// Its own frame has no page container to line up with
|
|
65
|
+
&.mw-action-bar-embedded {
|
|
66
|
+
inset-inline: calc(var(--mw-header-inset) - #{$header-island-reach});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
47
70
|
&-item {
|
|
48
71
|
display: flex;
|
|
49
72
|
flex-direction: column;
|
|
@@ -129,6 +152,12 @@
|
|
|
129
152
|
justify-content: center;
|
|
130
153
|
grid-auto-columns: minmax(0, 10rem);
|
|
131
154
|
}
|
|
155
|
+
|
|
156
|
+
// A dock: a container-wide island would float the targets in empty glass
|
|
157
|
+
.mw-action-bar-island {
|
|
158
|
+
width: fit-content;
|
|
159
|
+
margin-inline: auto;
|
|
160
|
+
}
|
|
132
161
|
}
|
|
133
162
|
|
|
134
163
|
@media print {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
text-decoration: none;
|
|
14
14
|
background: var(--mw-card-background);
|
|
15
15
|
border: 1px solid var(--mw-border);
|
|
16
|
-
border-radius: radius(
|
|
16
|
+
border-radius: radius($chip-radius);
|
|
17
17
|
padding: spacing('2');
|
|
18
18
|
transition: var(--mw-transition-fast);
|
|
19
19
|
color: var(--mw-text-color);
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
padding: spacing('1') spacing('2');
|
|
56
56
|
min-width: 32px;
|
|
57
57
|
min-height: 32px;
|
|
58
|
-
border-radius: radius(
|
|
58
|
+
border-radius: radius($chip-radius-sm);
|
|
59
59
|
|
|
60
60
|
.mw-techstack-logo {
|
|
61
61
|
width: 18px;
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
|
|
79
79
|
&-logo {
|
|
80
80
|
object-fit: contain;
|
|
81
|
-
border-radius: radius(
|
|
81
|
+
border-radius: radius($chip-radius-sm);
|
|
82
82
|
transition: transform var(--mw-duration-fast) var(--mw-ease-out);
|
|
83
83
|
flex-shrink: 0;
|
|
84
84
|
}
|
|
@@ -9,7 +9,6 @@ $dock-range: 160px;
|
|
|
9
9
|
$float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
10
10
|
|
|
11
11
|
@layer mw.layout {
|
|
12
|
-
// None of it on the flat bar, which stands where it is from the first frame
|
|
13
12
|
:root:not(.mw-header-bar) {
|
|
14
13
|
// A header that stays put starts as the full bar and docks into the island over
|
|
15
14
|
// the first stretch of scroll. A revealing one is off screen up there anyway and
|
|
@@ -51,172 +50,171 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
}
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
55
|
+
// The bar starts above the screen and rides in over the first stretch of scroll.
|
|
56
|
+
// Its own file, and not next to .mw-header: that one uses the `animation`
|
|
57
|
+
// shorthand, which a consumer bundler folds together with a timeline into a
|
|
58
|
+
// declaration no browser accepts.
|
|
59
|
+
@supports (animation-timeline: scroll()) {
|
|
60
|
+
.mw-header-reveal {
|
|
61
|
+
@include motion-safe {
|
|
62
|
+
animation-name: mw-header-reveal;
|
|
63
|
+
animation-timing-function: var(--mw-ease-out);
|
|
64
|
+
animation-fill-mode: both;
|
|
65
|
+
animation-timeline: scroll(root);
|
|
66
|
+
animation-range: 0 420px;
|
|
68
67
|
}
|
|
68
|
+
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
70
|
+
// The bar travels, a keep element travels the same distance back and stays
|
|
71
|
+
// put - for a burger that has to be reachable while the hero is untouched.
|
|
72
|
+
.mw-header-reveal .mw-header-keep {
|
|
73
|
+
@include motion-safe {
|
|
74
|
+
animation-name: mw-header-keep;
|
|
75
|
+
animation-timing-function: var(--mw-ease-out);
|
|
76
|
+
animation-fill-mode: both;
|
|
77
|
+
animation-timeline: scroll(root);
|
|
78
|
+
animation-range: 0 420px;
|
|
80
79
|
}
|
|
80
|
+
}
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
82
|
+
// A group's opacity cannot be taken back by a child, so the fade goes as
|
|
83
|
+
// soon as something is meant to stay visible through it.
|
|
84
|
+
.mw-header-reveal:has(.mw-header-keep) {
|
|
85
|
+
@include motion-safe {
|
|
86
|
+
animation-name: mw-header-slide;
|
|
88
87
|
}
|
|
88
|
+
}
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
animation-range: 0 420px;
|
|
100
|
-
}
|
|
90
|
+
// Glued under the bar, so it leaves and arrives with it. No class of its
|
|
91
|
+
// own: a ribbon under a revealing header has no other sensible behaviour.
|
|
92
|
+
body:has(.mw-header-reveal) .mw-announcement:not(.mw-announcement-static) {
|
|
93
|
+
@include motion-safe {
|
|
94
|
+
animation-name: mw-announcement-reveal;
|
|
95
|
+
animation-timing-function: var(--mw-ease-out);
|
|
96
|
+
animation-fill-mode: both;
|
|
97
|
+
animation-timeline: scroll(root);
|
|
98
|
+
animation-range: 0 420px;
|
|
101
99
|
}
|
|
102
100
|
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Both cover the height of the pair, or the shorter traveller arrives first and
|
|
104
|
+
// the two come in as two things instead of one block.
|
|
105
|
+
.mw-header-reveal {
|
|
106
|
+
--mw-header-reveal-offset: var(--mw-header-height);
|
|
107
|
+
}
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
body:has(.mw-announcement:not(.mw-announcement-static)) .mw-header-reveal {
|
|
110
|
+
--mw-header-reveal-offset: calc(
|
|
111
|
+
var(--mw-header-height) + var(--mw-announcement-height)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// A focus ring on something off screen is WCAG 2.4.11, so focus brings the bar
|
|
116
|
+
// back. `!important` is the one author declaration that outranks a running
|
|
117
|
+
// animation; `animation-name: none` would strand the element on its last value
|
|
118
|
+
// until the next scroll. Keeps are left out - they are on screen already, and a
|
|
119
|
+
// click on the theme toggle would otherwise haul in the whole bar. `:focus` on a
|
|
120
|
+
// descendant and not `:focus-within`: that matches every wrapper too.
|
|
121
|
+
body:has(
|
|
122
|
+
.mw-header-reveal :focus:not(.mw-header-keep, .mw-header-keep *),
|
|
123
|
+
.mw-announcement:focus-within
|
|
124
|
+
) {
|
|
106
125
|
.mw-header-reveal {
|
|
107
|
-
|
|
126
|
+
top: var(--mw-header-inset) !important;
|
|
127
|
+
opacity: 1 !important;
|
|
108
128
|
}
|
|
109
129
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
);
|
|
130
|
+
.mw-header-keep {
|
|
131
|
+
translate: none !important;
|
|
132
|
+
box-shadow: none !important;
|
|
114
133
|
}
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// descendant and not `:focus-within`: that matches every wrapper too.
|
|
122
|
-
body:has(
|
|
123
|
-
.mw-header-reveal :focus:not(.mw-header-keep, .mw-header-keep *),
|
|
124
|
-
.mw-announcement:focus-within
|
|
125
|
-
) {
|
|
126
|
-
.mw-header-reveal {
|
|
127
|
-
top: var(--mw-header-inset) !important;
|
|
128
|
-
opacity: 1 !important;
|
|
129
|
-
}
|
|
135
|
+
.mw-announcement:not(.mw-announcement-static) {
|
|
136
|
+
transform: none !important;
|
|
137
|
+
opacity: 1 !important;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
130
140
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
// The panel hangs off the bar, so the bar has to be in place - and the burger,
|
|
142
|
+
// which is the X while the panel is out, has to stay on its own position.
|
|
143
|
+
//
|
|
144
|
+
// A class the script sets, not `:has(.mw-navbar.mw-active)`: the burger is the
|
|
145
|
+
// control that closes the panel and cannot wait for a selector re-match, which
|
|
146
|
+
// is where WebKit's invalidation has been unreliable.
|
|
147
|
+
//
|
|
148
|
+
// Zeroing the distance rather than overriding each position that reads it: four
|
|
149
|
+
// mechanisms carry the pair - `top` and `translate`, each once as a class and
|
|
150
|
+
// once as a keyframe - and all four resolve this one property. Overriding the
|
|
151
|
+
// values instead pits `!important` against a running animation, which WebKit
|
|
152
|
+
// does not reliably hand to the author.
|
|
153
|
+
body.mw-nav-open {
|
|
154
|
+
.mw-header-reveal {
|
|
155
|
+
// The ribbon variant above raises the same property from a `:has()`, which
|
|
156
|
+
// counts its argument and outranks this rule - author against author, which
|
|
157
|
+
// every engine resolves the same way, not author against an animation.
|
|
158
|
+
--mw-header-reveal-offset: 0px !important;
|
|
159
|
+
opacity: 1 !important;
|
|
160
|
+
}
|
|
135
161
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
162
|
+
// Not carried by the offset, and there is no picture behind an open panel.
|
|
163
|
+
.mw-header-keep {
|
|
164
|
+
box-shadow: none !important;
|
|
140
165
|
}
|
|
166
|
+
}
|
|
141
167
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
body.mw-nav-open {
|
|
155
|
-
.mw-header-reveal {
|
|
156
|
-
// The ribbon variant above raises the same property from a `:has()`, which
|
|
157
|
-
// counts its argument and outranks this rule - author against author, which
|
|
158
|
-
// every engine resolves the same way, not author against an animation.
|
|
159
|
-
--mw-header-reveal-offset: 0px !important;
|
|
160
|
-
opacity: 1 !important;
|
|
168
|
+
// No scroll timelines: the script toggles these classes instead (main.js).
|
|
169
|
+
// State and transition are separate because the script sets the state a frame
|
|
170
|
+
// earlier - together the bar would slide away in front of the reader.
|
|
171
|
+
.mw-header-reveal.mw-header-away {
|
|
172
|
+
@include motion-safe {
|
|
173
|
+
top: calc(var(--mw-header-inset) - var(--mw-header-reveal-offset));
|
|
174
|
+
opacity: 0;
|
|
175
|
+
|
|
176
|
+
// Visible through the slide, so its shadow would hang into the hero
|
|
177
|
+
&:has(.mw-header-keep) {
|
|
178
|
+
opacity: 1;
|
|
179
|
+
box-shadow: none;
|
|
161
180
|
}
|
|
162
181
|
|
|
163
|
-
// Not carried by the offset, and there is no picture behind an open panel.
|
|
164
182
|
.mw-header-keep {
|
|
165
|
-
|
|
183
|
+
translate: 0 var(--mw-header-reveal-offset);
|
|
184
|
+
box-shadow: $float-edge;
|
|
166
185
|
}
|
|
167
186
|
}
|
|
187
|
+
}
|
|
168
188
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
top
|
|
175
|
-
opacity: 0;
|
|
176
|
-
|
|
177
|
-
// Visible through the slide, so its shadow would hang into the hero
|
|
178
|
-
&:has(.mw-header-keep) {
|
|
179
|
-
opacity: 1;
|
|
180
|
-
box-shadow: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.mw-header-keep {
|
|
184
|
-
translate: 0 var(--mw-header-reveal-offset);
|
|
185
|
-
box-shadow: $float-edge;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
+
.mw-header-driven {
|
|
190
|
+
@include motion-safe {
|
|
191
|
+
// The shared list plus `top`: writing only `top` drops the bar's own.
|
|
192
|
+
transition:
|
|
193
|
+
var(--mw-transition),
|
|
194
|
+
top var(--mw-duration-slow) var(--mw-ease-out);
|
|
189
195
|
|
|
190
|
-
|
|
191
|
-
@include motion-safe {
|
|
192
|
-
// The shared list plus `top`: writing only `top` drops the bar's own.
|
|
196
|
+
.mw-header-keep {
|
|
193
197
|
transition:
|
|
194
|
-
var(--mw-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.mw-header-keep {
|
|
198
|
-
transition:
|
|
199
|
-
translate var(--mw-duration-slow) var(--mw-ease-out),
|
|
200
|
-
box-shadow var(--mw-duration-slow) var(--mw-ease-out);
|
|
201
|
-
}
|
|
198
|
+
translate var(--mw-duration-slow) var(--mw-ease-out),
|
|
199
|
+
box-shadow var(--mw-duration-slow) var(--mw-ease-out);
|
|
202
200
|
}
|
|
203
201
|
}
|
|
202
|
+
}
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
204
|
+
.mw-announcement.mw-announcement-away {
|
|
205
|
+
@include motion-safe {
|
|
206
|
+
transform: translateY(
|
|
207
|
+
calc(-1 * (var(--mw-header-height) + var(--mw-announcement-height)))
|
|
208
|
+
);
|
|
209
|
+
opacity: 0;
|
|
212
210
|
}
|
|
211
|
+
}
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
213
|
+
.mw-announcement-driven {
|
|
214
|
+
@include motion-safe {
|
|
215
|
+
transition:
|
|
216
|
+
transform var(--mw-duration-slow) var(--mw-ease-out),
|
|
217
|
+
opacity var(--mw-duration-slow) var(--mw-ease-out);
|
|
220
218
|
}
|
|
221
219
|
}
|
|
222
220
|
|
|
@@ -7,17 +7,6 @@ $_control-size: 40px;
|
|
|
7
7
|
// frame on a box this short
|
|
8
8
|
$_mark-round: radius('lg');
|
|
9
9
|
|
|
10
|
-
// Where the island sits: percentages on a fixed box are the viewport's, so this
|
|
11
|
-
// puts its content box on the page container, $header-island-reach wider on
|
|
12
|
-
// either side. Logo and links stay put while the bar docks around them.
|
|
13
|
-
$_island-edge: max(
|
|
14
|
-
calc(var(--mw-header-inset) - #{$header-island-reach}),
|
|
15
|
-
calc(
|
|
16
|
-
(100% - var(--mw-container-width)) / 2 - var(--mw-header-pad) -
|
|
17
|
-
#{$header-island-reach}
|
|
18
|
-
)
|
|
19
|
-
);
|
|
20
|
-
|
|
21
10
|
@mixin navbar-mark-shape {
|
|
22
11
|
border-radius: radius-sharp() $_mark-round radius-sharp() $_mark-round;
|
|
23
12
|
|
|
@@ -51,16 +40,12 @@ $_island-edge: max(
|
|
|
51
40
|
.mw-header {
|
|
52
41
|
position: fixed;
|
|
53
42
|
top: var(--mw-header-inset);
|
|
54
|
-
left:
|
|
55
|
-
right:
|
|
43
|
+
left: island-edge();
|
|
44
|
+
right: island-edge();
|
|
56
45
|
z-index: z-index('header');
|
|
57
46
|
// Dark in both themes, fixed light included, where no light-dark() flips
|
|
58
47
|
--mw-focus-ring-color: var(--mw-header-navbar-list-active-color);
|
|
59
|
-
|
|
60
|
-
backdrop-filter: blur(20px) saturate(1.6);
|
|
61
|
-
border: 1px solid var(--mw-header-island-border);
|
|
62
|
-
border-radius: radius('xl');
|
|
63
|
-
box-shadow: var(--mw-elevation-4);
|
|
48
|
+
@include island-surface;
|
|
64
49
|
// The height comes from min-height; the padding only has to let a control
|
|
65
50
|
// grown to its 2.75rem touch floor fit inside it
|
|
66
51
|
padding: spacing('2') var(--mw-header-pad);
|
|
@@ -79,7 +64,7 @@ $_island-edge: max(
|
|
|
79
64
|
min-width: 0;
|
|
80
65
|
}
|
|
81
66
|
|
|
82
|
-
// The island already sits on the page container, see
|
|
67
|
+
// The island already sits on the page container, see island-edge()
|
|
83
68
|
.mw-container {
|
|
84
69
|
width: 100%;
|
|
85
70
|
display: flex;
|
|
@@ -263,7 +248,7 @@ $_island-edge: max(
|
|
|
263
248
|
}
|
|
264
249
|
}
|
|
265
250
|
|
|
266
|
-
// The panel hangs under the island as
|
|
251
|
+
// The panel hangs under the island as its lower half, not beside it. A
|
|
267
252
|
// drawer pinned to the right edge runs into the logo: 250px of panel against a
|
|
268
253
|
// 180px logo leaves 60px of overlap on a phone, and .mw-logo sits higher in the
|
|
269
254
|
// header's stacking context, so the logo prints on top of the menu. Under the
|
|
@@ -276,7 +261,8 @@ $_island-edge: max(
|
|
|
276
261
|
// Bound to it, the card simply travels with it.
|
|
277
262
|
@mixin navbar-top-sheet {
|
|
278
263
|
position: absolute;
|
|
279
|
-
|
|
264
|
+
// On the island's bottom border, so the two share one seam line
|
|
265
|
+
top: 100%;
|
|
280
266
|
// Out to the island's outer edge, past its border
|
|
281
267
|
left: -1px;
|
|
282
268
|
right: -1px;
|
|
@@ -286,7 +272,7 @@ $_island-edge: max(
|
|
|
286
272
|
// header, so a translucent card would show the page through it unblurred
|
|
287
273
|
background: var(--mw-header-background);
|
|
288
274
|
border: 1px solid var(--mw-header-island-border);
|
|
289
|
-
border-radius: radius('xl');
|
|
275
|
+
border-radius: 0 0 radius('xl') radius('xl');
|
|
290
276
|
z-index: z-index('header-panel');
|
|
291
277
|
|
|
292
278
|
// Rolled out from under the island rather than slid in. A slide needs a
|
|
@@ -309,10 +295,9 @@ $_island-edge: max(
|
|
|
309
295
|
// width of its longest label and the rows end a third of the way across
|
|
310
296
|
width: 100%;
|
|
311
297
|
// Tall menus stay reachable without the card growing past the screen: the
|
|
312
|
-
// room under the island, less the
|
|
298
|
+
// room under the island, less the inset below
|
|
313
299
|
max-height: calc(
|
|
314
|
-
100dvh - var(--mw-header-height) -
|
|
315
|
-
#{spacing('3')} - var(--mw-header-inset)
|
|
300
|
+
100dvh - var(--mw-header-height) - var(--mw-header-inset)
|
|
316
301
|
);
|
|
317
302
|
overflow-y: auto;
|
|
318
303
|
// Keeps a rubber-band scroll inside the panel from moving the page
|
|
@@ -430,6 +415,11 @@ $_island-edge: max(
|
|
|
430
415
|
z-index: z-index('header-scrim');
|
|
431
416
|
animation: mw-navbar-scrim-in var(--mw-duration-base) var(--mw-ease-out);
|
|
432
417
|
}
|
|
418
|
+
|
|
419
|
+
.mw-header {
|
|
420
|
+
border-bottom-left-radius: 0;
|
|
421
|
+
border-bottom-right-radius: 0;
|
|
422
|
+
}
|
|
433
423
|
}
|
|
434
424
|
|
|
435
425
|
@keyframes mw-navbar-item-in {
|
|
@@ -600,8 +590,8 @@ $_island-edge: max(
|
|
|
600
590
|
// rounding seam opens between them.
|
|
601
591
|
.mw-announcement:not(.mw-announcement-static) {
|
|
602
592
|
top: calc(var(--mw-header-height) - 1px);
|
|
603
|
-
left:
|
|
604
|
-
right:
|
|
593
|
+
left: island-edge();
|
|
594
|
+
right: island-edge();
|
|
605
595
|
border: 1px solid var(--mw-header-island-border);
|
|
606
596
|
border-top: none;
|
|
607
597
|
border-radius: 0 0 radius('xl') radius('xl');
|
|
@@ -614,14 +604,6 @@ $_island-edge: max(
|
|
|
614
604
|
border-bottom-left-radius: 0;
|
|
615
605
|
border-bottom-right-radius: 0;
|
|
616
606
|
box-shadow: none;
|
|
617
|
-
|
|
618
|
-
// The open menu covers the ribbon instead of hanging below it - a phone
|
|
619
|
-
// would otherwise lose the height of both before the first link
|
|
620
|
-
.mw-navbar {
|
|
621
|
-
top: 100%;
|
|
622
|
-
border-top-left-radius: 0;
|
|
623
|
-
border-top-right-radius: 0;
|
|
624
|
-
}
|
|
625
607
|
}
|
|
626
608
|
|
|
627
609
|
// The open menu covers it, but through the scrim's blur its edge would still
|
|
@@ -630,14 +612,16 @@ $_island-edge: max(
|
|
|
630
612
|
visibility: hidden;
|
|
631
613
|
}
|
|
632
614
|
|
|
633
|
-
// The flat bar
|
|
634
|
-
//
|
|
635
|
-
//
|
|
615
|
+
// The flat bar: full width, fixed at the top, no island and no docking (the
|
|
616
|
+
// dock in _header-reveal.scss is off under the same class). mw-header-reveal
|
|
617
|
+
// still rides it in. On <html> like the other site-wide variants.
|
|
636
618
|
:root.mw-header-bar {
|
|
637
619
|
--mw-header-height: #{$header-bar-height};
|
|
620
|
+
// Not `top: 0` on the header: that would outrank the reveal's resting and
|
|
621
|
+
// away positions, which are both counted from the inset
|
|
622
|
+
--mw-header-inset: 0px;
|
|
638
623
|
|
|
639
624
|
.mw-header {
|
|
640
|
-
top: 0;
|
|
641
625
|
left: 0;
|
|
642
626
|
right: 0;
|
|
643
627
|
min-height: var(--mw-header-height);
|