maverick-wave 5.16.0 → 5.19.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.
@@ -1,3 +1,12 @@
1
+ // Floating free over a picture, a control has to carry its own edge - the burger
2
+ // and the login button bring a primary surface, the theme toggle is a dark pill
3
+ // that a dark photo swallows. It travels with the float and not with the class,
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.
8
+ $float-edge: (0 0 0 1px rgb(255 255 255 / 0.22), 0 4px 14px rgb(0 0 0 / 0.4));
9
+
1
10
  @layer mw.layout {
2
11
  // For a page that opens on a full-bleed picture and wants nothing on top of
3
12
  // it: the bar starts above the screen and rides in over the first stretch of
@@ -17,6 +26,28 @@
17
26
  animation-range: 0 420px;
18
27
  }
19
28
 
29
+ // Anything carrying mw-header-keep sits the ride out: the bar travels,
30
+ // the element travels the same distance back and stays where it is. The
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.
35
+ .mw-header-reveal .mw-header-keep {
36
+ animation-name: mw-header-keep;
37
+ animation-timing-function: var(--mw-ease-out);
38
+ animation-fill-mode: both;
39
+ animation-timeline: scroll(root);
40
+ animation-range: 0 420px;
41
+ }
42
+
43
+ // A group's opacity cannot be taken back by a child, so the fade has to
44
+ // go as soon as something is meant to stay visible through it. No loss:
45
+ // the bar is off screen for that stretch anyway, the fade only softened
46
+ // the last few pixels of the arrival.
47
+ .mw-header-reveal:has(.mw-header-keep) {
48
+ animation-name: mw-header-slide;
49
+ }
50
+
20
51
  // The ribbon is glued under the bar, so it has to leave and arrive with
21
52
  // it - left behind it would hang alone at the top of an otherwise clean
22
53
  // hero. No class of its own: a ribbon on a page with a revealing header
@@ -51,18 +82,48 @@
51
82
  // author declaration that outranks a running animation; dropping
52
83
  // `animation-name` instead leaves the element stuck on its last value, because
53
84
  // a restarted scroll timeline only catches up on the next scroll.
54
- body:has(.mw-header-reveal:focus-within, .mw-announcement:focus-within) {
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.
91
+ body:has(
92
+ .mw-header-reveal :focus:not(.mw-header-keep, .mw-header-keep *),
93
+ .mw-announcement:focus-within
94
+ ) {
55
95
  .mw-header-reveal {
56
96
  top: 0 !important;
57
97
  opacity: 1 !important;
58
98
  }
59
99
 
100
+ .mw-header-keep {
101
+ translate: none !important;
102
+ box-shadow: none !important;
103
+ }
104
+
60
105
  .mw-announcement:not(.mw-announcement-static) {
61
106
  transform: none !important;
62
107
  opacity: 1 !important;
63
108
  }
64
109
  }
65
110
 
111
+ // Same treatment while the drawer is open, which mw-header-keep makes
112
+ // reachable long before the bar arrives. The drawer is fixed but hangs off
113
+ // the header - backdrop-filter makes that one its containing block, the way a
114
+ // transform would - so with the bar still away it would sit a header height
115
+ // too high.
116
+ body:has(.mw-header-reveal):has(.mw-navbar.open) {
117
+ .mw-header-reveal {
118
+ top: 0 !important;
119
+ }
120
+
121
+ .mw-header-keep {
122
+ translate: none !important;
123
+ box-shadow: none !important;
124
+ }
125
+ }
126
+
66
127
  // Older Safari and Firefox ship no scroll timelines, so there the script
67
128
  // toggles these classes instead (main.js) - the same two positions,
68
129
  // triggered instead of scrubbed. State and transition are separate because
@@ -72,6 +133,15 @@
72
133
  .mw-header-reveal.mw-header-away {
73
134
  top: calc(-1 * var(--mw-header-reveal-offset));
74
135
  opacity: 0;
136
+
137
+ &:has(.mw-header-keep) {
138
+ opacity: 1;
139
+ }
140
+
141
+ .mw-header-keep {
142
+ translate: 0 var(--mw-header-reveal-offset);
143
+ box-shadow: $float-edge;
144
+ }
75
145
  }
76
146
 
77
147
  .mw-header-driven {
@@ -80,6 +150,12 @@
80
150
  transition:
81
151
  var(--mw-transition),
82
152
  top var(--mw-duration-slow) var(--mw-ease-out);
153
+
154
+ .mw-header-keep {
155
+ transition:
156
+ translate var(--mw-duration-slow) var(--mw-ease-out),
157
+ box-shadow var(--mw-duration-slow) var(--mw-ease-out);
158
+ }
83
159
  }
84
160
 
85
161
  .mw-announcement.mw-announcement-away {
@@ -115,6 +191,35 @@
115
191
  }
116
192
  }
