02b2ce8109
Same treatment as the seasonal themes: split the 502-line chatBackground.ts Record into one premium module per background under lotus/backgrounds/ (each exposes a tuned dark + light ChatBgVariants), one Opus agent per background against a shared brief. chatBackground.ts now assembles DARK/LIGHT from the modules; getChatBg is unchanged. Carbon + Aurora are kept inline as-is (user favorites); none stays the empty layer. Every redesign: layered oklch palettes, seamless tiling with worked-out tile math (integer-multiple periods; edge-wrapping inline-SVG data-URIs for circuit/hexgrid/waves/herringbone/chevron/tactical), independently-tuned dark+light (not a recolor), and low "felt-not-read" opacity so chat text stays WCAG-AA legible. The 5 animated backgrounds (rain, star drift, grid pulse, aurora flow, fireflies) each colocate a vanilla-extract keyframe .css.ts, animate only background-position for a jump-free loop, and — since getChatBg strips animation for reduced-motion — render a finished static frame too. Redesigned: blueprint, stars, topographic, herringbone, crosshatch, chevron, polka, triangles, plaid, tactical, circuit, hexgrid, waves, neon, anim-rain, anim-stars, anim-pulse, anim-aurora, anim-fireflies. Gates: tsc clean, ESLint clean, Prettier clean, build OK, 551 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
2.0 KiB
TypeScript
40 lines
2.0 KiB
TypeScript
import { keyframes } from '@vanilla-extract/css';
|
|
|
|
// Grid Pulse — a slow "energy" glow that sweeps across a static tech grid.
|
|
//
|
|
// The motif is a crisp thin grid that pulses. Rather than scaling the grid
|
|
// (which shifts every line and reads as a jitter behind text), we keep the grid
|
|
// perfectly still and PAN a single soft radial "bloom" layer diagonally across
|
|
// it. As the bloom drifts, the grid lines it passes over appear to brighten and
|
|
// then settle — a calm travelling pulse, never a flash.
|
|
//
|
|
// Layer mapping (see animPulse.ts — one background-position value per layer):
|
|
// 0. grid core lines (vertical) — STATIC ('0 0')
|
|
// 1. grid core lines (horizontal) — STATIC ('0 0')
|
|
// 2. grid fine sub-lines (V) — STATIC ('0 0')
|
|
// 3. grid fine sub-lines (H) — STATIC ('0 0')
|
|
// 4. TRAVELLING BLOOM — panned here (the only moving layer)
|
|
// 5. base wash / centre glow — STATIC ('0 0')
|
|
// 6. vignette — STATIC ('0 0')
|
|
//
|
|
// Seamless loop: the bloom layer is authored to tile (its backgroundSize in
|
|
// animPulse.ts is 480px — an exact 4x multiple of the 120px grid module, and
|
|
// 8x of the 60px sub-grid). Panning it by EXACTLY one bloom-tile (480px on both
|
|
// axes) returns every pixel to an identical neighbouring tile, so the wrap at
|
|
// 100% is invisible. Diagonal travel (both axes move together) makes the sweep
|
|
// feel organic while still landing on a whole-tile offset.
|
|
//
|
|
// getChatBg adds `willChange: 'background-position'` for the animated case, so a
|
|
// background-position pulse is exactly what the compositor is hinted for. It
|
|
// STRIPS this whole `animation` for prefers-reduced-motion / pause-animations,
|
|
// at which point the static bloom position authored in animPulse.ts is what
|
|
// shows — a finished, gently glowing grid.
|
|
export const gridPulse = keyframes({
|
|
'0%': {
|
|
backgroundPosition: '0 0, 0 0, 0 0, 0 0, 0px 0px, 0 0, 0 0',
|
|
},
|
|
'100%': {
|
|
backgroundPosition: '0 0, 0 0, 0 0, 0 0, 480px 480px, 0 0, 0 0',
|
|
},
|
|
});
|