Files
cinny/src/app/components/seasonal/themes/Halloween.css.ts
T

147 lines
5.3 KiB
TypeScript
Raw Normal View History

import { keyframes, style } from '@vanilla-extract/css';
/**
* Halloween overlay styles. Motion is transform/opacity only.
*
* [#239] Colours come from two custom properties set on the overlay root so
* the bats/spider stay dark silhouettes on light themes and turn to a pale
* dusk-violet on dark themes (a black bat on a black theme is invisible).
*/
export const halloweenRoot = style({
position: 'absolute',
inset: 0,
vars: {
'--hw-ink': 'oklch(0.24 0.04 300)',
'--hw-web': 'oklch(0.42 0.02 290 / 0.42)',
'--hw-eye': 'oklch(0.72 0.18 55)',
},
selectors: {
'body.dark-theme &, body.butter-theme &': {
vars: {
'--hw-ink': 'oklch(0.72 0.06 300)',
'--hw-web': 'oklch(0.85 0.02 290 / 0.36)',
'--hw-eye': 'oklch(0.8 0.17 60)',
},
},
},
});
/** A bat group crosses the top of the screen in ~9 s, then a long calm. */
export const animBatFlight = keyframes({
'0%': { transform: 'translate3d(-10vw, 0, 0)', opacity: '0' },
'1%': { opacity: '1' },
'29%': { opacity: '1' },
'30%': { transform: 'translate3d(110vw, -6vh, 0)', opacity: '0' },
'100%': { transform: 'translate3d(110vw, -6vh, 0)', opacity: '0' },
});
/** Up-and-down bobbing on each bat, so the group doesn't move as one block. */
export const animBatBob = keyframes({
'0%': { transform: 'translate3d(0, 0, 0)' },
'50%': { transform: 'translate3d(0, 14px, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
});
/** Wing beat: the silhouette squashes vertically around the body. */
export const animBatFlap = keyframes({
'0%': { transform: 'scaleY(1)' },
'50%': { transform: 'scaleY(0.62)' },
'100%': { transform: 'scaleY(1)' },
});
/** The spider bobs on its thread (thread and spider move together). */
export const animSpiderBob = keyframes({
'0%': { transform: 'translate3d(0, 0, 0)' },
'50%': { transform: 'translate3d(0, 18px, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
});
/** A slow sway of the spider itself on the end of the thread. */
export const animSpiderSway = keyframes({
'0%': { transform: 'rotate(-6deg)' },
'50%': { transform: 'rotate(6deg)' },
'100%': { transform: 'rotate(-6deg)' },
});
/**
* A small ghost rises from the lower part of the screen, drifting sideways,
* and fades out — ~8 s in a 50 s cycle.
*/
export const animGhost = keyframes({
'0%': { transform: 'translate3d(0, 0, 0) scale(0.9)', opacity: '0' },
'3%': { opacity: '0.8' },
'8%': { transform: 'translate3d(6vw, -20vh, 0) scale(1)' },
'13%': { opacity: '0.75' },
'16%': { transform: 'translate3d(-2vw, -42vh, 0) scale(1.05)', opacity: '0' },
'100%': { transform: 'translate3d(-2vw, -42vh, 0) scale(1.05)', opacity: '0' },
});
/** The ghost's tail wisps, a gentle wobble. */
export const animGhostWobble = keyframes({
'0%': { transform: 'rotate(-4deg)' },
'50%': { transform: 'rotate(4deg)' },
'100%': { transform: 'rotate(-4deg)' },
});
/** [#239] Will-o'-the-wisp: a slow wandering loop (paired with a pulse). */
export const animWispWander = keyframes({
'0%': { transform: 'translate3d(0, 0, 0)' },
'25%': { transform: 'translate3d(40px, -30px, 0)' },
'50%': { transform: 'translate3d(-10px, -60px, 0)' },
'75%': { transform: 'translate3d(-45px, -20px, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
});
/** Soft breathing glow of a wisp. */
export const animWispPulse = keyframes({
'0%': { opacity: '0.35', transform: 'scale(0.85)' },
'50%': { opacity: '0.9', transform: 'scale(1.1)' },
'100%': { opacity: '0.35', transform: 'scale(0.85)' },
});
/** A witch on a broom crosses high up, right to left, in ~11 s of 60 s. */
export const animWitch = keyframes({
'0%': { transform: 'translate3d(0, 0, 0) rotate(-4deg)', opacity: '0' },
'1%': { opacity: '1' },
'9%': { transform: 'translate3d(-60vw, -3vh, 0) rotate(3deg)' },
'17.5%': { opacity: '1' },
'18%': { transform: 'translate3d(-118vw, 2vh, 0) rotate(-3deg)', opacity: '0' },
'100%': { transform: 'translate3d(-118vw, 2vh, 0) rotate(-3deg)', opacity: '0' },
});
/**
* The big spider: bobs for most of the cycle, then climbs up its thread,
* pauses, and drops back with a little bounce (24 s cycle).
*/
export const animSpiderClimb = keyframes({
'0%': { transform: 'translate3d(0, 0, 0)' },
'20%': { transform: 'translate3d(0, 14px, 0)' },
'40%': { transform: 'translate3d(0, 0, 0)' },
'55%': { transform: 'translate3d(0, -22vh, 0)' },
'68%': { transform: 'translate3d(0, -22vh, 0)' },
'71%': { transform: 'translate3d(0, 24px, 0)' },
'74%': { transform: 'translate3d(0, -8px, 0)' },
'77%': { transform: 'translate3d(0, 6px, 0)' },
'80%': { transform: 'translate3d(0, 0, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
});
/** Jack-o'-lantern candle flicker (opacity of the glow behind the face). */
export const animCandle = keyframes({
'0%': { opacity: '0.75' },
'20%': { opacity: '1' },
'35%': { opacity: '0.6' },
'55%': { opacity: '0.95' },
'70%': { opacity: '0.7' },
'100%': { opacity: '0.75' },
});
/** The swarm crosses faster than the regular bats: ~7 s of a 60 s cycle. */
export const animSwarmFlight = keyframes({
'0%': { transform: 'translate3d(-10vw, 0, 0)', opacity: '0' },
'0.5%': { opacity: '1' },
'11.5%': { opacity: '1' },
'12%': { transform: 'translate3d(110vw, -8vh, 0)', opacity: '0' },
'100%': { transform: 'translate3d(110vw, -8vh, 0)', opacity: '0' },
});