diff --git a/src/app/components/seasonal/Seasonal.css.ts b/src/app/components/seasonal/Seasonal.css.ts deleted file mode 100644 index bf897133e..000000000 --- a/src/app/components/seasonal/Seasonal.css.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { keyframes } from '@vanilla-extract/css'; - -/** Generic fall: particles drop from top to bottom with a slight rotate. */ -export const animSeasonFall = keyframes({ - '0%': { transform: 'translateY(-20px) translateX(0) rotate(0deg)', opacity: '0' }, - '5%': { opacity: '1' }, - '90%': { opacity: '0.8' }, - '100%': { transform: 'translateY(110vh) translateX(25px) rotate(360deg)', opacity: '0' }, -}); - -/** Leaf fall: exaggerated horizontal sway as the leaf tumbles down. */ -export const animLeafFall = keyframes({ - '0%': { transform: 'translateY(-20px) translateX(0) rotate(-20deg)', opacity: '0' }, - '8%': { opacity: '0.85' }, - '25%': { transform: 'translateY(25vh) translateX(35px) rotate(40deg)' }, - '50%': { transform: 'translateY(50vh) translateX(-25px) rotate(130deg)' }, - '75%': { transform: 'translateY(75vh) translateX(45px) rotate(260deg)' }, - '92%': { opacity: '0.6' }, - '100%': { transform: 'translateY(110vh) translateX(5px) rotate(380deg)', opacity: '0' }, -}); - -/** Float up: hearts / embers rise from the bottom. */ -export const animFloatUp = keyframes({ - '0%': { transform: 'translateY(0) scale(0.6) translateX(0)', opacity: '0' }, - '8%': { opacity: '0.9' }, - '50%': { transform: 'translateY(-50vh) scale(1) translateX(15px)' }, - '85%': { opacity: '0.4' }, - '100%': { transform: 'translateY(-105vh) scale(1.3) translateX(-10px)', opacity: '0' }, -}); - -/** Bob: lanterns gently rise and fall with a slight tilt. */ -export const animBob = keyframes({ - '0%': { transform: 'translateY(0px) rotate(-4deg)' }, - '50%': { transform: 'translateY(-18px) rotate(4deg)' }, - '100%': { transform: 'translateY(0px) rotate(-4deg)' }, -}); - -/** Lantern tassel sway (used on the tassel element only). */ -export const animTasselSway = keyframes({ - '0%': { transform: 'rotate(-8deg)' }, - '50%': { transform: 'rotate(8deg)' }, - '100%': { transform: 'rotate(-8deg)' }, -}); - -/** Glitch jitter: rapid position jumps that feel like a signal error. */ -export const animGlitch = keyframes({ - '0%': { transform: 'translate(0, 0)' }, - '2%': { transform: 'translate(-4px, 2px)' }, - '4%': { transform: 'translate(4px, -2px)' }, - '6%': { transform: 'translate(0, 0)' }, - '48%': { transform: 'translate(0, 0)' }, - '50%': { transform: 'translate(3px, -3px)' }, - '52%': { transform: 'translate(-3px, 3px)' }, - '54%': { transform: 'translate(0, 0)' }, - '78%': { transform: 'translate(0, 0)' }, - '80%': { transform: 'translate(-5px, 1px)' }, - '82%': { transform: 'translate(0, 0)' }, - '100%': { transform: 'translate(0, 0)' }, -}); - -/** Glitch color: hue + saturation spikes that look like a corrupted signal. */ -export const animGlitchColor = keyframes({ - '0%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '8%': { filter: 'hue-rotate(180deg) saturate(3)' }, - '9%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '55%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '57%': { filter: 'hue-rotate(90deg) saturate(2)' }, - '58%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '80%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '82%': { filter: 'hue-rotate(270deg) saturate(2.5)' }, - '83%': { filter: 'hue-rotate(0deg) saturate(1)' }, - '100%': { filter: 'hue-rotate(0deg) saturate(1)' }, -}); - -/** Glitch scanline: a horizontal band sweeps across, flickering. */ -export const animGlitchScan = keyframes({ - '0%': { transform: 'translateY(-100%)' }, - '100%': { transform: 'translateY(100vh)' }, -}); - -/** Burst: circle expands outward from a point and fades — firework petal. */ -export const animBurst = keyframes({ - '0%': { transform: 'scale(0) rotate(0deg)', opacity: '1' }, - '50%': { opacity: '0.7' }, - '100%': { transform: 'scale(1) rotate(45deg)', opacity: '0' }, -}); - -/** Firework trail: a small dot rockets upward before bursting. */ -export const animRocket = keyframes({ - '0%': { transform: 'translateY(0)', opacity: '1' }, - '100%': { transform: 'translateY(-40vh)', opacity: '0' }, -}); - -/** Deep space warp: stars streak from center outward. */ -export const animWarp = keyframes({ - '0%': { transform: 'scale(0.05) translate(0, 0)', opacity: '0' }, - '10%': { opacity: '1' }, - '100%': { transform: 'scale(4) translate(0, 0)', opacity: '0' }, -}); - -/** Arcade scanline flicker. */ -export const animScanline = keyframes({ - '0%': { opacity: '0.12' }, - '50%': { opacity: '0.04' }, - '100%': { opacity: '0.12' }, -}); - -/** Arcade pixel blink: decorative corner glyphs blink. */ -export const animPixelBlink = keyframes({ - '0%, 49%': { opacity: '1' }, - '50%, 100%': { opacity: '0' }, -}); - -/** Gold shimmer: a shine sweeps across a metallic surface. */ -export const animGoldShimmer = keyframes({ - '0%': { backgroundPosition: '-300% 0' }, - '100%': { backgroundPosition: '300% 0' }, -}); - -/** Clover drift: gentle fall with a slow spin. */ -export const animCloverDrift = keyframes({ - '0%': { transform: 'translateY(-20px) rotate(0deg)', opacity: '0' }, - '5%': { opacity: '0.7' }, - '90%': { opacity: '0.5' }, - '100%': { transform: 'translateY(110vh) rotate(720deg)', opacity: '0' }, -}); - -/** Earth Day leaf sway: gentle horizontal oscillation for ambient leaf particles. */ -export const animEarthLeafDrift = keyframes({ - '0%': { transform: 'translateY(-10px) translateX(0) rotate(0deg)', opacity: '0' }, - '8%': { opacity: '0.6' }, - '30%': { transform: 'translateY(30vh) translateX(20px) rotate(90deg)' }, - '60%': { transform: 'translateY(60vh) translateX(-15px) rotate(200deg)' }, - '90%': { opacity: '0.4' }, - '100%': { transform: 'translateY(110vh) translateX(10px) rotate(340deg)', opacity: '0' }, -}); diff --git a/src/app/components/seasonal/SeasonalEffect.tsx b/src/app/components/seasonal/SeasonalEffect.tsx index 7a968c870..a49c76278 100644 --- a/src/app/components/seasonal/SeasonalEffect.tsx +++ b/src/app/components/seasonal/SeasonalEffect.tsx @@ -4,682 +4,23 @@ import { settingsAtom } from '../../state/settings'; import { zIndices } from '../../styles/zIndex'; import { SeasonTheme } from './types'; import { getActiveSeason } from './seasonSchedule'; -import { - animSeasonFall, - animLeafFall, - animFloatUp, - animBob, - animTasselSway, - animGoldShimmer, - animCloverDrift, - animEarthLeafDrift, - animWarp, - animScanline, - animPixelBlink, -} from './Seasonal.css'; +import { HalloweenOverlay } from './themes/Halloween'; +import { ChristmasOverlay } from './themes/Christmas'; +import { NewYearOverlay } from './themes/NewYear'; +import { AutumnOverlay } from './themes/Autumn'; +import { AprilFoolsOverlay } from './themes/AprilFools'; +import { LunarNewYearOverlay } from './themes/LunarNewYear'; +import { ValentinesOverlay } from './themes/Valentines'; +import { StPatricksOverlay } from './themes/StPatricks'; +import { EarthDayOverlay } from './themes/EarthDay'; +import { DeepSpaceOverlay } from './themes/DeepSpace'; +import { ArcadeOverlay } from './themes/Arcade'; // SeasonTheme + the date-window logic now live in leaf modules (single source // of truth, shared with the settings UI). Re-exported here for existing // importers that still reach for it from this file. export type { SeasonTheme }; -// ─── Individual theme overlays ──────────────────────────────────────────────── - -function HalloweenOverlay({ reduced }: { reduced: boolean }) { - const particles = Array.from({ length: 22 }); - return ( - <> - {/* Dark purple ambient tint */} -