117
193
 
194
+ // The same ride without the fade, for a bar something stays visible through
195
+ @keyframes mw-header-slide {
196
+ 0%,
197
+ 30% {
198
+ top: calc(-1 * var(--mw-header-reveal-offset));
199
+ }
200
+
201
+ 100% {
202
+ top: 0;
203
+ }
204
+ }
205
+
206
+ // `translate` and not `transform`: the burger puts its hover lift and its
207
+ // press on transform, and a running animation outranks an author declaration
208
+ // - it would hold the button flat for the rest of the page. The standalone
209
+ // property stacks with the shorthand instead of replacing it.
210
+ @keyframes mw-header-keep {
211
+ 0%,
212
+ 30% {
213
+ translate: 0 var(--mw-header-reveal-offset);
214
+ box-shadow: $float-edge;
215
+ }
216
+
217
+ 100% {
218
+ translate: none;
219
+ box-shadow: none;
220
+ }
221
+ }
222
+
118
223
  // The ribbon holds nothing fixed, so it can use the cheaper transform
119
224
  @keyframes mw-announcement-reveal {
120
225
  0%,
@@ -338,11 +338,29 @@
338
338
  // bottom edge, the bubble does not fit, and it gets pushed back over the
339
339
  // trigger it belongs to. What this gives up is the clipping immunity the
340
340
  // anchored path buys - a modifier inside a scroll container is cut off.
341
+ //
342
+ // Opting out has to restore the placement, not just the positioning scheme:
343
+ // the anchored path below is the later rule and clears `bottom`, `top`,
344
+ // `left` and the centring transform so `position-area` can resolve them
345
+ // itself. Leave that cleared and the box falls to its static position, which
346
+ // for a pseudo element is inside the trigger it is supposed to sit above.
347
+ // The three rules under this one then move the one edge each of them owns.
341
348
  &:is(.mw-tooltip-below, .mw-tooltip-end, .mw-tooltip-start) {
342
349
  &::before,
343
350
  &::after {
344
351
  position: absolute;
345
352
  position-area: none;
353
+ bottom: 100%;
354
+ top: auto;
355
+ left: 50%;
356
+ }
357
+
358
+ &::before {
359
+ transform: translateX(-50%);
360
+ }
361
+
362
+ &::after {
363
+ transform: translateX(-50%) rotate(45deg);
346
364
  }
347
365
  }
348
366
 
@@ -100,7 +100,8 @@
100
100
  // The 18px silhouette stays: mini buttons sit in tag rows and table cells,
101
101
  // where growing the circle would shift the layout around them. Only the hit
102
102
  // area grows, to 28px - clear of the 24px floor. The extra margin keeps two
103
- // neighbouring hit areas from overlapping.
103
+ // neighbouring hit areas from overlapping. One with a `data-tooltip` is the
104
+ // exception the mixin makes and goes to a 24px circle instead.
104
105
  .mw-btn-mini {
105
106
  margin-left: spacing('4');
106
107
  @include hit-area(5px);
@@ -117,9 +118,9 @@
117
118
  @include hit-area(6px);
118
119
  }
119
120
 
120
- // The box itself and not a hit area over it: half of these carry a
121
- // `data-tooltip`, which owns both pseudo-elements, so the trick above lands
122
- // on some of the logos and silently skips the rest.
121
+ // One size for the whole row rather than hit-area(): half of these carry a
122
+ // `data-tooltip`, and the mixin treats those differently by design - a row
123
+ // of logos where some grew and some did not reads as a rendering fault.
123
124
  .mw-techstack-item-sm {
124
125
  min-width: 2.5rem;
125
126
  min-height: 2.5rem;