maverick-wave 5.21.0 → 5.23.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 +11 -4
- package/.claude/skills/mw-maverick-wave/references/components.md +123 -11
- package/.claude/skills/mw-maverick-wave/references/layout.md +10 -0
- package/.claude/skills/mw-maverick-wave/references/theming.md +5 -0
- package/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/index.html +18 -0
- package/maverick-wave.min.css +3 -3
- package/package.json +3 -2
- package/release.sh +6 -0
- package/scripts/verify.js +16 -5
- package/src/partials/contact-container.html +85 -0
- package/src/partials/documentation-container.html +3 -0
- package/src/partials/leader-row-container.html +139 -0
- package/src/partials/lunch-menu-container.html +341 -0
- package/src/scss/abstracts/_functions.scss +38 -7
- package/src/scss/components/_contact.scss +29 -0
- package/src/scss/components/_date-stamp.scss +141 -0
- package/src/scss/components/_index.scss +3 -0
- package/src/scss/components/_leader-row.scss +89 -0
- package/src/scss/components/_pricing.scss +4 -0
- package/src/scss/layout/_grid.scss +6 -0
- package/src/scss/layout/_header-reveal.scss +43 -78
- package/src/scss/layout/_main.scss +16 -0
|
@@ -23,17 +23,48 @@
|
|
|
23
23
|
// Only the slope is in vw: a size in vw alone ignores the reader's font size and
|
|
24
24
|
// stops responding to zoom, the rem term is what keeps that alive.
|
|
25
25
|
@function fluid($min, $max, $from: 'xs', $to: 'md') {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
@return _ramp(
|
|
27
|
+
_to-rem($min, 'min'),
|
|
28
|
+
_to-rem($max, 'max'),
|
|
29
|
+
math.div(map.get(v.$breakpoints, $from), 16px),
|
|
30
|
+
math.div(map.get(v.$breakpoints, $to), 16px),
|
|
31
|
+
vw
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The same ramp against the container instead of the viewport. For anything
|
|
36
|
+
// inside a `@container` block: sizing there off vw means a 440px card on a
|
|
37
|
+
// 1400px screen takes the width a 900px card was measured for, which is the
|
|
38
|
+
// mismatch container queries exist to remove. The endpoints are container
|
|
39
|
+
// widths rather than breakpoint names, because a container has no breakpoints.
|
|
40
|
+
@function fluid-container($min, $max, $from, $to) {
|
|
41
|
+
$from-rem: _to-rem($from, 'from');
|
|
42
|
+
$to-rem: _to-rem($to, 'to');
|
|
43
|
+
|
|
44
|
+
@if $from-rem == $to-rem {
|
|
45
|
+
@error "fluid-container(): $from and $to must differ, both are #{$from}.";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@return _ramp(
|
|
49
|
+
_to-rem($min, 'min'),
|
|
50
|
+
_to-rem($max, 'max'),
|
|
51
|
+
$from-rem,
|
|
52
|
+
$to-rem,
|
|
53
|
+
cqi
|
|
54
|
+
);
|
|
55
|
+
}
|
|
30
56
|
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
// Both of the above draw the same line through (from, min) and (to, max) and
|
|
58
|
+
// disagree only on what measures the axis. Shared so a change to the rem term -
|
|
59
|
+
// the part that keeps the size responding to zoom - cannot reach one and miss
|
|
60
|
+
// the other.
|
|
61
|
+
@function _ramp($min-rem, $max-rem, $from-rem, $to-rem, $unit) {
|
|
62
|
+
$slope: math.div($max-rem - $min-rem, $to-rem - $from-rem);
|
|
63
|
+
$intercept: $min-rem - $slope * $from-rem;
|
|
33
64
|
|
|
34
65
|
@return clamp(
|
|
35
66
|
#{$min-rem}rem,
|
|
36
|
-
#{_round4($intercept)}rem + #{_round4($slope * 100)}
|
|
67
|
+
#{_round4($intercept)}rem + #{_round4($slope * 100)}#{$unit},
|
|
37
68
|
#{$max-rem}rem
|
|
38
69
|
);
|
|
39
70
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
@layer mw.components {
|
|
4
|
+
// The block that says how to reach someone - postal address, phone, mail.
|
|
5
|
+
// Belongs on an <address>, which is what the one font-style is for: the
|
|
6
|
+
// element is italic by default, and nobody sets an address in italics.
|
|
7
|
+
.mw-contact {
|
|
8
|
+
font-style: normal;
|
|
9
|
+
display: grid;
|
|
10
|
+
gap: spacing('3');
|
|
11
|
+
|
|
12
|
+
> * {
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&-row {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-wrap: wrap;
|
|
19
|
+
gap: spacing('1') spacing('4');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Sized rather than auto, so the values line up under each other however
|
|
23
|
+
// long the words in front of them are
|
|
24
|
+
&-label {
|
|
25
|
+
flex: 0 0 5rem;
|
|
26
|
+
color: var(--mw-text-muted-color);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
@layer mw.components {
|
|
4
|
+
// Every fluid value here runs the same ramp: from the width the two-column
|
|
5
|
+
// layout starts at to the width a day card stops growing at. Against the
|
|
6
|
+
// container, not the viewport - the layout switch below is a container query,
|
|
7
|
+
// and sizing off vw would hand a narrow card in a wide window the
|
|
8
|
+
// measurements of a wide one.
|
|
9
|
+
@function _stamp-ramp($min, $max) {
|
|
10
|
+
@return fluid-container($min, $max, $mw-card-wide, 900px);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Named because the today fill cancels the paddings to reach the card border,
|
|
14
|
+
// and the price variant repeats the track list - two copies would drift apart.
|
|
15
|
+
$_mw-date-stamp-pad-top: _stamp-ramp(spacing('4'), spacing('6'));
|
|
16
|
+
$_mw-date-stamp-pad-bottom: _stamp-ramp(spacing('3'), spacing('5'));
|
|
17
|
+
$_mw-date-stamp-day-col: _stamp-ramp(4.25rem, 7rem);
|
|
18
|
+
$_mw-date-stamp-gap: _stamp-ramp(spacing('4'), spacing('7'));
|
|
19
|
+
$_mw-date-stamp-day-size: _stamp-ramp('xl', '3xl');
|
|
20
|
+
|
|
21
|
+
// The date on a dated card - an event, a changelog entry, a day on a menu.
|
|
22
|
+
// Narrow, it is a line above the title. Wide, it turns into a column beside
|
|
23
|
+
// the content, which is the only thing that fills the right half of a card
|
|
24
|
+
// that has a whole row to itself.
|
|
25
|
+
.mw-date-stamp {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: baseline;
|
|
28
|
+
gap: spacing('3');
|
|
29
|
+
margin-bottom: spacing('4');
|
|
30
|
+
color: var(--mw-text-muted-color);
|
|
31
|
+
|
|
32
|
+
&-day {
|
|
33
|
+
font-size: font-size('sm');
|
|
34
|
+
font-weight: font-weight('bold');
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
letter-spacing: 0.08em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&-date {
|
|
40
|
+
font-size: font-size('sm');
|
|
41
|
+
font-variant-numeric: tabular-nums;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A fill, not a text colour: the price on the same card is already primary,
|
|
45
|
+
// and two blues compete where one would point. The padding is pulled back
|
|
46
|
+
// out again so the fill bleeds around the line instead of indenting it.
|
|
47
|
+
&-today {
|
|
48
|
+
padding: spacing('2') spacing('3');
|
|
49
|
+
margin-inline: spacing('-3');
|
|
50
|
+
margin-block-start: spacing('-2');
|
|
51
|
+
border-radius: radius('sm') radius('lg') radius('sm') radius('lg');
|
|
52
|
+
background: var(--mw-primary-color);
|
|
53
|
+
color: var(--mw-primary-accent-text-color);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// No box of its own until there are two columns to be one of, so the card body
|
|
58
|
+
// keeps seeing the title, the text and the price as its own children - which
|
|
59
|
+
// is what mw-offer and mw-card-body's last-child rule both select on
|
|
60
|
+
.mw-date-stamp-body {
|
|
61
|
+
display: contents;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@container mw-card (width >= #{$mw-card-wide}) {
|
|
65
|
+
// The rule sits here and not in _cards.scss because the stamp is the guest -
|
|
66
|
+
// the card body's own single column is right for everything that is not one
|
|
67
|
+
.mw-card-body:has(> .mw-date-stamp) {
|
|
68
|
+
display: grid;
|
|
69
|
+
grid-template-columns: $_mw-date-stamp-day-col minmax(0, 1fr);
|
|
70
|
+
// On the same curve as the column, so the dish keeps the width the day
|
|
71
|
+
// and the price give up rather than losing it again to the gutters
|
|
72
|
+
column-gap: $_mw-date-stamp-gap;
|
|
73
|
+
padding-block: $_mw-date-stamp-pad-top $_mw-date-stamp-pad-bottom;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// A third track, for the one price that covers the whole day - it is what the
|
|
77
|
+
// right half of a wide card is otherwise spent on. Only when there is a
|
|
78
|
+
// price: with a dish per row it travels with its dish, and an empty track
|
|
79
|
+
// would still charge the card a gap.
|
|
80
|
+
.mw-card-body:has(> .mw-date-stamp):has(> .mw-price) {
|
|
81
|
+
grid-template-columns: $_mw-date-stamp-day-col minmax(0, 1fr) auto;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The row is left to stretch to the body, so a today fill can span the whole
|
|
85
|
+
// card - which means each column has to centre its own content instead
|
|
86
|
+
.mw-date-stamp-body {
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The card body zeroes its own last child, but the wrapper hides ours from
|
|
93
|
+
// that rule - without this a trailing tag row adds a paragraph margin
|
|
94
|
+
.mw-date-stamp-body > *:last-child {
|
|
95
|
+
margin-bottom: spacing('0');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Beside the dish it prices, not under the description that follows it. The
|
|
99
|
+
// second selector only exists to outrank mw-offer, which pushes a price to
|
|
100
|
+
// the bottom of the card and ties with the first on specificity alone.
|
|
101
|
+
.mw-card-body:has(> .mw-date-stamp) > .mw-price,
|
|
102
|
+
.mw-offer > .mw-card-body:has(> .mw-date-stamp) > .mw-price {
|
|
103
|
+
align-self: center;
|
|
104
|
+
justify-self: end;
|
|
105
|
+
margin-top: spacing('0');
|
|
106
|
+
padding-top: spacing('0');
|
|
107
|
+
text-align: right;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.mw-date-stamp {
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
align-items: flex-start;
|
|
113
|
+
// Stretched to the row, so the day and the date centre against the title
|
|
114
|
+
// and description beside them rather than hanging off the first line
|
|
115
|
+
justify-content: center;
|
|
116
|
+
gap: spacing('2');
|
|
117
|
+
margin-bottom: spacing('0');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Top edge of the card to its bottom one: the body's padding is handed back
|
|
121
|
+
// so the fill reaches the border. Only the two left corners are rounded, and
|
|
122
|
+
// to the card's own arc less the 1px it sits inside - the right edge is an
|
|
123
|
+
// inside edge and a cut is what reads as one.
|
|
124
|
+
.mw-date-stamp-today {
|
|
125
|
+
margin-block: calc(-1 * #{$_mw-date-stamp-pad-top})
|
|
126
|
+
calc(-1 * #{$_mw-date-stamp-pad-bottom});
|
|
127
|
+
margin-inline: calc(-1 * #{spacing('5')}) 0;
|
|
128
|
+
padding-block: $_mw-date-stamp-pad-top $_mw-date-stamp-pad-bottom;
|
|
129
|
+
padding-inline: spacing('5');
|
|
130
|
+
border-radius: calc(#{radius-sharp()} - 1px) 0 0
|
|
131
|
+
calc(#{radius('xl')} - 1px);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.mw-date-stamp-day {
|
|
135
|
+
// Tracks the column it sits in, or it would outgrow it on the way down
|
|
136
|
+
font-size: $_mw-date-stamp-day-size;
|
|
137
|
+
line-height: 1;
|
|
138
|
+
letter-spacing: 0.02em;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
@forward 'cards';
|
|
12
12
|
@forward 'code';
|
|
13
13
|
@forward 'coming-soon';
|
|
14
|
+
@forward 'contact';
|
|
14
15
|
@forward 'content-slider';
|
|
16
|
+
@forward 'date-stamp';
|
|
15
17
|
@forward 'divider';
|
|
16
18
|
@forward 'dropdown';
|
|
17
19
|
@forward 'empty-state';
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
@forward 'kanban';
|
|
22
24
|
@forward 'kbd';
|
|
23
25
|
@forward 'lang-switch';
|
|
26
|
+
@forward 'leader-row';
|
|
24
27
|
@forward 'links';
|
|
25
28
|
@forward 'lists';
|
|
26
29
|
@forward 'localhost-indicator';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
@use '../abstracts' as *;
|
|
2
|
+
|
|
3
|
+
@layer mw.components {
|
|
4
|
+
// Dots on the baseline, not under the descenders; em so they follow font-size
|
|
5
|
+
$_mw-leader-baseline-lift: 0.34em;
|
|
6
|
+
|
|
7
|
+
// A label on the left, its value on the right, a dotted line bridging the
|
|
8
|
+
// gap. The line is the whole point: over a wide row the eye loses track of
|
|
9
|
+
// which value belongs to which label, which is why every printed menu, index
|
|
10
|
+
// and invoice draws one.
|
|
11
|
+
//
|
|
12
|
+
// The leader is the row's own ::before rather than an element in the markup.
|
|
13
|
+
// A generated box on a flex container is a flex item, so "order" can place it
|
|
14
|
+
// between the two spans - and generated content stays out of the
|
|
15
|
+
// accessibility tree, where a filler element would need aria-hidden.
|
|
16
|
+
.mw-leader-row {
|
|
17
|
+
--mw-leader-row-style: dotted;
|
|
18
|
+
--mw-leader-row-width: 2px;
|
|
19
|
+
--mw-leader-row-color: var(--mw-border);
|
|
20
|
+
|
|
21
|
+
display: flex;
|
|
22
|
+
// The values never shrink, so without a wrap the label is the only thing
|
|
23
|
+
// left to give - and it gives until it is one letter per line
|
|
24
|
+
flex-wrap: wrap;
|
|
25
|
+
align-items: flex-end;
|
|
26
|
+
gap: spacing('3');
|
|
27
|
+
margin: 0;
|
|
28
|
+
|
|
29
|
+
&::before {
|
|
30
|
+
content: '';
|
|
31
|
+
order: 1;
|
|
32
|
+
flex: 1 1 auto;
|
|
33
|
+
// Keeps a stub of line visible when label and value nearly fill the row
|
|
34
|
+
min-width: spacing('6');
|
|
35
|
+
border-bottom: var(--mw-leader-row-width) var(--mw-leader-row-style)
|
|
36
|
+
var(--mw-leader-row-color);
|
|
37
|
+
margin-bottom: $_mw-leader-baseline-lift;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&-label {
|
|
41
|
+
order: 0;
|
|
42
|
+
// No min-width override: the default floor is the longest word, so the
|
|
43
|
+
// label wraps between words and stops there instead of going to nothing.
|
|
44
|
+
// Past that floor the row wraps rather than the label shrinking further.
|
|
45
|
+
//
|
|
46
|
+
// The row also works as a dl group, and a dd carries 40px of browser margin
|
|
47
|
+
margin: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&-value {
|
|
51
|
+
order: 2;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
margin: 0;
|
|
54
|
+
// A price or a page number must not break, and a column of them only
|
|
55
|
+
// lines up with tabular figures.
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
font-variant-numeric: tabular-nums;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Two values in one row read as a single long range with only a gap between
|
|
61
|
+
// them. The mark takes the leader's colour, so retuning a row moves both.
|
|
62
|
+
// It hangs off the first of the pair, not the second: once the row wraps, a
|
|
63
|
+
// trailing mark reads as "continues below" and a leading one as a slip.
|
|
64
|
+
&-value:has(+ &-value)::after {
|
|
65
|
+
content: '|';
|
|
66
|
+
margin-left: spacing('3');
|
|
67
|
+
color: var(--mw-leader-row-color);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&-dashed {
|
|
71
|
+
--mw-leader-row-style: dashed;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&-solid {
|
|
75
|
+
--mw-leader-row-style: solid;
|
|
76
|
+
--mw-leader-row-width: 1px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&-top {
|
|
80
|
+
align-items: flex-start;
|
|
81
|
+
|
|
82
|
+
&::before {
|
|
83
|
+
// flex-start pins the leader to the top edge, so it drops to the first
|
|
84
|
+
// line's baseline itself instead of being lifted off the last one
|
|
85
|
+
margin: calc(1lh - #{$_mw-leader-baseline-lift}) 0 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -159,6 +159,12 @@ $_column-steps-lg: (
|
|
|
159
159
|
grid-template-columns: repeat(auto-fill, minmax(min(350px, 100%), 1fr));
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// Every row as tall as the tallest rather than as tall as its own contents.
|
|
163
|
+
// A modifier, not a grid of its own - it needs one of the above to size.
|
|
164
|
+
.mw-grid-even {
|
|
165
|
+
grid-auto-rows: 1fr;
|
|
166
|
+
}
|
|
167
|
+
|
|
162
168
|
.mw-grid-lg {
|
|
163
169
|
@extend %grid-base-lg;
|
|
164
170
|
}
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// or the ring would still be there in the arrived bar, on one control and not
|
|
5
|
-
// its neighbour. Fixed values and not the elevation ramp, the same call the
|
|
6
|
-
// parallax shadow makes: this lands on the picture, not on a theme surface, and
|
|
7
|
-
// the light theme cuts its shadows for paper.
|
|
1
|
+
// Carried by the float and not by the class, or the ring would still be there in
|
|
2
|
+
// the arrived bar. Fixed values and not the elevation ramp: this lands on a
|
|
3
|
+
// picture, not on a theme surface, and the light theme cuts its shadows for paper.
|
|
8
4
|
$float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
9
5
|
|
|
10
6
|
@layer mw.layout {
|
|
11
|
-
//
|
|
12
|
-
// it: the bar starts above the screen and rides in over the first stretch of
|
|
13
|
-
// scroll. Inside the @supports, so a browser without scroll timelines gets a
|
|
14
|
-
// normal header rather than none at all.
|
|
15
|
-
//
|
|
7
|
+
// The bar starts above the screen and rides in over the first stretch of scroll.
|
|
16
8
|
// Its own file, and not next to .mw-header: that one uses the `animation`
|
|
17
9
|
// shorthand, which a consumer bundler folds together with a timeline into a
|
|
18
10
|
// declaration no browser accepts.
|
|
@@ -26,12 +18,8 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
26
18
|
animation-range: 0 420px;
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// distance is the bar's own, so it waits at the position it will hold in
|
|
32
|
-
// the arrived bar - nothing jumps at the end, the bar just closes in
|
|
33
|
-
// behind it. For a login or burger button that has to be reachable while
|
|
34
|
-
// the hero is still untouched.
|
|
21
|
+
// The bar travels, a keep element travels the same distance back and stays
|
|
22
|
+
// put - for a burger that has to be reachable while the hero is untouched.
|
|
35
23
|
.mw-header-reveal .mw-header-keep {
|
|
36
24
|
animation-name: mw-header-keep;
|
|
37
25
|
animation-timing-function: var(--mw-ease-out);
|
|
@@ -40,18 +28,14 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
40
28
|
animation-range: 0 420px;
|
|
41
29
|
}
|
|
42
30
|
|
|
43
|
-
// A group's opacity cannot be taken back by a child, so the fade
|
|
44
|
-
//
|
|
45
|
-
// the bar is off screen for that stretch anyway, the fade only softened
|
|
46
|
-
// the last few pixels of the arrival.
|
|
31
|
+
// A group's opacity cannot be taken back by a child, so the fade goes as
|
|
32
|
+
// soon as something is meant to stay visible through it.
|
|
47
33
|
.mw-header-reveal:has(.mw-header-keep) {
|
|
48
34
|
animation-name: mw-header-slide;
|
|
49
35
|
}
|
|
50
36
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// hero. No class of its own: a ribbon on a page with a revealing header
|
|
54
|
-
// has no other sensible behaviour.
|
|
37
|
+
// Glued under the bar, so it leaves and arrives with it. No class of its
|
|
38
|
+
// own: a ribbon under a revealing header has no other sensible behaviour.
|
|
55
39
|
body:has(.mw-header-reveal)
|
|
56
40
|
.mw-announcement:not(.mw-announcement-static) {
|
|
57
41
|
animation-name: mw-announcement-reveal;
|
|
@@ -63,9 +47,8 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
63
47
|
}
|
|
64
48
|
}
|
|
65
49
|
|
|
66
|
-
// Both cover the height of the pair, or the shorter traveller arrives first
|
|
67
|
-
//
|
|
68
|
-
// that height is just the bar's.
|
|
50
|
+
// Both cover the height of the pair, or the shorter traveller arrives first and
|
|
51
|
+
// the two come in as two things instead of one block.
|
|
69
52
|
.mw-header-reveal {
|
|
70
53
|
--mw-header-reveal-offset: var(--mw-header-height);
|
|
71
54
|
}
|
|
@@ -76,18 +59,12 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
76
59
|
);
|
|
77
60
|
}
|
|
78
61
|
|
|
79
|
-
// A
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
// A keep element is on screen already and needs no rescue, so it is left out -
|
|
87
|
-
// otherwise a click on the theme toggle, which focuses it, would haul in the
|
|
88
|
-
// whole bar. It takes `:focus` on a descendant and not `:focus-within` on the
|
|
89
|
-
// header: within also matches every wrapper around the focused control, and no
|
|
90
|
-
// `:not()` on those can tell which one holds the keep.
|
|
62
|
+
// A focus ring on something off screen is WCAG 2.4.11, so focus brings the bar
|
|
63
|
+
// back. `!important` is the one author declaration that outranks a running
|
|
64
|
+
// animation; `animation-name: none` would strand the element on its last value
|
|
65
|
+
// until the next scroll. Keeps are left out - they are on screen already, and a
|
|
66
|
+
// click on the theme toggle would otherwise haul in the whole bar. `:focus` on a
|
|
67
|
+
// descendant and not `:focus-within`: that matches every wrapper too.
|
|
91
68
|
body:has(
|
|
92
69
|
.mw-header-reveal :focus:not(.mw-header-keep, .mw-header-keep *),
|
|
93
70
|
.mw-announcement:focus-within
|
|
@@ -108,43 +85,36 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
108
85
|
}
|
|
109
86
|
}
|
|
110
87
|
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// which is the X from the moment the panel is out, has to stay on the burger's
|
|
114
|
-
// own position rather than a header height below it.
|
|
88
|
+
// The panel hangs off the bar, so the bar has to be in place - and the burger,
|
|
89
|
+
// which is the X while the panel is out, has to stay on its own position.
|
|
115
90
|
//
|
|
116
|
-
// A class the script sets,
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
// invalidation has been unreliable. The burger is the control that closes the
|
|
120
|
-
// panel - it cannot be the thing that waits for a repaint.
|
|
91
|
+
// A class the script sets, not `:has(.mw-navbar.open)`: the burger is the
|
|
92
|
+
// control that closes the panel and cannot wait for a selector re-match, which
|
|
93
|
+
// is where WebKit's invalidation has been unreliable.
|
|
121
94
|
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
// `!important` against a running animation, and WebKit does not reliably hand
|
|
128
|
-
// that win to the author: the bar came in, the burger kept its offset, and the
|
|
129
|
-
// X sat a header height below the panel it belongs to.
|
|
95
|
+
// Zeroing the distance rather than overriding each position that reads it: four
|
|
96
|
+
// mechanisms carry the pair - `top` and `translate`, each once as a class and
|
|
97
|
+
// once as a keyframe - and all four resolve this one property. Overriding the
|
|
98
|
+
// values instead pits `!important` against a running animation, which WebKit
|
|
99
|
+
// does not reliably hand to the author.
|
|
130
100
|
body.mw-nav-open {
|
|
131
101
|
.mw-header-reveal {
|
|
132
|
-
|
|
102
|
+
// The ribbon variant above raises the same property from a `:has()`, which
|
|
103
|
+
// counts its argument and outranks this rule - author against author, which
|
|
104
|
+
// every engine resolves the same way, not author against an animation.
|
|
105
|
+
--mw-header-reveal-offset: 0px !important;
|
|
133
106
|
opacity: 1 !important;
|
|
134
107
|
}
|
|
135
108
|
|
|
136
|
-
// Not carried by the offset
|
|
137
|
-
// floating over a picture, and there is no picture behind an open panel.
|
|
109
|
+
// Not carried by the offset, and there is no picture behind an open panel.
|
|
138
110
|
.mw-header-keep {
|
|
139
111
|
box-shadow: none !important;
|
|
140
112
|
}
|
|
141
113
|
}
|
|
142
114
|
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// the script sets the state a frame earlier: together they would send the
|
|
147
|
-
// bar sliding away in front of the reader instead of having it gone already.
|
|
115
|
+
// No scroll timelines: the script toggles these classes instead (main.js).
|
|
116
|
+
// State and transition are separate because the script sets the state a frame
|
|
117
|
+
// earlier - together the bar would slide away in front of the reader.
|
|
148
118
|
@media (prefers-reduced-motion: no-preference) {
|
|
149
119
|
.mw-header-reveal.mw-header-away {
|
|
150
120
|
top: calc(-1 * var(--mw-header-reveal-offset));
|
|
@@ -161,8 +131,7 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
161
131
|
}
|
|
162
132
|
|
|
163
133
|
.mw-header-driven {
|
|
164
|
-
// The shared list plus `top
|
|
165
|
-
// here would drop the header's own colour and shadow transitions.
|
|
134
|
+
// The shared list plus `top`: writing only `top` drops the bar's own.
|
|
166
135
|
transition:
|
|
167
136
|
var(--mw-transition),
|
|
168
137
|
top var(--mw-duration-slow) var(--mw-ease-out);
|
|
@@ -188,12 +157,10 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
188
157
|
}
|
|
189
158
|
}
|
|
190
159
|
|
|
191
|
-
// `top` and not a transform:
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
// starts past 0 leaves the bar in its resting state below that point - so the
|
|
196
|
-
// header would be visible for exactly the stretch it should stay away for.
|
|
160
|
+
// `top` and not a transform: that would make the header the containing block
|
|
161
|
+
// for anything fixed inside it, at every value. The wait sits in the keyframes
|
|
162
|
+
// and not in the range - a range starting past 0 leaves the bar resting, and
|
|
163
|
+
// visible, for exactly the stretch it should stay away for.
|
|
197
164
|
@keyframes mw-header-reveal {
|
|
198
165
|
0%,
|
|
199
166
|
30% {
|
|
@@ -219,10 +186,8 @@ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
|
|
|
219
186
|
}
|
|
220
187
|
}
|
|
221
188
|
|
|
222
|
-
// `translate` and not `transform`: the burger puts its hover lift
|
|
223
|
-
//
|
|
224
|
-
// - it would hold the button flat for the rest of the page. The standalone
|
|
225
|
-
// property stacks with the shorthand instead of replacing it.
|
|
189
|
+
// `translate` and not `transform`: the burger puts its hover lift on transform,
|
|
190
|
+
// and a running animation would hold it flat for the rest of the page.
|
|
226
191
|
@keyframes mw-header-keep {
|
|
227
192
|
0%,
|
|
228
193
|
30% {
|
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
flex-direction: column;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// Set in from the container it sits in, and giving that back a step at a time
|
|
14
|
+
// as the screen shrinks. For a column of full-width cards, which at 1200px
|
|
15
|
+
// leaves more empty card than content.
|
|
16
|
+
.mw-container-narrow {
|
|
17
|
+
width: 80%;
|
|
18
|
+
margin-inline: auto;
|
|
19
|
+
|
|
20
|
+
@include media-down('lg') {
|
|
21
|
+
width: 90%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@include media-down('md') {
|
|
25
|
+
width: 100%;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
13
29
|
.mw-container {
|
|
14
30
|
width: var(--mw-container-width);
|
|
15
31
|
margin: spacing('0') auto;
|