Glassmorphism✨ Premium

Morphing Corner Shapes

Four 100 px pieces — superellipse, bevel, notch, scoop — that, on hover, really turn into the next shape over 0.6 s with a slight bounce and a 5° rotation: four clip-path: polygon() of 68 points each, in the same order, which the browser interpolates point by point.

CSSTransitionClip-path

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →

3 usage examples

Pieces of a shape puzzle game — browser game — Morphing Corner Shapes example 1

① Pieces of a shape puzzle game — browser game

WhenLevel 12, three moves left: a dotted target row (bevel, notch, hollow, rounded) and four pieces the player hovers to make them take the next shape.
WhyThe shape change is the game mechanic, not decoration: the player sees the piece move from one cut to the next and learns the order of the cycle (rounded → bevel → notch → hollow → rounded). With a snapping change, they would only see a jump.
SettingsPieces enlarged to 112 px, letters A to D at 24 px; two starting pieces as superellipses so one has to be turned; 112 × 62 px dotted target slots, the solved one in solid green.
Choosing the shape of a custom pin — a manufacturer's configurator — Morphing Corner Shapes example 2

② Choosing the shape of a custom pin — a manufacturer's configurator

WhenStep 2 of 4 of the configurator: the association's enamel pin is shown in its four cuts; the customer hovers each to see the next one.
WhySeeing one cut morph into the next on hover avoids going back and forth between previews. The pin's design (anchor, enamel ring, gold rim) stays still while the outline changes around it: exactly what the customer compares.
Settings104 px pieces with a single radial-gradient background (blue enamel, gold rim) also applied on hover, so only the shape changes; name of each cut under the piece.
Choosing a label die-cut — online print shop — Morphing Corner Shapes example 3

③ Choosing a label die-cut — online print shop

WhenThe customer ordering 250 jam labels (mirabelle, quince, rhubarb, blackberry) hovers the proposed cuts, on their own design.
WhyEach hover shows the next cut on the label itself: corner shapes are compared without opening a preview. The 0.6 s morph leaves time to see where the cut bites into the label's frame.
Settings108 px pieces with a cream background and a thin frame in radial-gradient, burgundy serif text; same background on hover (only shape and rotation change); wooden table drawn in gradients.

How it works

Each .morph-shape (100 × 100 px, 135° gradient, white uppercase 10.4 px label) carries four variables: --msh-squircle (superellipse n = 4), --msh-bevel (24% cut corners), --msh-notch (18% square notch) and --msh-scoop-concave (24% scooped quarter circle). Each one is a polygon() of 68 points in percentages, 17 per corner, in the same order: from the left edge to the top edge, then the other three corners by rotation. When :hover swaps one variable for the next, transition: clip-path 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) moves each point towards its counterpart: the superellipse stretches into a bevel, the bevel digs into a notch, the notch rounds into a scoop, the scoop swells into a superellipse.

Measured while hovering the first piece, on the vertical coordinate of the first point (50% → 24%): 42.4% at 40 ms, 28.8% at 150 ms, 21.7% at 300 ms — the bounce curve overshoots the target —, 22.4% at 450 ms, 24% at 800 ms in Chromium 151; 39.5, 27.3, 21.5, 22.7 and 24% in Firefox 153; 32.3% at 120 ms in WebKit 26. The piece also rotates ±5° and grows 5% (0.4 s). The background switches to the next shape's gradient too, but a gradient does not interpolate: the color changes at once, when the hover starts. Leaving the piece plays the transition backwards. No JavaScript.

