6db07f1371
Adds 11 CSS-only seasonal overlays (Halloween, Christmas, New Year, Autumn, April Fool's, Lunar New Year, Valentine's Day, St. Patrick's Day, Earth Day, Deep Space, Retro Arcade) with date-based auto-detection and a manual override dropdown in Settings → Appearance → Seasonal Theme. All themes respect prefers-reduced-motion. SeasonalEffect mounts at z-index 9997 in App.tsx. Also rewrites all 5 animated chat background keyframes for smoother, more organic motion: Digital Rain gains a phosphor glow flicker; Star Drift now loops each layer by exactly its own tile size (no more seam); Grid Pulse adds an independent brightness oscillation at a prime period; Aurora Flow drives all four gradient layers through distinct paths; Fireflies adds glow-pulse and opacity-blink animations at prime periods for unsynchronised bioluminescence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
137 lines
5.4 KiB
TypeScript
137 lines
5.4 KiB
TypeScript
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' },
|
|
});
|