Choreographing an illustrated hero with plain CSS
How the hero on this site assembles itself in stages using one SVG, a handful of keyframes and no animation library.
The illustration on the home page is not a single flattened image. It is one SVG split into named layers — the desk, the character, the wall art and the desk accessories — and each layer is a small Angular component rendered inside a shared coordinate system.
One timeline, many layers
Every layer uses the same keyframes. What changes is a set of custom properties: a delay, a duration and the direction an object travels from. Staggered items such as sticky notes receive an index via --i, so their delay is a single calc() expression.
.background-art .bg-item {
animation: hero-enter 450ms var(--ease-out) both;
animation-delay: calc(1100ms + var(--i) * 75ms);
}Only opacity and transform are animated, so the browser never has to recalculate layout. The SVG reserves its space with a fixed viewBox and aspect-ratio, which means nothing shifts while the pieces arrive.
Playing once, even with SSR
Because the page is server-rendered, the animation starts as soon as the HTML is painted, before any JavaScript runs. Hydration reuses the existing DOM, so the sequence is not interrupted. A tiny root service records that the entrance has played; returning to the home page later shows the finished scene instead of replaying it.
Respecting reduced motion
- Under
prefers-reduced-motion: reduce, every entrance and idle animation is disabled. - Nothing is hidden by default — elements are only transparent while an animation is active, so content stays visible if animations never run.
- After the entrance, only a few subtle idle details move: a blink, some coffee steam and a gently swaying plant.