The four shapes are polygon()s with the same structure, not url(#squircle), url(#bevel)… pointing to SVG <clipPath>s, for two reasons. A <clipPath> must exist on the page with its id: when missing, the pieces stay squares and only the rotation and color change show (measured: corner clickable). And even with valid <clipPath>s, going from one url() to another does not animate: it would snap. Four 68-point polygons in the same order make the morph real.

Accessibility

  • Hover only: the shape change only exists with a mouse. The pieces are divs with no role and no tabindex (Tab never stops on them, measured); with a finger, a tap triggers :hover and the shape stays until the next touch. If the shape carries information (game, configurator), add tabindex="0" and repeat the :hover rules as :focus-visible; since clip-path clips the focus ring, mark focus with the rotation itself or an outline on the parent.
  • prefers-reduced-motion not in the code: measured under emulation, rotation, grow and morph all stay active. Add @media (prefers-reduced-motion: reduce) { .morph-shape { transition: none } .morph-shape:hover { transform: none } }: the shape still changes, without motion.
  • Contrast of the white 10.4 px label: 4.2:1 on #8b5cf6, 3.5:1 on #ec4899, 2.4:1 on #06b6d4, 2.5:1 on #10b981 — all under 4.5:1. Darken the gradient start colors or raise the text to 14 px at weight 700 (3:1 threshold).

Browser compatibility

CSS only: clip-path: polygon() in percentages, clip-path transitions between polygons with the same number of points, custom properties, transform. Zero JavaScript, zero SVG. Morph measured in Chromium 151, Firefox 153 and WebKit 26 (intermediate values read during the transition in all three).

Chrome 55+✓ Full
Firefox 54+✓ Full
Safari 13.1+✓ Full
Edge 79+✓ Full
Mobile iOS 13.4+✓ Full (a tap triggers hover)
Android Chrome 55+✓ Full (a tap triggers hover)

Without clip-path (Firefox before 54, Safari before 13.1 without prefix), the pieces are square and only rotation and color change remain. If you replace a shape with a polygon of a different point count, the browser can no longer interpolate: the change snaps again, with no error.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="demo-preview">
  <div class="morph-shapes-wrap">
    <div class="morph-shape morph-shape--squircle">…</div>
    <div class="morph-shape morph-shape--bevel">…</div>
    <div class="morph-shape morph-shape--notch">…</div>
    <div class="morph-shape morph-shape--scoop">…</div>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Morphing Corner Shapes, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Cycle order (CSS — .morph-shape--*:hover rules) superellipse → bevel → notch → scoop → superellipse Change the variable called in each :hover. Any pair of shapes animates, since all four have 68 points in the same order.
Shape depth (CSS — the four --msh-* variables) bevel 24%, notch 18%, scoop 24%, superellipse n = 4 Recompute the 17 points of one corner, then rotate them: for a b% bevel, points go from (0, b) to (b, 0) in a straight line. Keep 17 points per corner, or interpolation breaks.
Duration and bounce (CSS — transition of .morph-shape) clip-path 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.9s ease-in-out for a slow morph with no overshoot; the original curve overshoots the target by about 2 percentage points around 300 ms.
Rotation and grow (CSS — transform of the :hover rules) ±5° and scale(1.05) transform: none for a configurator where the piece must stay straight; ±12° for a livelier game.
Size (CSS — width / height of .morph-shape) 100 × 100 px Shapes are in percentages: they follow. A non-square piece stretches the corners along the long side.

FAQ

A browser can only interpolate two polygon()s that have the same number of points: it then moves each point towards the point of the same rank. Two url(#…) pointing to SVG <clipPath>s never interpolate — the shape flips at the end. The code's four shapes have 68 points, 17 per corner, in the same order: any pair animates. Measured in Chromium, Firefox and WebKit, intermediate values run through the 0.6 s.
Because background holds a linear-gradient(), and gradients do not interpolate: the transition: background 0.4s rule has no effect, the color changes at the first instant of the hover. For a fade, put the color in a variable declared with @property --msh-c { syntax: '<color>'; inherits: false; initial-value: #8b5cf6 }, use it in the gradient and transition the variable.
Yes, with a class: replace the .morph-shape--squircle:hover selectors with .morph-shape--squircle.is-next (and so on), then toggle the class in JavaScript: el.addEventListener('click', () => el.classList.toggle('is-next')). The transition stays the same.