Home / Snippets / Animation /

Morph transition

Morph between shapes on hover — square to circle, pentagon to diamond, and back.

Square
Circle
Pentagon
Widely Supported
animationno-js

Quick implementation

/* Square to circle morph */
.morph {
  width: 6rem;
  height: 6rem;
  border-radius: 0.5rem;
  background: oklch(0.55 0.18 265);
  transition: border-radius 0.6s cubic-bezier(0.4, 0, 0.2, 1),
              background 0.6s ease;
}

.morph:hover,
.morph:focus-visible {
  border-radius: 50%;
  background: oklch(0.60 0.20 310);
}

/* Clip-path morph: pentagon to diamond */
.morph-clip {
  clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
  background: oklch(0.55 0.18 25);
  transition: clip-path 0.6s cubic-bezier(0.4, 0, 0.2, 1),
              background 0.6s ease;
}

.morph-clip:hover,
.morph-clip:focus-visible {
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%);
  background: oklch(0.60 0.20 200);
}

@media (prefers-reduced-motion: reduce) {
  .morph,
  .morph-clip {
    transition-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 interactive shape animations.

Goal: Morph an element between two shapes on hover using CSS transitions — border-radius and clip-path approaches.

Technical constraints:
- border-radius morph: transition from a small radius to 50% for a square-to-circle effect.
- clip-path morph: use polygon() with the same number of points in both states so the browser can interpolate.
- Use cubic-bezier(0.4, 0, 0.2, 1) for a natural easing curve.
- Use oklch() for all color values — no hex or rgba().
- Include :focus-visible alongside :hover for keyboard users.

Framework variant (pick one):
A) Vanilla HTML + CSS only.
B) React component — accept shape prop ("square" | "circle" | "pentagon" | "diamond") and morph on hover.

Edge cases to handle:
- Respect prefers-reduced-motion: reduce duration to near-instant.
- clip-path polygon points must match count in both states, otherwise the browser can't interpolate.
- border-radius + width/height morph together triggers layout — prefer transform: scale() if performance is critical.

Return CSS only.

Why this matters in 2026

Shape morphing used to require SVG <animate> or JavaScript FLIP libraries. Modern CSS can transition border-radius and clip-path: polygon() natively, letting you morph between shapes with a single transition rule. This is especially powerful for avatar frames, icon states, and decorative elements that need to feel alive.

The logic

For border-radius morphs, the browser interpolates between numeric values — 0.5rem to 50% creates a smooth square-to-circle arc. For clip-path, both the start and end polygon() must have the same number of vertices. The browser interpolates each vertex independently, producing a fluid shape morph. Use cubic-bezier(0.4, 0, 0.2, 1) for a material-design deceleration curve that feels natural.

Accessibility & performance

clip-path transitions are paint-only and GPU-friendly. border-radius changes trigger paint but not layout (unless combined with size changes). Pair morphs with :focus-visible so keyboard users can trigger the effect. Always gate animations behind prefers-reduced-motion — shape changes can be disorienting for some users.