feat(seasonal): redesign all 11 seasonal themes as modular per-theme overlays
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>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { keyframes } from '@vanilla-extract/css';
|
||||
|
||||
/**
|
||||
* Earth Day overlay keyframes. Every animation touches ONLY `transform` and
|
||||
* `opacity` so the compositor can run them on the GPU — no layout/paint thrash.
|
||||
* keyframes() returns the generated animation-name string, applied inline.
|
||||
*
|
||||
* Motif: verdant, hopeful nature. Leaves tumble, seeds/spores drift, pollen
|
||||
* motes glow and pulse, soft sun rays breathe from above, the blue-marble
|
||||
* Earth gently respires in a corner.
|
||||
*/
|
||||
|
||||
/** Falling leaf: tumbles down with a wide pendular sway and slow spin. */
|
||||
export const animLeafTumble = keyframes({
|
||||
'0%': { transform: 'translate3d(0, -10vh, 0) rotate(-18deg)', opacity: '0' },
|
||||
'8%': { opacity: '0.7' },
|
||||
'28%': { transform: 'translate3d(4vw, 22vh, 0) rotate(60deg)' },
|
||||
'52%': { transform: 'translate3d(-3vw, 48vh, 0) rotate(165deg)' },
|
||||
'76%': { transform: 'translate3d(5vw, 74vh, 0) rotate(280deg)' },
|
||||
'90%': { opacity: '0.5' },
|
||||
'100%': { transform: 'translate3d(1vw, 112vh, 0) rotate(360deg)', opacity: '0' },
|
||||
});
|
||||
|
||||
/** Tiny seed / spore: drifts slowly downward, swaying like dandelion fluff. */
|
||||
export const animSeedDrift = keyframes({
|
||||
'0%': { transform: 'translate3d(0, -6vh, 0) rotate(0deg)', opacity: '0' },
|
||||
'12%': { opacity: '0.55' },
|
||||
'40%': { transform: 'translate3d(3vw, 34vh, 0) rotate(140deg)' },
|
||||
'70%': { transform: 'translate3d(-2.5vw, 64vh, 0) rotate(250deg)' },
|
||||
'88%': { opacity: '0.4' },
|
||||
'100%': { transform: 'translate3d(2vw, 110vh, 0) rotate(360deg)', opacity: '0' },
|
||||
});
|
||||
|
||||
/** Pollen mote: floats gently upward in a soft serpentine path. */
|
||||
export const animPollenFloat = keyframes({
|
||||
'0%': { transform: 'translate3d(0, 0, 0) scale(0.75)', opacity: '0' },
|
||||
'14%': { opacity: '0.9' },
|
||||
'38%': { transform: 'translate3d(10px, -22vh, 0) scale(1)' },
|
||||
'64%': { transform: 'translate3d(-10px, -46vh, 0) scale(0.92)', opacity: '0.7' },
|
||||
'90%': { opacity: '0.2' },
|
||||
'100%': { transform: 'translate3d(6px, -72vh, 0) scale(0.7)', opacity: '0' },
|
||||
});
|
||||
|
||||
/** Soft brightness twinkle layered on each pollen mote's glow. */
|
||||
export const animPollenGlow = keyframes({
|
||||
'0%': { opacity: '0.55' },
|
||||
'50%': { opacity: '1' },
|
||||
'100%': { opacity: '0.55' },
|
||||
});
|
||||
|
||||
/** Sun rays from above: slow breathing of opacity + a faint scale shimmer. */
|
||||
export const animRayBreathe = keyframes({
|
||||
'0%': { transform: 'scaleY(1)', opacity: '0.4' },
|
||||
'50%': { transform: 'scaleY(1.05)', opacity: '0.7' },
|
||||
'100%': { transform: 'scaleY(1)', opacity: '0.4' },
|
||||
});
|
||||
|
||||
/** Green aurora veil: a wide, slow horizontal sway with a gentle swell. */
|
||||
export const animAuroraSway = keyframes({
|
||||
'0%': { transform: 'translate3d(-6%, 0, 0) scale(1.1)', opacity: '0.45' },
|
||||
'50%': { transform: 'translate3d(6%, -2%, 0) scale(1.2)', opacity: '0.7' },
|
||||
'100%': { transform: 'translate3d(-6%, 0, 0) scale(1.1)', opacity: '0.45' },
|
||||
});
|
||||
|
||||
/** Blue-marble Earth: a barely-perceptible respiration of its halo. */
|
||||
export const animEarthRespire = keyframes({
|
||||
'0%': { transform: 'scale(1)', opacity: '0.85' },
|
||||
'50%': { transform: 'scale(1.04)', opacity: '1' },
|
||||
'100%': { transform: 'scale(1)', opacity: '0.85' },
|
||||
});
|
||||
Reference in New Issue
Block a user