26f998d243
Split the 808-line SeasonalEffect monolith into one self-contained module per theme under seasonal/themes/ (<Theme>.tsx + <Theme>.css.ts), and gave every theme a premium, research-backed redesign (one Opus agent per theme against a shared brief). SeasonalEffect now just imports the 11 overlays and dispatches; the orphaned shared Seasonal.css.ts is removed (each theme owns its keyframes). Each overlay: layered oklch palettes, GPU-only animation (transform/opacity), `contain: layout paint style` to kill repaint flicker, ≤~40-element perf budget, particles seeded once via useMemo (no per-frame state), a gorgeous STATIC prefers-reduced-motion form (the settings preview thumbnail), WCAG-AA-preserving low opacities, and no new deps / no external assets (inline SVG data-URIs, Tauri/CSP-safe). Themes: Halloween, Christmas, New Year, Autumn, April Fools, Lunar New Year, Valentines, St. Patrick's, Earth Day, Deep Space, Arcade. Gates: tsc clean, ESLint clean, Prettier clean, build OK, 551 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import { keyframes } from '@vanilla-extract/css';
|
|
|
|
/**
|
|
* Halloween overlay keyframes. Every animation touches ONLY `transform` and
|
|
* `opacity` so the compositor can run them on the GPU without layout/paint.
|
|
* keyframes() returns the generated animation-name string, applied inline.
|
|
*/
|
|
|
|
/** Slow breathing of the sickly moon-glow vignette. */
|
|
export const animMoonPulse = keyframes({
|
|
'0%': { transform: 'scale(1)', opacity: '0.55' },
|
|
'50%': { transform: 'scale(1.06)', opacity: '0.8' },
|
|
'100%': { transform: 'scale(1)', opacity: '0.55' },
|
|
});
|
|
|
|
/** Low fog band: drifts sideways while gently rising and swelling. */
|
|
export const animFogDrift = keyframes({
|
|
'0%': { transform: 'translate3d(-12%, 6%, 0) scale(1.1)', opacity: '0' },
|
|
'15%': { opacity: '0.5' },
|
|
'50%': { transform: 'translate3d(6%, -2%, 0) scale(1.25)', opacity: '0.65' },
|
|
'85%': { opacity: '0.45' },
|
|
'100%': { transform: 'translate3d(18%, 4%, 0) scale(1.1)', opacity: '0' },
|
|
});
|
|
|
|
/** A bat flaps slowly across the sky in a shallow arc. */
|
|
export const animBatGlide = keyframes({
|
|
'0%': { transform: 'translate3d(-12vw, 8vh, 0) scale(0.9)', opacity: '0' },
|
|
'10%': { opacity: '0.7' },
|
|
'45%': { transform: 'translate3d(45vw, -4vh, 0) scale(1)' },
|
|
'80%': { transform: 'translate3d(85vw, 6vh, 0) scale(0.95)', opacity: '0.6' },
|
|
'100%': { transform: 'translate3d(112vw, 2vh, 0) scale(0.9)', opacity: '0' },
|
|
});
|
|
|
|
/** The bat's wings beat — fast vertical squash of the wing element. */
|
|
export const animWingFlap = keyframes({
|
|
'0%': { transform: 'scaleY(1) scaleX(1)' },
|
|
'50%': { transform: 'scaleY(0.35) scaleX(1.08)' },
|
|
'100%': { transform: 'scaleY(1) scaleX(1)' },
|
|
});
|
|
|
|
/** Will-o'-wisp ember: floats upward, swaying, pulsing in brightness. */
|
|
export const animEmberFloat = keyframes({
|
|
'0%': { transform: 'translate3d(0, 0, 0) scale(0.7)', opacity: '0' },
|
|
'12%': { opacity: '0.85' },
|
|
'35%': { transform: 'translate3d(14px, -28vh, 0) scale(1)' },
|
|
'65%': { transform: 'translate3d(-12px, -55vh, 0) scale(0.9)', opacity: '0.7' },
|
|
'90%': { opacity: '0.25' },
|
|
'100%': { transform: 'translate3d(8px, -82vh, 0) scale(0.6)', opacity: '0' },
|
|
});
|
|
|
|
/** Soft twinkle for embers — independent opacity flicker layered on top. */
|
|
export const animEmberTwinkle = keyframes({
|
|
'0%': { opacity: '0.6' },
|
|
'50%': { opacity: '1' },
|
|
'100%': { opacity: '0.6' },
|
|
});
|