Morphing Shape
Four border-radius corners cycle through soft and extreme values in a continuous loop — an organic living shape, pure CSS, zero JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Cards has 41 effects, including 6 free. Explore the category →
3 usage examples



How it works
The animation is driven by @keyframes morphShape, which evolves border-radius through four states. CSS shorthand lets you address diagonal corner pairs in a single value: at 0%/100% (20px 60px), corners TL and BR are 20 px, TR and BL are 60 px; at 25% (60px 20px), the roles swap. The result is a diagonal rotation of the roundness.
Keyframes 50% (20px 20px 60px 60px) and 75% (60px 60px 20px 20px) address all four corners individually to shift the rounding from top to bottom then back. The ease-in-out curve over the 8 s infinite loop smooths each transition — the shape appears to breathe rather than snap.
The .card-morph-expand variant adds a click interaction: toggling the .expanded class via JavaScript scales the card to transform: scale(1.1) with a violet glow (box-shadow: rgba(99,102,241,.3) 0 25px 50px), fades the preview view to opacity: 0, and reveals full content positioned at position: absolute; inset: 0 through an opacity and transform: scale() transition.
Accessibility
- prefers-reduced-motion is absent from the code — the animation loops indefinitely even when the OS requests reduced motion. Add:
@media (prefers-reduced-motion: reduce) { .card-morph { animation: none; } } - The
<div class="card-morph">carries noroleoraria-label. For purely decorative use, addaria-hidden="true"; for interactive use, addrole="button"andaria-label. - The
.card-morph-expandvariant responds to clicks but does not definearia-expanded— screen readers cannot track the open/closed state. - Text contrast: the default 'Morph' label (
#ffffff) on--bg-card: #12121ayields a ratio > 7:1, meeting WCAG AAA. - The expand variant intercepts no
Enter/Spacekeydown and has notabindex— it is not keyboard-accessible in its base form.
Browser compatibility
Requires multi-value border-radius and CSS @keyframes — supported in all modern browsers since 2016.
Without @keyframes support, the card displays the static --radius-xl: 1rem radius (gently rounded corners) — no JS errors, content remains readable.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<div class="card-demo card-morph">
Content
</div>
</div>
<!-- Interactive variant: reveals content on click -->
<div class="card-demo card-morph-expand">
<div class="card-preview">
<div class="card-preview-icon"><!-- icon --></div>
</div>
<div class="card-full-content">
<h5>Title</h5>
<p>Description</p>
</div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| --bg-card | #12121a | Card background color. Use rgba() for a semi-transparent background that layers over a colored context. |
| --border | rgba(255,255,255,.08) | 1 px border color and opacity. Switch to rgba(99,102,241,.4) for a visible indigo outline. |
| animation-duration in .card-morph | 8s | Duration of one full cycle. 4s = snappy; 8s = natural; 14s = very slow and meditative. |
| Min value in @keyframes morphShape | 20px | Minimum corner radius at the 0%/25%/50%/75% keyframes. Reduce to 4px for a more angular morph. |
| Max value in @keyframes morphShape | 60px | Maximum corner radius. Push it to 120px or 50% for an extreme amplitude between near-rectangle and ellipse. |
| scale() in .card-morph-expand.expanded | scale(1.1) | Card zoom factor on click (expand variant). Lower to scale(1.04) for a subtle expansion. |
| box-shadow in .card-morph-expand.expanded | rgba(99,102,241,.3) 0 25px 50px | Glow color and spread on click. Replace rgba(99,102,241,.3) with your primary brand color. |
FAQ
.card-morph:hover { animation-play-state: paused; } to your CSS — the shape freezes instantly on its current position on hover, with no jump.overflow: hidden to the .card-morph element, then place an <img> inside with width:100%; height:100%; object-fit:cover;. The image corners will follow the organic shape at every frame.@media (prefers-reduced-motion: reduce) { .card-morph { animation: none; } }. The card remains visible with its initial --radius-xl: 1rem radius.