← All articles

· 1 min read

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.