
Morphing the Path
Forget GreenSock; you can now transition complex SVG shapes directly in your stylesheet using the native CSS d property.
d: path("M10 80 Q 95 10 180 80") is no longer just a static attribute buried in your HTML; it is now a fully animatable CSS property. For years, if you wanted to morph a wavy line into a straight one, you reached for GSAP’s MorphSVGPlugin or some other heavy-duty library. Those tools are fantastic, but if you’re just looking to toggle a "Play" icon into a "Pause" icon, bringing in a 30kb library is like using a sledgehammer to hang a picture frame.
The Secret Sauce: The path() Function
The magic happens when you move the SVG path data out of the d="" attribute and into your stylesheet using the path() function.
Here is a dead-simple example of a hover effect that changes a triangle into a square:
<svg viewBox="0 0 100 100" class="morph-box">
<path class="shape" d="M20,80 L50,20 L80,80 Z" />
</svg>.shape {
fill: #6366f1;
transition: d 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.morph-box:hover .shape {
/* Note: The number of points must match! */
d: path("M20,80 L20,20 L80,20 L80,80 Z");
}By putting the path data in the CSS d property, the browser engine handles the interpolation. It calculates the midpoints for you, giving you that buttery-smooth morphing effect natively.
The "Golden Rule" of Native Morphing
I learned this the hard way: The number of nodes (points) in your start path and end path must be identical.
If your starting shape has four points and your ending shape has twenty, the browser gives up on the smooth transition and just "snaps" to the final state. It’s like trying to morph a square into a circle—the browser needs to know which corner goes where.
To solve this, I usually draw my "simplest" shape first, then for the more complex shape, I just stack extra points on top of each other in the same coordinate space. It’s a bit of a hack, but it keeps the path count consistent.
Practical Example: The Play-to-Pause Toggle
This is the classic use case. Instead of swapping icons and losing the visual continuity, we can morph the coordinates.
.icon-path {
/* Play Button Shape */
d: path("M20 10 L80 50 L20 90 Z");
transition: d 0.3s ease;
fill: #10b981;
}
.is-playing .icon-path {
/* Pause Button (Two rectangles, but drawn as one continuous path) */
/* This requires some clever path drawing to keep the point count the same! */
d: path("M20 10 L45 10 L45 90 L20 90 Z M55 10 L80 10 L80 90 L55 90 Z");
}Wait, did you catch that? I used multiple commands (M, L, Z) within the same path() string. As long as the structure matches in the transition state, the browser will slide those points right into place.
Morphing with Keyframes
You aren’t limited to simple transitions. You can use CSS @keyframes to create organic, "gooey" loading animations without touching a single line of JavaScript.
@keyframes blob-shift {
0%, 100% {
d: path("M25,50 Q25,25 50,25 T75,50 T50,75 T25,50");
}
50% {
d: path("M15,50 Q15,15 50,15 T85,50 T50,85 T15,50");
}
}
.loader-blob {
animation: blob-shift 3s infinite ease-in-out;
fill: #f43f5e;
}The "Gotchas" (Because there's always a catch)
1. Safari Support: For a long time, Safari was the holdout. However, recent versions have finally caught up. If you're supporting ancient browsers, you'll still need a polyfill or a library.
2. Coordinates: CSS d: path() expects the path to be defined in the same coordinate system as the SVG viewBox. If your SVG is 0 0 100 100, your path data points should stay within those bounds.
3. Complexity: If you are trying to morph a detailed illustration of a cat into a toaster, native CSS will likely fail you. This method is best for UI elements, icons, and simple decorative shapes. For the heavy lifting (like re-ordering points or auto-segmenting), GSAP is still king.
Why you should care
Performance and DX (Developer Experience).
When you use native CSS, the browser's compositor can optimize the animation. You're also shipping zero extra bytes of JavaScript to the client. It feels "cleaner" to have your visual states defined in your CSS rather than having JS manipulate DOM attributes every few milliseconds.
Next time you're building a menu toggle or a funky button hover, open your SVG in a text editor, grab that path data, and try moving it to your CSS. It’s surprisingly satisfying when it just *works*.


