Site Slap Devlog #3 (the hero that died twice before it shipped)
TL;DR
- fire skin is done. the third and final tier, dark cinematic, animated aurora hero in pure css
- the original plan was an ai-generated image hero, then an ai video. both died. the final version uses zero assets
- three motion moments: staggered word reveal on load, score count-up, roast lines fading in on scroll
- one bug in the results css: rem where em should be. the “/100” rendered at 4 pixels tall
the plan kept dying
fire was supposed to have a generated image hero. i made the image, it came out painterly instead of real, i liked it anyway. then i tried animating it into a looping video. the videos came out static, then the download button stopped working, and my credits were almost gone.
so i dropped the whole thing.
the replacement is an aurora effect built entirely in css. no image, no video, no library, nothing to download. rose, amber and teal glows drifting over near-black. it loads instantly and it can’t break.
how the aurora works
two repeating linear gradients stacked on one div. the first paints thin diagonal stripes of rose, amber and teal. the second paints stripes of the background color on top, so it cuts gaps into the first one.
then filter: blur(42px) melts the stripes into soft bands of light.
the movement is one keyframe. the background is sized at 300% width, and background-position slides from 50% to 350% over 26 seconds on a loop. the stripes drift sideways like slow northern lights. that’s the entire trick, one animated property, gpu-composited, no jank.
the motion
the headline is five words, each wrapped in its own span with a staggered animation-delay. they rise 28px and fade in one after another on load.
when results land, the page auto-scrolls down and the score counts up from 0 with a requestAnimationFrame loop and a cubic ease-out, so it sprints early and settles slow.
the roast and fix lines start invisible. an IntersectionObserver adds a class when each one enters the viewport, staggered 80ms apart.
it works.
what i learned
em and rem are not interchangeable, and the difference is exactly one letter.
the giant score is clamped up to 168px. next to it, “/100” is sized at 0.28em, which means 28% of the parent’s font size, so it scales with the score. i typed 0.28rem instead. rem means 28% of the root font size, 16px. so “/100” rendered at about 4.5 pixels. invisible.
em looks at the parent. rem looks at the page. one letter, 40 pixel difference.
also, prefers-reduced-motion has a trap. if you just set animation: none on elements that start at opacity 0, they stay at opacity 0 forever. the media query has to force opacity: 1 back on, or reduced-motion users get a blank page.
Tip of the Day
if your css animation “isn’t working”, check whether it already finished. a mount animation runs once in the first second. if you looked away, you missed it, and the end state looks identical to no animation at all.
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.