Files
cinny/src/app/features/lotus/backgrounds/animAurora.ts
T
Lotus CIandClaude Opus 5.5 2700aad209
CI / Build & Quality Checks (pull_request) Successful in 1m57s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 9s
CI / Playwright smoke (e2e) (pull_request) Failing after 9m56s
perf(backgrounds): animated chat backgrounds cost ~5% CPU instead of 50 to 70% (#337)
background-position can't run on the compositor, so every frame of the
animated chat backgrounds repainted the whole chat area: 51 to 72% main-thread
busy while idle in a room at 60 fps. The drifts are slow (3 to 22 px/s,
under a pixel per frame), so each animation now uses steps(): about 12
updates/s for fireflies, stars and aurora and about 24/s for rain and
pulse.

Measured headless (20 s idle in a room, CDP TaskDuration):
  none 0.7% → 0.9%; rain 50.7 → 5.9; stars 71.2 → 4.5;
  pulse 64.9 → 5.8; aurora 58.9 → 4.2; fireflies 71.7 → 3.8.

Aurora and pulse used ease-in-out; with steps() their motion is now
even-paced. Pause animations / reduced motion still strip the animation.

Fixes #337

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-16 19:05:52 -04:00

82 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { CSSProperties } from 'react';
import { ChatBgVariants } from './types';
import { auroraFlow } from './animAurora.css';
// Aurora Flow — a premium ANIMATED aurora: soft ribbons of northern-lights color
// slowly drifting and curling over a deep, calm base.
//
// CONCEPT
// Broad, heavily-feathered gradient bands stacked over a deep midnight base, with
// a gentle vignette that darkens the edges and keeps the reading center calm.
// The distinct STATIC 'aurora' is a favorite still; this one earns its own slot
// by MOVING — see animAurora.css.ts, which slowly pans each ribbon along its own
// path via `background-position` so the curtains appear to fold and cross.
//
// LAYER ORDER (must stay in lockstep with auroraFlow's per-layer position list):
// 1. green ribbon 2. teal ribbon 3. violet ribbon 4. sky highlight
// 5. calm reading core (static) 6. vignette (static)
//
// READABILITY
// Every ribbon is a wide ellipse fading fully to transparent well before its
// edge, at low alpha (~0.05–0.13), so no band ever concentrates enough contrast
// under the message column to threaten WCAG-AA. Layer 5 lifts a soft, even wash
// through the vertical center — the reading zone — so text always sits on a calm,
// low-variance field. oklch() keeps every hue perceptually smooth and low-chroma.
//
// MOTION / SEAMLESS LOOP
// backgroundSize is >100% per animated layer, giving room to drift; the keyframe
// returns every band to its start over one long, ease-in-out period, so the loop
// is seamless and the motion barely-perceptible. willChange/animation are added
// (and stripped for reduced-motion) by getChatBg; the static positions below are
// the finished still that shows when motion is off.
const dark: CSSProperties = {
// Deep midnight blue — the polar night sky the aurora glows over.
backgroundColor: 'oklch(0.17 0.045 255)',
backgroundImage: [
// 1. Green ribbon — the signature aurora band.
'radial-gradient(ellipse 70% 45% at 50% 50%, oklch(0.7 0.14 160 / 0.13) 0%, oklch(0.7 0.14 160 / 0.05) 45%, transparent 72%)',
// 2. Teal ribbon — cool counterpart, offset.
'radial-gradient(ellipse 80% 40% at 50% 50%, oklch(0.65 0.12 200 / 0.12) 0%, oklch(0.65 0.12 200 / 0.04) 48%, transparent 74%)',
// 3. Violet ribbon — the high curtain fold.
'radial-gradient(ellipse 65% 55% at 50% 50%, oklch(0.55 0.13 300 / 0.11) 0%, oklch(0.55 0.13 300 / 0.04) 46%, transparent 70%)',
// 4. Sky/aqua highlight — subtle shimmer that counter-drifts.
'radial-gradient(ellipse 55% 35% at 50% 50%, oklch(0.72 0.1 220 / 0.09) 0%, transparent 65%)',
// 5. Calm reading core (static) — a soft even wash down the center column so
// message text always rests on a low-variance field.
'radial-gradient(ellipse 120% 60% at 50% 50%, oklch(0.2 0.04 255 / 0.5) 0%, transparent 70%)',
// 6. Vignette (static) — gently darkens the edges for luminous depth.
'radial-gradient(ellipse 130% 120% at 50% 50%, transparent 55%, oklch(0.12 0.04 260 / 0.55) 100%)',
].join(','),
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
backgroundRepeat: 'no-repeat',
animation: `${auroraFlow} 60s steps(180) infinite`,
};
const light: CSSProperties = {
// Pale cool base — a soft pre-dawn sky the pastel aurora dreams over.
backgroundColor: 'oklch(0.97 0.012 240)',
backgroundImage: [
// 1. Mint ribbon.
'radial-gradient(ellipse 70% 45% at 50% 50%, oklch(0.85 0.08 160 / 0.5) 0%, oklch(0.85 0.08 160 / 0.16) 45%, transparent 72%)',
// 2. Sky ribbon.
'radial-gradient(ellipse 80% 40% at 50% 50%, oklch(0.83 0.07 220 / 0.48) 0%, oklch(0.83 0.07 220 / 0.14) 48%, transparent 74%)',
// 3. Lilac ribbon — the high curtain fold.
'radial-gradient(ellipse 65% 55% at 50% 50%, oklch(0.82 0.07 300 / 0.42) 0%, oklch(0.82 0.07 300 / 0.12) 46%, transparent 70%)',
// 4. Aqua highlight — subtle shimmer that counter-drifts.
'radial-gradient(ellipse 55% 35% at 50% 50%, oklch(0.88 0.06 200 / 0.34) 0%, transparent 65%)',
// 5. Calm reading core (static) — a bright even wash down the center column
// so dark message text always rests on a light, low-variance field.
'radial-gradient(ellipse 120% 60% at 50% 50%, oklch(0.99 0.005 240 / 0.6) 0%, transparent 70%)',
// 6. Vignette (static) — a whisper of cool shade at the edges for depth.
'radial-gradient(ellipse 130% 120% at 50% 50%, transparent 55%, oklch(0.9 0.02 250 / 0.45) 100%)',
].join(','),
backgroundSize: '260% 240%, 300% 260%, 240% 280%, 220% 200%, 100% 100%, 100% 100%',
backgroundPosition: '0% 30%, 100% 70%, 50% 0%, 20% 80%, 50% 50%, 50% 50%',
backgroundRepeat: 'no-repeat',
animation: `${auroraFlow} 60s steps(180) infinite`,
};
export const animAurora: ChatBgVariants = { dark, light };