artist page glass: the hero fades INTO the frost - the burial moves from the sheet's paint to the photo's own alpha (gfade frame+counter: in-flow pass-throughs wrapping every photo layer, seam mirror excluded; a STATIC inverted-smoothstep mask parked at --fog-rest rides the sheet's glide on the frame while the counter rides the exact inverse, so the photo stays pinned, the mask never animates, and photo-visible-fraction equals the base fade at every pixel by construction); under glass the sheet's band goes transparent and its tail lands on the frost tint, so the same dissolve now lands on desktop glow - solid mode byte-identical (wrappers inert, from-poses no-ops); the wrappers must NEVER be positioned: the in-flow banner is what sizes hero-bg and absolute wrappers collapse the chain to zero
This commit is contained in:
1 parent
6ba1864498
commit
7ab678fd22
1 file changed
+158
@@ -4644,6 +4644,17 @@
|
||||
rotation, the outgoing pair stays underneath until the new pair
|
||||
fades in. -->
|
||||
<div class="hero-bg" aria-hidden="true">
|
||||
<!-- Dark-glass fade frame + counter (inert in solid mode:
|
||||
two absolute inset-0 pass-through boxes, no transform,
|
||||
no mask). Under glass the FRAME carries a static mask
|
||||
whose band mirrors the sheet's smoothstep and rides
|
||||
the sheet's own glide; the COUNTER rides the exact
|
||||
inverse, keeping every photo layer visually pinned —
|
||||
both transforms compositor-side, the mask never
|
||||
animates. The seam-clip lives OUTSIDE the pair: the
|
||||
bar's mirror must never fade. -->
|
||||
<div class="gfade-frame" aria-hidden="true">
|
||||
<div class="gfade-counter">
|
||||
<div class="hero-fallback" style="--seed-hue: {seedHue}"></div>
|
||||
{#if seedUrl}
|
||||
<div
|
||||
@@ -4738,6 +4749,8 @@
|
||||
{:else if !selectedUsesGpu}
|
||||
<div class="banner base"></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<!-- THE SEAM TAIL (last child on purpose: paints over every
|
||||
hero layer). The clip pins at the bar's TOP line and
|
||||
does not ride: the mirror inside rides with the hero,
|
||||
@@ -5734,6 +5747,83 @@
|
||||
var(--bg-base) 100%
|
||||
);
|
||||
}
|
||||
/* ── DARK GLASS: the hero fades INTO the frost ─────────────────────
|
||||
The burial moves from the sheet's paint to the photo's own alpha.
|
||||
The .gfade-frame (wrapping every photo layer; seam-clip excluded)
|
||||
carries a STATIC mask whose band is the sheet's exact smoothstep
|
||||
INVERTED (mask alpha ≡ 1 − sheet alpha), parked at var(--fog-rest)
|
||||
— which IS the band's hero-local rest offset: the sheetclip sits at
|
||||
−pin-origin, the sheet at --band-top, and hero-bg at
|
||||
--band-from-pin = --band-top − pin-origin, so the offsets cancel to
|
||||
fog-rest exactly. The frame rides the sheet's glide (same sampler,
|
||||
same range) carrying the mask; the counter rides the inverse so the
|
||||
photo stays pinned. Photo-visible-fraction across the band therefore
|
||||
equals the base fade at every pixel BY CONSTRUCTION — the only
|
||||
intended difference is what the fade lands ON: the sheet's band is
|
||||
transparent under glass (the photo fades itself) and its tail is the
|
||||
frost tint, so the photo dissolves into desktop glow. Above the
|
||||
band the mask is solid — the photo stays full under the bar at every
|
||||
pose, so the data-painted contract holds with no stand-in strip.
|
||||
Pre-measure, an unset --fog-rest resolves the mask solid via the
|
||||
999999px sentinel: a no-op, base look. */
|
||||
/* The frame/counter are IN-FLOW pass-throughs, deliberately not
|
||||
positioned: the in-flow banner img is what gives .hero-bg its
|
||||
height, and an absolute wrapper collapses that chain to zero (the
|
||||
whole hero then clips away — measured the hard way). Their boxes
|
||||
coincide with hero-bg's content box, so when the glass transform
|
||||
makes the frame a containing block, absolute hero layers re-anchor
|
||||
to an identical box. No rule needed in solid mode at all. */
|
||||
:global(:root.glass) .gfade-frame {
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
#000 0,
|
||||
#000 var(--fog-rest, 999999px),
|
||||
rgba(0, 0, 0, 0.981) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.08),
|
||||
rgba(0, 0, 0, 0.919) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.17),
|
||||
rgba(0, 0, 0, 0.844) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.25),
|
||||
rgba(0, 0, 0, 0.741) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.33),
|
||||
rgba(0, 0, 0, 0.603) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.42),
|
||||
rgba(0, 0, 0, 0.5) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.5),
|
||||
rgba(0, 0, 0, 0.397) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.58),
|
||||
rgba(0, 0, 0, 0.259) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.67),
|
||||
rgba(0, 0, 0, 0.156) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.75),
|
||||
rgba(0, 0, 0, 0.081) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.83),
|
||||
rgba(0, 0, 0, 0.019) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.92),
|
||||
transparent calc(var(--fog-rest, 999999px) + var(--fog-span, 170px)),
|
||||
transparent 100%
|
||||
);
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to bottom,
|
||||
#000 0,
|
||||
#000 var(--fog-rest, 999999px),
|
||||
rgba(0, 0, 0, 0.981) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.08),
|
||||
rgba(0, 0, 0, 0.919) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.17),
|
||||
rgba(0, 0, 0, 0.844) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.25),
|
||||
rgba(0, 0, 0, 0.741) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.33),
|
||||
rgba(0, 0, 0, 0.603) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.42),
|
||||
rgba(0, 0, 0, 0.5) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.5),
|
||||
rgba(0, 0, 0, 0.397) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.58),
|
||||
rgba(0, 0, 0, 0.259) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.67),
|
||||
rgba(0, 0, 0, 0.156) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.75),
|
||||
rgba(0, 0, 0, 0.081) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.83),
|
||||
rgba(0, 0, 0, 0.019) calc(var(--fog-rest, 999999px) + var(--fog-span, 170px) * 0.92),
|
||||
transparent calc(var(--fog-rest, 999999px) + var(--fog-span, 170px)),
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
/* Under glass the sheet no longer buries the photo (the mask above
|
||||
does), so its band is fully transparent and its page below is the
|
||||
FROST TINT — the ground the fade lands on. Solid mode keeps the
|
||||
original gradient untouched. */
|
||||
:global(:root.glass) .sheet {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(18, 18, 18, 0) 0px,
|
||||
rgba(18, 18, 18, 0) var(--fog-span),
|
||||
rgba(18, 18, 18, 0.5) calc(var(--fog-span) * 1.15),
|
||||
rgba(18, 18, 18, 0.5) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* LAYER 3 — the content, over the sheet and the hero. Its one special
|
||||
behaviour: rows fade out over 18px as they slide under the banner
|
||||
@@ -5917,6 +6007,22 @@
|
||||
));
|
||||
}
|
||||
|
||||
/* Dark glass on this path: the fade frame/counter track the sheet's
|
||||
p² delta with the same live calc (delta only — the frame's rest
|
||||
pose is 0, its mask already parks at --fog-rest). */
|
||||
:global(:root.glass) .gfade-frame {
|
||||
transform: translateY(calc(
|
||||
var(--p, 0) * var(--p, 0)
|
||||
* (var(--strip) - var(--fog-above) - var(--fog-rest))
|
||||
));
|
||||
}
|
||||
:global(:root.glass) .gfade-counter {
|
||||
transform: translateY(calc(
|
||||
-1 * var(--p, 0) * var(--p, 0)
|
||||
* (var(--strip) - var(--fog-above) - var(--fog-rest))
|
||||
));
|
||||
}
|
||||
|
||||
.artist-page[data-scroll="1"] .banner,
|
||||
.artist-page[data-scroll="1"] :global(.gpu-hero) {
|
||||
filter: var(--mono);
|
||||
@@ -6124,6 +6230,38 @@
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
|
||||
/* Dark glass: the fade frame rides the sheet's glide (same p²
|
||||
sampler, same range — the two must never disagree by a pixel)
|
||||
carrying its static mask; the counter rides the exact inverse so
|
||||
the photo layers stay pinned. Both are pure compositor
|
||||
transforms; the mask itself never animates. */
|
||||
:global(:root.glass) .gfade-frame {
|
||||
animation-name: gfade-frame-ride;
|
||||
animation-duration: auto;
|
||||
animation-timing-function: linear(
|
||||
0, 0.0025 5%, 0.01 10%, 0.0225 15%, 0.04 20%, 0.0625 25%,
|
||||
0.09 30%, 0.1225 35%, 0.16 40%, 0.2025 45%, 0.25 50%,
|
||||
0.3025 55%, 0.36 60%, 0.4225 65%, 0.49 70%, 0.5625 75%,
|
||||
0.64 80%, 0.7225 85%, 0.81 90%, 0.9025 95%, 1
|
||||
);
|
||||
animation-fill-mode: both;
|
||||
animation-timeline: --artist-pane;
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
:global(:root.glass) .gfade-counter {
|
||||
animation-name: gfade-counter-ride;
|
||||
animation-duration: auto;
|
||||
animation-timing-function: linear(
|
||||
0, 0.0025 5%, 0.01 10%, 0.0225 15%, 0.04 20%, 0.0625 25%,
|
||||
0.09 30%, 0.1225 35%, 0.16 40%, 0.2025 45%, 0.25 50%,
|
||||
0.3025 55%, 0.36 60%, 0.4225 65%, 0.49 70%, 0.5625 75%,
|
||||
0.64 80%, 0.7225 85%, 0.81 90%, 0.9025 95%, 1
|
||||
);
|
||||
animation-fill-mode: both;
|
||||
animation-timeline: --artist-pane;
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
|
||||
/* The identity block, ungated for the same reason. Today its top
|
||||
sheds p·travel via --edge (a per-frame LAYOUT of the whole idpin
|
||||
subtree); translateY(-p·travel) is the identical rigid motion of
|
||||
@@ -6488,6 +6626,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark glass: the fade frame carries the sheet's exact delta (its rest
|
||||
pose is 0 — the mask parks at --fog-rest in its own gradient), the
|
||||
counter the exact inverse. Same endpoints as sheet-glide-ride. */
|
||||
@keyframes gfade-frame-ride {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(var(--fog-ride, 0px));
|
||||
}
|
||||
}
|
||||
@keyframes gfade-counter-ride {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(calc(-1 * var(--fog-ride, 0px)));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes idblock-ride {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
|
||||
Reference in new issue
Block a user