Home / Snippets / Animation /

Hero morph transition

Toggle between compact and full hero layouts — the avatar and text morph smoothly between positions.

Jane Designer

Product designer crafting interfaces that feel natural and delightful.

Jane Designer

Product designer crafting interfaces that feel natural and delightful. Currently at Acme Corp, previously at Design Studio. Specializing in design systems and motion.

Experimental
animationexperimental

Quick implementation

/* Hero morph with View Transitions */
.hero-avatar {
  view-transition-name: hero-avatar;
}

.hero-title {
  view-transition-name: hero-title;
}

/* Compact layout */
.hero--compact .hero-avatar {
  width: 4rem;
  height: 4rem;
}

/* Full layout */
.hero--full .hero-avatar {
  width: 8rem;
  height: 8rem;
}

/* Custom morph timing */
::view-transition-group(hero-avatar),
::view-transition-group(hero-title) {
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Trigger: document.startViewTransition(() => toggle()) */

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-duration: 0.01s;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building hero section layouts.

Goal: Morph a hero section between compact (inline, small avatar) and full (stacked, large avatar) layouts with animated transitions using the View Transitions API.

Technical constraints:
- Assign view-transition-name to the avatar and heading so they morph independently.
- Use ::view-transition-group() to customize animation timing with cubic-bezier(0.4, 0, 0.2, 1).
- Use oklch() for all color values — no hex or rgba().
- Duration 0.4–0.6s for the morph animation.
- Trigger via document.startViewTransition() wrapping a class toggle.

Framework variant (pick one):
A) Vanilla JS — toggle .hero--compact / .hero--full class inside startViewTransition callback.
B) React — use startViewTransition in a state setter, conditionally render compact or full layout.

Edge cases to handle:
- Respect prefers-reduced-motion: set animation-duration to near-instant.
- If View Transitions aren't supported, the class toggle still works — layout just snaps.
- Each view-transition-name must be unique per page — avoid collisions with other named elements.

Return HTML + CSS + JS.

Why this matters in 2026

Hero sections often need to adapt between states — scrolling from a full splash to a compact header, toggling between profile views, or switching layout modes. The View Transitions API lets you assign view-transition-name to individual elements within the hero, and the browser automatically morphs their size and position between states. This replaces complex FLIP calculations with a few lines of CSS.

The logic

Give the avatar and heading each a unique view-transition-name. When you toggle the layout class inside document.startViewTransition(), the browser snapshots both elements at their old size/position, applies the new class, then creates ::view-transition-group(name) pseudo-elements that interpolate between the two states. The avatar smoothly resizes from 4rem to 8rem while sliding to its new position, and the heading repositions accordingly — all GPU-composited.

Accessibility & performance

The morph animation runs on composited layers — no layout recalculation during the transition. The content remains in the DOM and accessible throughout. Gate custom timing behind prefers-reduced-motion — users who disable motion still see the layout change, just without animation. Ensure the hero content is readable in both compact and full states, with sufficient contrast and text sizing.