The animation system in Choose Your Destiny is layered across three distinct technologies: raw CSSDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/choose-your-destiny/llms.txt
Use this file to discover all available pages before exploring further.
@keyframes for the purely cosmetic CRT effects, CSS custom properties for ambient background motion, and Framer Motion for all state-driven UI transitions. The CSS animations are designed to feel broken by default — jagged clip masks, stepped opacity blinks, and slow phosphor flicker — while the Framer Motion layers supply the smooth, physics-aware responses that make interactions feel polished beneath the glitch veneer. Understanding which layer does what is the key to extending the system without fighting it.
GlitchText
GlitchText (exported from VisualEffects.js) renders an arcade-font heading that appears to corrupt and reassemble itself continuously. The effect is achieved entirely with CSS — no JavaScript RAF loops or canvas drawing.
How it works
The component renders the text once as visible content, then duplicates it twice using:before and :after pseudo-elements that read the same string from a data-text attribute. Both copies sit position: absolute over the original, each clipped to a narrow horizontal slice via the clip: rect(...) property. Their clip rectangles animate through 21 distinct positions at different rates and with alternate-reverse direction, ensuring the before/after layers are rarely in sync with each other or with the original — producing the characteristic fragmented corruption look.
The :before layer uses a magenta text-shadow offset of -2px 0, the :after layer uses cyan at the same offset, mirroring the chromatic aberration of a misaligned CRT electron gun.
CSS keyframes
JSX usage
The
text prop populates both the visible text content and the data-text attribute that pseudo-elements read via attr(data-text). If you change the displayed text after mount (e.g. from an animation), make sure data-text stays in sync — otherwise the glitch layers will show stale content.ScanlineOverlay
ScanlineOverlay (exported from VisualEffects.js) renders a fixed full-viewport layer at z-50 that overlays every element on the page with a horizontal scanline pattern. It simulates the horizontal phosphor line structure of a CRT display.
How it works
The overlay is a singlediv with position: fixed; inset: 0; pointer-events: none so it never captures mouse or touch events. Its background is a CSS linear-gradient that alternates between transparent and a semi-transparent black at every 2px interval, producing dark horizontal bands across the full screen.
.scanlines at opacity-20, keeping the effect subtle enough to not obscure underlying content while still communicating the CRT texture at a glance.
JSX usage
CRT Flicker
Alongside the scanline overlay sits a secondfixed layer (exported as CRTFlicker from VisualEffects.js) that drives a slow, breathing opacity oscillation. While scanlines provide the static grid texture, the flicker overlay simulates the low-frequency brightness instability of an aging cathode-ray tube.
CSS animation
ease-in-out) so it reads as an ambient quality rather than a distracting strobe. The bg-white/[0.015] background color on the overlay element itself provides a barely-there brightening at full opacity, which dims to near-nothing at the 50% trough.
PerspectiveGrid
PerspectiveGrid (exported from VisualEffects.js) renders the animated receding grid that appears at the bottom of several sections. It recreates the classic synthwave horizon floor using only CSS transforms and an infinitely scrolling background-position.
How it works
PerspectiveGrid renders as a fixed full-width overlay anchored to the bottom of the viewport (fixed bottom-0 left-0 w-full h-[40vh]). Inside it, a 200% × 200% div is offset left by -50% to center the grid beyond the visible viewport edges. The inner element has a CSS perspective transform applied that rotates it flat toward the viewer, and an infinite background-position animation that moves the grid lines downward, creating the illusion of flying forward through a neon grid tunnel.
50px end value in grid-move matches the background-size cell height exactly, so the loop is perfectly seamless.
Color variants
PerspectiveGrid accepts a color prop that selects one of three preset grid line RGBA values:
JSX usage
BlinkingCursor
BlinkingCursor (from UIComponents.js) uses a step-end keyframe rather than the default ease, making the opacity change instantaneous at the 50% frame boundary — a binary on/off flick with no fade.
step-end timing function is what distinguishes this from a pulse animation. With ease or linear, opacity would ramp smoothly; step-end holds at opacity: 1 for the full first half of each second, then snaps to opacity: 0 at the 50% frame, holding there until the cycle resets at 100%. This matches the exact behavior of a hardware terminal cursor.
Framer Motion Animations
Beyond CSS, Framer Motion handles all state-driven UI motion: skill bar width reveals, stage card entry, and HP bar drain in the hero section.Skill Bar Width Animation
Skill bars animate fromwidth: 0% to their target percentage when they enter the viewport. Framer Motion’s whileInView trigger fires the animation lazily, so bars that haven’t scrolled into view remain at zero until needed.
Stage Reveal (Opacity + X Slide)
Section content panels enter from the right with a combinedopacity fade and horizontal x slide. The x displacement is typically 40px–60px, giving the impression of content being rendered in from off-screen.
HP Bar Drain
The hero HP bar drains from full to the player’s effective HP level on mount, producing a game-boot feel. The animation uses a negativewidth transition starting at 100% and settling at the remaining-HP percentage.
Performance Notes
All four CSS animations that run continuously are optimised for GPU compositing:- will-change
- backface-visibility
- translateZ(0)
Both
.crt-flicker and .perspective-grid declare will-change on the property they animate. This tells the browser’s compositor to promote the element to its own layer before the animation begins, avoiding expensive paint operations on every frame.The
.glitch-text and .blinking-cursor animations intentionally omit will-change. Glitch clips change rapidly enough that the overhead of a dedicated layer would outweigh the benefit, and the blinking cursor is a tiny 3×5 px element where layer promotion would waste memory. Only promote elements to their own compositor layer when the animation runs on a large surface or at a high frame rate.