Morphing SVG Icons
Animated icons that morph between two states—menu ↔ close, play ↔ pause, heart ↔ broken heart. Uses SVG path animations with smooth transitions and interactive click/hover triggers for a modern, sleek UI.
Build animated icons that morph smoothly between two states—perfect for navigation menus, media controls, and social interactions. This snippet uses SVG path animations with CSS transitions and vanilla JavaScript for lightweight, performant morphing effects.
Features
- Menu ↔ Close: Hamburger icon transforms into an X with smooth line rotation and translation.
- Play ↔ Pause: Triangle morphs into pause bars with crossfade and scale transitions.
- Heart ↔ Broken Heart: SVG path morphing between filled heart and broken heart states.
- Interactive: Click or hover to trigger morphing—fully customizable triggers.
- Lightweight: Pure CSS + minimal vanilla JS—no external animation libraries required.
HTML Structure
Each icon is a clickable button containing an SVG. Use data-state or a class to track the current state for toggling.
<div class="morph-icons-grid">
<!-- Menu ↔ Close -->
<button class="morph-icon" id="menu-icon" aria-label="Toggle menu">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line class="line line-top" x1="3" y1="6" x2="21" y2="6"/>
<line class="line line-mid" x1="3" y1="12" x2="21" y2="12"/>
<line class="line line-bot" x1="3" y1="18" x2="21" y2="18"/>
</svg>
</button>
<!-- Play ↔ Pause -->
<button class="morph-icon" id="play-icon" aria-label="Play/Pause">
<svg viewBox="0 0 24 24">
<path class="play-path" d="M8 5v14l11-7z"/>
<g class="pause-group">
<rect class="pause-bar" x="6" y="4" width="4" height="16" rx="1"/>
<rect class="pause-bar" x="14" y="4" width="4" height="16" rx="1"/>
</g>
</svg>
</button>
<!-- Heart ↔ Broken Heart -->
<button class="morph-icon" id="heart-icon" aria-label="Like">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path class="heart-path" d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
<path class="broken-path" d="M4.84 4.61a5.5 5.5 0 0 0 0 7.78l1.06 1.06L12 21.23l2.91-2.91-4.5-4.5 2.5-2.5-4.5-4.5-2.5 2.5-2.06-2.06a5.5 5.5 0 0 0-7.78-7.78l1.06 1.06z"/>
</svg>
</button>
</div>
CSS: Menu ↔ Close (Hamburger to X)
The hamburger uses three lines. When active, the top and bottom lines rotate and translate to form an X; the middle line fades out.
.morph-icon {
width: 48px;
height: 48px;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
cursor: pointer;
transition: background 0.3s, border-color 0.3s;
}
.morph-icon svg {
width: 100%;
height: 100%;
display: block;
}
.morph-icon .line {
transform-origin: center;
transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.25s ease;
}
.morph-icon.active .line-top {
transform: translateY(6px) rotate(45deg);
}
.morph-icon.active .line-mid {
opacity: 0;
}
.morph-icon.active .line-bot {
transform: translateY(-6px) rotate(-45deg);
}
CSS: Play ↔ Pause
Use two overlapping groups—play triangle and pause bars—and toggle visibility with opacity and scale.
.morph-icon .play-path {
transform-origin: center;
transition: opacity 0.3s, transform 0.3s;
}
.morph-icon .pause-group {
opacity: 0;
transform: scale(0.8);
transform-origin: center;
transition: opacity 0.3s, transform 0.3s;
pointer-events: none;
}
.morph-icon.active .play-path {
opacity: 0;
transform: scale(0.8);
}
.morph-icon.active .pause-group {
opacity: 1;
transform: scale(1);
}
CSS: Heart ↔ Broken Heart
Two paths overlap. Morph by transitioning opacity so one fades in as the other fades out, creating a smooth crossfade effect.
.morph-icon .heart-path,
.morph-icon .broken-path {
transition: opacity 0.4s ease, stroke 0.3s;
}
.morph-icon .broken-path {
opacity: 0;
stroke: #ef4444;
}
.morph-icon.active .heart-path {
opacity: 0;
}
.morph-icon.active .broken-path {
opacity: 1;
}
JavaScript: Toggle on Click
Attach click handlers to toggle the active class on each button.
document.querySelectorAll('.morph-icon').forEach(btn => {
btn.addEventListener('click', () => btn.classList.toggle('active'));
});
For hover-triggered morphing instead of click, use mouseenter and mouseleave:
document.querySelectorAll('.morph-icon').forEach(btn => {
btn.addEventListener('mouseenter', () => btn.classList.add('active'));
btn.addEventListener('mouseleave', () => btn.classList.remove('active'));
});
Resources
- Demo: Use the Demo button to see all morphing icons in action with click and hover interactions.
- Download: Use the Download button to get the full source (HTML, CSS, JS) for the morphing SVG icons collection.
Tips
- Adjust
transitiondurations andcubic-beziercurves to match your app’s motion design. - Use
strokeandfilltransitions for color feedback (e.g., heart turns red when “broken”). - Add
aria-pressedoraria-expandedfor accessibility when toggling states. - For more complex path morphing (same number of path commands), consider libraries like GSAP MorphSVG or anime.js for advanced interpolation.