Ice scene: the glossy orbs become rising bubbles, ~38% more transparent
- Orbs rise from below the bottom edge to above the top edge, each with its own 36-68s pass (slower on phones), a negative delay (scene already populated on load), own horizontal position, a gentle sway and a slight scale drift; opacity fades in/out at the edges so the loop never pops. - Only transform + opacity animate, each orb on its own composited layer (will-change: transform); no filter/backdrop-filter, nothing animates size/offsets/shadow. The glacier blobs are untouched. - Opacity lowered ~38% in both themes: 0.55->0.34 light, 0.8->0.5 dark, phone overrides 0.32->0.2 / 0.42->0.26. - prefers-reduced-motion: rise removed, orbs return to the old static placements (a paused-from-start CSS animation is not applied in Chrome and would park them below the fold); tab hidden: a visibility listener parks the whole scene (.ice-paused), blob drift included. - Measured with a CDP perf trace on a 4x throttled phone profile: p95 33.4ms, 0 frames over 34ms, no long tasks - identical to the pre-change baseline; ~0.6% extra main-thread CPU. Desktop stays compositor-bound from the pre-existing blob drift, as before. tests/bubble-perf.mjs keeps it repeatable. - New bubbles suite (34 checks) in the repo harness; the axe contrast matrix is now 16 runs (360/390/1280 x dark/light x EN/CS) and passes. - SW cache -> freezmo-v31; README decision 46. Full suite: 11/11 suites, 424 checks.
This commit is contained in:
parent
dbd5dfbc58
commit
6b6a6ea158
9 changed files with 419 additions and 31 deletions
|
|
@ -178,14 +178,14 @@
|
|||
--ice-base-top: #f4f9ff;
|
||||
--ice-base-bottom: #e6f1fb;
|
||||
--ice-blob-opacity: 0.18;
|
||||
--ice-orb-opacity: 0.55;
|
||||
--ice-orb-opacity: 0.34;
|
||||
--ice-grain-opacity: 0.05;
|
||||
}
|
||||
.dark {
|
||||
--ice-base-top: #070b16;
|
||||
--ice-base-bottom: #0c1426;
|
||||
--ice-blob-opacity: 0.4;
|
||||
--ice-orb-opacity: 0.8;
|
||||
--ice-orb-opacity: 0.5;
|
||||
--ice-grain-opacity: 0.045;
|
||||
}
|
||||
|
||||
|
|
@ -279,11 +279,21 @@ body {
|
|||
50% { transform: translate3d(3vw, -4vh, 0) scale(1.1); }
|
||||
}
|
||||
|
||||
/* Glossy ice orbs, placed so the glass panels blur them. */
|
||||
/* Glossy ice orbs: slow bubbles that rise from just below the bottom edge to
|
||||
above the top edge, swaying gently and fading in/out near the edges so the
|
||||
loop never pops. Only transform + opacity animate, each orb on its own
|
||||
composited layer (will-change: transform): no filter/backdrop-filter, and no
|
||||
animated size, offsets or shadow. Per-orb duration, delay (negative, so the
|
||||
scene is already populated on load), horizontal position and sway keep them
|
||||
out of sync. */
|
||||
.ice-orb {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
border-radius: 50%;
|
||||
opacity: var(--ice-orb-opacity);
|
||||
will-change: transform;
|
||||
animation: ice-rise var(--ice-orb-duration, 48s) ease-in-out infinite;
|
||||
animation-delay: var(--ice-orb-delay, 0s);
|
||||
background: radial-gradient(
|
||||
circle at 30% 25%,
|
||||
rgb(255 255 255 / 0.95) 0%,
|
||||
|
|
@ -293,11 +303,25 @@ body {
|
|||
);
|
||||
box-shadow: 0 18px 40px -18px rgb(30 64 175 / 0.55), inset 0 -8px 18px -10px rgb(30 64 175 / 0.6);
|
||||
}
|
||||
.ice-orb-1 { width: 190px; height: 190px; top: 14%; left: 12%; }
|
||||
.ice-orb-2 { width: 84px; height: 84px; top: 58%; left: 4%; }
|
||||
.ice-orb-3 { width: 220px; height: 220px; bottom: -70px; right: 10%; }
|
||||
.ice-orb-4 { width: 52px; height: 52px; top: 24%; right: 28%; }
|
||||
.ice-orb-5 { width: 120px; height: 120px; bottom: 16%; left: 38%; }
|
||||
.ice-orb-1 { width: 190px; height: 190px; left: 12%; --ice-orb-duration: 46s; --ice-orb-delay: -18s; --ice-sway: 4vw; }
|
||||
.ice-orb-2 { width: 84px; height: 84px; left: 4%; --ice-orb-duration: 58s; --ice-orb-delay: -41s; --ice-sway: -3vw; }
|
||||
.ice-orb-3 { width: 220px; height: 220px; right: 10%; --ice-orb-duration: 36s; --ice-orb-delay: -7s; --ice-sway: 5vw; }
|
||||
.ice-orb-4 { width: 52px; height: 52px; right: 28%; --ice-orb-duration: 64s; --ice-orb-delay: -52s; --ice-sway: -4vw; }
|
||||
.ice-orb-5 { width: 120px; height: 120px; left: 38%; --ice-orb-duration: 52s; --ice-orb-delay: -27s; --ice-sway: 3vw; }
|
||||
|
||||
/* The rise: -100vh - 100% clears the top edge (100% = the orb's own height);
|
||||
the sway crosses the path twice and the scale drifts slightly. Opacity
|
||||
reaches the token value by 12% and holds it until 88%, so the fades happen
|
||||
while the orb is still partly outside the scene. */
|
||||
@keyframes ice-rise {
|
||||
0% { transform: translate3d(0, 0, 0) scale(0.94); opacity: 0; }
|
||||
12% { opacity: var(--ice-orb-opacity); }
|
||||
32% { transform: translate3d(var(--ice-sway, 0px), calc(-32vh - 32%), 0) scale(0.97); }
|
||||
58% { transform: translate3d(calc(var(--ice-sway, 0px) * -0.55), calc(-58vh - 58%), 0) scale(1.02); }
|
||||
80% { transform: translate3d(calc(var(--ice-sway, 0px) * 0.4), calc(-80vh - 80%), 0) scale(1.04); }
|
||||
88% { opacity: var(--ice-orb-opacity); }
|
||||
100% { transform: translate3d(0, calc(-100vh - 100%), 0) scale(1.06); opacity: 0; }
|
||||
}
|
||||
|
||||
/* Frosted grain over the whole scene. */
|
||||
.ice-grain {
|
||||
|
|
@ -310,6 +334,25 @@ body {
|
|||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ice-blob { animation: none; }
|
||||
/* Orbs go back to the pre-rise static layout (a paused animation is not
|
||||
applied at all in Chrome, which would leave them parked below the fold)
|
||||
and give their composited layers back. */
|
||||
.ice-orb {
|
||||
animation: none;
|
||||
will-change: auto;
|
||||
}
|
||||
.ice-orb-1 { top: 14%; left: 12%; }
|
||||
.ice-orb-2 { top: 58%; left: 4%; }
|
||||
.ice-orb-3 { top: auto; bottom: -70px; right: 10%; }
|
||||
.ice-orb-4 { top: 24%; right: 28%; }
|
||||
.ice-orb-5 { top: auto; bottom: 16%; left: 38%; }
|
||||
}
|
||||
|
||||
/* Background tab: park every scene animation, so an invisible page costs no
|
||||
compositor work. */
|
||||
.ice-paused .ice-orb,
|
||||
.ice-paused .ice-blob {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
/* Phones: fewer, softer blobs so the drift animation stays cheap. Unlayered
|
||||
|
|
@ -321,28 +364,30 @@ body {
|
|||
.ice-orb-5 {
|
||||
display: none;
|
||||
}
|
||||
.ice-orb-1 { width: 120px; height: 120px; }
|
||||
.ice-orb-3 { width: 150px; height: 150px; }
|
||||
.ice-orb-1 { width: 120px; height: 120px; --ice-orb-duration: 54s; --ice-orb-delay: -21s; }
|
||||
.ice-orb-2 { --ice-orb-duration: 64s; --ice-orb-delay: -46s; }
|
||||
.ice-orb-3 { width: 150px; height: 150px; --ice-orb-duration: 44s; --ice-orb-delay: -8s; }
|
||||
}
|
||||
|
||||
/* Phones: the decoration is already *behind* the content (the scene is a
|
||||
fixed, pointer-events:none layer at z-index -1), but it shines through the
|
||||
translucent page and glass, so it reads as overlap. Thin it right down and
|
||||
tuck the orbs to the edges, away from the text column and the tab bar. */
|
||||
translucent page and glass, so it reads as overlap. Thin it right down,
|
||||
tuck the orbs to the edges, away from the text column and the tab bar, and
|
||||
slow their rise down further so the drifting slivers stay calm. */
|
||||
@media (max-width: 639px) {
|
||||
:root {
|
||||
--ice-blob-opacity: 0.1;
|
||||
--ice-orb-opacity: 0.32;
|
||||
--ice-orb-opacity: 0.2;
|
||||
}
|
||||
.dark {
|
||||
--ice-blob-opacity: 0.2;
|
||||
--ice-orb-opacity: 0.42;
|
||||
--ice-orb-opacity: 0.26;
|
||||
}
|
||||
/* Phones: orbs hug the screen edges so they can never sit under the
|
||||
content column's cards or primary buttons. The lower-right orb still
|
||||
read as a glow behind the wizard action bar, so phones drop it. */
|
||||
.ice-orb-1 { width: 96px; height: 96px; top: -44px; left: -48px; }
|
||||
.ice-orb-2 { width: 72px; height: 72px; top: 60%; left: -44px; }
|
||||
.ice-orb-1 { width: 96px; height: 96px; left: -48px; --ice-orb-duration: 62s; --ice-orb-delay: -30s; --ice-sway: 5vw; }
|
||||
.ice-orb-2 { width: 72px; height: 72px; left: -44px; --ice-orb-duration: 68s; --ice-orb-delay: -49s; --ice-sway: -4vw; }
|
||||
.ice-orb-3 { display: none; }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue