Merge nucleic/upbeat-jade-ibis-kt5e into main
Mirrored from corpo f8f4b82.
This commit is contained in:
+73
-25
@@ -15,10 +15,12 @@ const canonical = new URL('/', Astro.site);
|
|||||||
hand those 120px to the page, so no element and no viewport unit reaches them —
|
hand those 120px to the page, so no element and no viewport unit reaches them —
|
||||||
see the note on .ambient for what actually resolves it. -->
|
see the note on .ambient for what actually resolves it. -->
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<!-- Not --paper. Safari paints its overscroll regions with this flat colour, but
|
<!-- --paper, and it has to stay --paper: this is one of the surfaces Safari fills
|
||||||
the page underneath is the ambient field, which lifts the edges well off paper.
|
around the page, alongside the canvas, and the field is now brought to --paper
|
||||||
Measured off rendered pixels at the top and bottom edges. -->
|
at the viewport edges so all of them agree. A "better matching" value here is
|
||||||
<meta name="theme-color" content="#0f1813" />
|
precisely the bug — it reintroduces a colour that differs from the page edge,
|
||||||
|
which surfaces as a bar whenever Safari switches to it during a scroll. -->
|
||||||
|
<meta name="theme-color" content="#0a100c" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
@@ -175,10 +177,10 @@ const canonical = new URL('/', Astro.site);
|
|||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
/* Deliberately not inset: 0. A fixed box pinned to the bottom stops at the small
|
/* Deliberately not inset: 0. A fixed box pinned to the bottom stops at the small
|
||||||
viewport, but iOS Safari keeps rendering the page behind its translucent
|
viewport, leaving the strip between svh and lvh unpainted by this layer — which
|
||||||
toolbar — so the strip between svh and lvh fell back to flat paper and read as
|
matters because the toolbar retracts and hands those pixels back to the page.
|
||||||
a grey bar under the chrome. Sized to the large viewport, the field paints
|
Sized to the large viewport, the layer owns them in both states; what it puts
|
||||||
through the toolbar instead. */
|
there is the ::after note's business. */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
height: 100lvh;
|
height: 100lvh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -194,23 +196,55 @@ const canonical = new URL('/', Astro.site);
|
|||||||
and no viewport-fit=cover reaches them; earlier attempts only moved the seam.
|
and no viewport-fit=cover reaches them; earlier attempts only moved the seam.
|
||||||
|
|
||||||
So the canvas is the single point, and there is exactly one thing to get right:
|
So the canvas is the single point, and there is exactly one thing to get right:
|
||||||
the canvas colour and the page's own edge colour have to be equal. The canvas can
|
every surface Safari fills around the page, and the page's own colour where it
|
||||||
only be flat --paper, and the field is a moving light whose edge tone drifts, so
|
meets them, must be the same paint. Those surfaces can only be flat colours, and
|
||||||
matching by picking a constant is a value that is wrong again as soon as the bands
|
the field is a moving light whose edge tone drifts, so matching them by picking a
|
||||||
move. Instead the field is brought to zero at the bottom of the viewport — then
|
constant is a value that is wrong again as soon as the bands move — and wrong in a
|
||||||
page edge, canvas and theme-color are all --paper by construction, whatever the
|
different way in each scroll state, which is what made the bar come and go.
|
||||||
animation is doing, and the seam cannot come back.
|
|
||||||
|
|
||||||
Costs nothing to look at: this falls inside the strip Safari's toolbar already
|
The field is therefore brought to zero at both edges of the viewport. --paper is
|
||||||
covers. Static, so the layer still composites without repainting per frame. */
|
then the only colour in play: canvas, theme-color, overscroll, the chrome strips,
|
||||||
|
and the page itself where it touches any of them. There is no second value left
|
||||||
|
for Safari to swap in, so no scroll state, toolbar collapse or fixed-layer
|
||||||
|
reposition has anything to expose. That is the property to preserve — if this ever
|
||||||
|
needs editing, keep the edges at --paper rather than tuning a colour to match.
|
||||||
|
|
||||||
|
The one thing that has to be right is where the bottom reaches --paper, and it is
|
||||||
|
not a number to taste. This layer is fixed and 100lvh tall, but the bottom edge of
|
||||||
|
what the reader can see is only at 100lvh with the toolbar collapsed; expanded, it
|
||||||
|
is at 100svh, ~100px higher, and Safari slides between the two mid-scroll. Ending
|
||||||
|
the fade at the element's own bottom therefore left the visible edge sitting part
|
||||||
|
way up the fade whenever the toolbar was out — lighter than the strip below it, so
|
||||||
|
the bar came back — and clean again once the toolbar went away. A boundary at a
|
||||||
|
coordinate that moves is not a fix, it is the same bug relocated.
|
||||||
|
|
||||||
|
Nor can that band simply be held flat: those pixels are behind the chrome in one
|
||||||
|
state and are ordinary page in the other, so a flat patch there is invisible with
|
||||||
|
the toolbar out and a hard-edged band the moment it retracts. Both failures are
|
||||||
|
the same failure — an edge existing anywhere the viewport boundary can land.
|
||||||
|
|
||||||
|
So there is no edge. The field decays to --paper continuously, and is already
|
||||||
|
exactly --paper by 100svh, above the highest position the visible bottom can take;
|
||||||
|
everything from there to 100lvh is the same paint. Every boundary the viewport
|
||||||
|
could land on is gone, in either state and part way through the transition between
|
||||||
|
them. The decay is spread over ~14rem because the whole excursion is about five
|
||||||
|
steps of RGB — under a step per 40px, below anything the eye reads as an edge, and
|
||||||
|
the grain dithers the remainder. Keep that shape if this is ever edited: reach
|
||||||
|
--paper at or before 100svh, and get there gradually. Top edge likewise, though it
|
||||||
|
is simpler — the layout viewport's top does not move, so it only needs to arrive.
|
||||||
|
Static, so the layer still composites without repainting per frame. */
|
||||||
.ambient::after {
|
.ambient::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
inset: 0;
|
||||||
bottom: 0;
|
background: linear-gradient(
|
||||||
left: 0;
|
to bottom,
|
||||||
height: 5rem;
|
var(--paper) 0,
|
||||||
background: linear-gradient(to top, var(--paper), rgb(10 16 12 / 0%));
|
rgb(10 16 12 / 0%) 8rem,
|
||||||
|
rgb(10 16 12 / 0%) calc(100svh - 14rem),
|
||||||
|
var(--paper) 100svh,
|
||||||
|
var(--paper) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Oversized so the bands can travel a long way without an edge ever entering the
|
/* Oversized so the bands can travel a long way without an edge ever entering the
|
||||||
@@ -679,14 +713,28 @@ const canonical = new URL('/', Astro.site);
|
|||||||
which lands on load and leaves it plainly visible. */
|
which lands on load and leaves it plainly visible. */
|
||||||
@supports (animation-timeline: view()) {
|
@supports (animation-timeline: view()) {
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
|
/* Where the reveal runs, the footer is transparent until it is scrolled to, so
|
||||||
|
opacity is what hides it and the layout no longer has to. That buys back both
|
||||||
|
halves of the drop the fallback needs: the toolbar term goes, because a footer
|
||||||
|
behind Safari's bar cannot show through it at zero alpha, and the clearance
|
||||||
|
shrinks to the little that keeps the rule from being mid-fade at rest. All of
|
||||||
|
it was scroll the reader had to spend before anything began to happen. */
|
||||||
|
.page-shell {
|
||||||
|
--footer-drop: clamp(4.75rem, 9.3vh, 5.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: footer-reveal linear both;
|
animation: footer-reveal linear both;
|
||||||
animation-timeline: view();
|
animation-timeline: view();
|
||||||
/* Finishes at 88% rather than 100%: the last stretch of "entry" is the
|
/* "entry" opens before the footer's edge reaches the fold, not at it: parked,
|
||||||
element clearing Safari's translucent toolbar, and waiting that long
|
it already measures 9-11% entered across phone sizes. So the start has to be
|
||||||
means it is still fading while it looks fully on screen. */
|
past that or the footer sits permanently part-faded at rest — 20% clears it
|
||||||
animation-range: entry 12% entry 88%;
|
on every size tested with room to spare, and still begins the reveal within
|
||||||
|
the first few pixels of scroll. Ending at 45% means it reads as arrived at
|
||||||
|
roughly a third of the travel, instead of still fading while it already
|
||||||
|
looks fully on screen. */
|
||||||
|
animation-range: entry 20% entry 45%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user