import { ChatBgVariants } from './types'; import { starDrift } from './animStars.css'; // animStars ("Star Drift") — a serene deep-space field slowly drifting, with // genuine parallax between a near (brighter, faster) and a far (dim, slower) // star layer, floated on a faint nebula wash and calmed by a center vignette. // // Concept: three tiling star layers at coprime-ish tile sizes (137/191/233 dark, // 149/199/251 light) so their combined repeat is astronomically large and no // seam is ever perceivable. The near layer is crisp and sparse; the far "dust" // layer is dim and dense — the layer that gives depth. Beneath the stars sit a // deep-blue -> violet nebula (two soft ellipses) and a center vignette that keeps // the reading column the calmest, lowest-contrast area of the whole canvas. // // Layer stacking order (CSS paints image #1 on TOP): // 1. near stars — brighter, largest visible drift (tile 137 / 149) // 2. mid stars — softer, medium (tile 191 / 199) // 3. far dust — dimmest, slowest, most-repeated (tile 233 / 251) // 4. center vignette (100% 100%, static) // 5. nebula wash A (100% 100%, static) // 6. nebula wash B (100% 100%, static) // // Animation: `starDrift` (see animStars.css.ts) is a SLOW background-position PAN // that translates each star layer by an exact integer number of its own tiles, // so the loop is seamless AND the three layers drift at different apparent // speeds (parallax). getChatBg adds willChange/contain for the animated case and // STRIPS the `animation` for prefers-reduced-motion — at which point the static // backgroundPosition below (identical to the keyframe's 0% frame) shows as a // fully finished starfield on its own. // // Density is kept modest toward the center by the vignette + conservative dot // sizes, and every star opacity stays low so text over the field always clears // WCAG-AA in both themes. export const animStars: ChatBgVariants = { // Dark: cool white + faint blue stars on a near-black cosmos, lifted onto a // deep-blue -> violet nebula with a soft vignette darkening the calm center. dark: { backgroundColor: 'oklch(0.15 0.03 275)', backgroundImage: [ // 1. near stars — crisp cool-white, sparse, the "fast" parallax layer 'radial-gradient(circle at center, oklch(0.98 0.012 255 / 0.85) 0.6px, transparent 1.5px)', // 2. mid stars — softer, a touch blue, more of them 'radial-gradient(circle at center, oklch(0.90 0.03 260 / 0.52) 0.6px, transparent 1.3px)', // 3. far dust — faint blue haze, the slow depth layer (most repeats) 'radial-gradient(circle at center, oklch(0.78 0.06 255 / 0.28) 0.5px, transparent 1.1px)', // 4. center vignette — keeps the reading column calmest / lowest-contrast 'radial-gradient(ellipse 120% 90% at 50% 42%, transparent 40%, oklch(0.09 0.03 270 / 0.58) 100%)', // 5. nebula wash A — deep violet high-right 'radial-gradient(ellipse 140% 120% at 78% 10%, oklch(0.26 0.09 285 / 0.55) 0%, transparent 55%)', // 6. nebula wash B — deep blue low-left 'radial-gradient(ellipse 130% 110% at 16% 94%, oklch(0.21 0.07 250 / 0.50) 0%, transparent 58%)', ].join(','), backgroundSize: [ '137px 137px', // near stars '191px 191px', // mid stars '233px 233px', // far dust '100% 100%', // vignette '100% 100%', // nebula A '100% 100%', // nebula B ].join(','), // Must equal starDrift's 0% frame so reduced-motion shows this exact field. backgroundPosition: [ '0 0', // near '61px 43px', // mid (offset breaks tile alignment) '113px 97px', // far (offset again) '0 0', // vignette '0 0', // nebula A '0 0', // nebula B ].join(','), animation: `${starDrift} 90s linear infinite`, }, // Light: an airy pre-dawn sky. No literal white-on-white stars — instead very // soft pale sparkles plus the merest cool speckles, floated on a gentle cool // gradient. Reads as elegant atmosphere, never as noise over text. light: { backgroundColor: 'oklch(0.965 0.008 255)', backgroundImage: [ // 1. near sparkles — a hair brighter/warmer than the sky 'radial-gradient(circle at center, oklch(0.995 0.015 90 / 0.50) 0.6px, transparent 1.5px)', // 2. mid cool speckles — faintest hint of darkness for texture/contrast 'radial-gradient(circle at center, oklch(0.60 0.05 260 / 0.15) 0.5px, transparent 1.2px)', // 3. far dust — very soft cool haze, the slow depth layer 'radial-gradient(circle at center, oklch(0.70 0.04 255 / 0.11) 0.5px, transparent 1.1px)', // 4. center vignette — subtly brightens the calm reading center 'radial-gradient(ellipse 120% 90% at 50% 44%, oklch(1 0 0 / 0.45) 30%, transparent 100%)', // 5. pre-dawn wash A — cool blue high-right 'radial-gradient(ellipse 150% 120% at 80% 6%, oklch(0.90 0.05 255 / 0.60) 0%, transparent 60%)', // 6. pre-dawn wash B — warm blush low-left 'radial-gradient(ellipse 140% 120% at 14% 96%, oklch(0.93 0.04 40 / 0.42) 0%, transparent 62%)', ].join(','), // Same tile sizes as dark (137/191/233). The shared starDrift keyframe pans // each layer by an exact integer multiple of ITS tile (near 2x137, mid 1x191, // far 1x233); reusing these tiles here guarantees the loop wraps seamlessly in // light mode too, since one keyframe drives both themes. Coprime-ish sizes keep // the combined repeat astronomically large so no seam is ever perceivable. backgroundSize: [ '137px 137px', // near sparkles '191px 191px', // mid speckles '233px 233px', // far dust '100% 100%', // vignette '100% 100%', // wash A '100% 100%', // wash B ].join(','), // Positions mirror the keyframe 0% frame (== reduced-motion static field). backgroundPosition: [ '0 0', // near '61px 43px', // mid '113px 97px', // far '0 0', // vignette '0 0', // wash A '0 0', // wash B ].join(','), animation: `${starDrift} 100s linear infinite`, }, };