Morphing Shape
50 × 50 px, gradient #6366f1 → #d946ef, four shapes looping over 2 s: circle, square, two opposite diagonal leaves — each state turns a quarter turn. 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. Loaders has 29 effects, including 4 free. Explore the category →
3 usage examples



How it works
A single div.loader-morph of 50 × 50 px, painted by linear-gradient(135deg, #6366f1, #d946ef) (indigo to rose-violet), receives the animation morphLoader set to 2s ease-in-out infinite. No JavaScript, no extra element, no pseudo-element.
The @keyframes morphLoader defines four states: circle at 0 %/100 % (border-radius: 50%, rotate(0deg)); square at 25 % (border-radius: 0, rotate(90deg)); diagonal leaf A at 50 % (border-radius: 50% 0 — CSS shorthand for TL and BR rounded at 50%, TR and BL sharp —, rotate(180deg)); diagonal leaf B at 75 % (border-radius: 0 50% — TR and BL rounded at 50%, TL and BR sharp —, rotate(270deg)). Each shape state lands at an exact quarter-turn: the square at 90° sits flush with the axes, with no unwanted tilt. The ease-in-out timing holds each shape briefly before the next deformation.
Accessibility
- prefers-reduced-motion not handled: the animation runs continuously regardless of the user's preference. Add to your CSS:
@media (prefers-reduced-motion: reduce) { .loader-morph { animation: none; border-radius: 50%; } }— the element stays visible as a static colored disc. - Loading indicator role not declared: the code contains neither
role="status", noraria-busy, noraria-live, nor an accessible label. Wrap thedivin a container withrole="status" aria-live="polite" aria-label="Loading"(or setaria-busy="true"on the element it covers) so screen readers announce when loading starts and ends. - Contrast: #6366f1 on dark background #0a0a0f measures 4.4:1 and #d946ef measures 3.7:1 — both above the 3:1 threshold for graphical components (WCAG 1.4.11). On a light background both drop below 3:1; adapt the gradient or add a dark background to the container.
- Non-focusable element: the
divis not interactive and does not receive keyboard focus. No label is needed on it; the container carryingrole="status"holds the accessible name.
Browser compatibility
Requires unprefixed @keyframes, percentage border-radius, transform: rotate() and linear-gradient. Zero dependencies.
Without animation support (old browsers), the element is a fixed 50 × 50 px square with the indigo-violet gradient. Without linear-gradient (IE 8 and below), the element has no visible background.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="loader-morph"></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 |
|---|---|---|
Size (CSS — width and height) |
50px | Both values must stay equal to keep a square. At 20–24 px it fits inside a 44 px button; at 80 px it works as a large page-level indicator. |
Gradient colors (CSS — linear-gradient(135deg, #6366f1, #d946ef)) |
#6366f1 → #d946ef | Replace either or both hex values. A translucent white gradient works on a dark button; a flat color (#6366f1, #6366f1) removes the gradient entirely. |
Duration (CSS — animation-duration on .loader-morph) |
2s | 1 s = snappy pace for short waits (< 500 ms); 3 s = calm rhythm for longer generation. |
Timing (CSS — animation-timing-function on .loader-morph) |
ease-in-out | ease-in-out holds each shape briefly. linear gives a continuous, uniform motion — more mechanical. |
Square shape (CSS — border-radius: 0 at 25 %) |
0 (sharp corners) | Replace with 4px for a slightly rounded square — a perceptible intermediate between the full circle and the strict square. |
Intermediate shapes (CSS — 50% 0 at 50 %, 0 50% at 75 %) |
TL+BR rounded, then TR+BL rounded | Replace 50% 0 with 50% 10% to soften the sharp corners of the diagonal leaves. 30% 0 gives shallower, less pronounced leaves. |
| prefers-reduced-motion (CSS — to add) | absent from the code | Add @media (prefers-reduced-motion: reduce) { .loader-morph { animation: none; border-radius: 50%; } } to freeze the indicator on systems that request it. |
FAQ
border-radius on a variable-size interactive card — no rotation, click to expand, JavaScript required. fx-0431 is a 50 × 50 px loading indicator driven entirely by a CSS animation that coordinates border-radius and rotate() simultaneously, with no JavaScript and no interaction. Context (card vs. loader), scale and mechanics are all different.rotate(45deg) at 25 % — the square becomes a diamond and the diagonal leaves appear skewed.@media (prefers-reduced-motion: reduce) { .loader-morph { animation: none; border-radius: 50%; } }. The element stays visible as a fixed colored disc — presence confirmed without motion.