Liquid✨ Premium

Shape Shifter

A block cut out with clip-path that reshapes on every click, from circle to square, triangle, hexagon then star, in 0.8 s: all five shapes are 40-point polygons, so every point glides to its match, and the gradient changes hue at the same time. The cutout applies to everything inside the block, photos included; a real button for the keyboard, and the shape is set without morphing when the user reduces motion.

CSSclip-pathCycle

Updated

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

3 usage examples

Member photo frame — digital membership card of a judo club — Shape Shifter example 1

① Member photo frame — digital membership card of a judo club

WhenOn her membership card, the young judoka taps her photo to pick a frame: round, square, triangle, hexagon or star, on the orange of her belt.
Whyclip-path cuts the photo itself, with no edited image: that is what SVG Path Morph, which only draws a path, cannot do.
Settings140 px block holding the portrait (three elements drawn in CSS, to replace with an <img>); colors set for every shape to #fdba74 → #ea580c; "Round frame", "Square frame"… labels in data-names.
Cutting template for a custom mirror — an online glazier's configurator — Shape Shifter example 2

② Cutting template for a custom mirror — an online glazier's configurator

WhenIn the middle of the configurator, the mirror preview and its "Hexagon — 60 cm" label; each tap moves to the next shape, with the options and price alongside.
WhyThe shape is cut from the block that carries the reflection: the customer sees their mirror, not a pictogram.
SettingsBlock enlarged to 170 px; silver gradient #e2e8f0 → #7c8ea3 identical for every shape; two slanted highlights as pseudo-elements, cut with the mirror; drop shadow that follows the shape through filter: drop-shadow() on the button.
Picking a pawn before a game — an online board game — Shape Shifter example 3

③ Picking a pawn before a game — an online board game

WhenSetup screen: "Player 2, pick your pawn"; each tap moves the pawn through circle, square, triangle, hexagon and star, each with its own color.
WhyEach shape arrives with its own gradient: the pawn's shape and color change together, like the pieces of a real game.
SettingsOriginal settings: 140 px block, the five gradients from the code; filter: drop-shadow(0 8px 12px rgba(0,0,0,.45)) on the button to set the pawn on the green felt.

How it works

The .shape-shifter-btn button wraps a 140 × 140 px block, .shape-shifter, whose data-shape attribute picks the shape: circle, square, triangle, hexagon or star. Each value has its own CSS rule, a clip-path: polygon(…) and two gradient colors. On click (or Enter, Space), the script moves to the next shape and writes its name under the block, in .shape-label, reading it from the button's data-names attribute ("Circle,Square,Triangle,Hexagon,Star"). The morph itself is a CSS transition: transition: clip-path .8s cubic-bezier(.68, -.2, .27, 1.2), with a slight back-up at the start and a slight overshoot at the end.

A browser can only interpolate two clip-path values when they use the same function with the same number of points: between circle() and polygon(), or between a 4-vertex square and a 10-vertex star, it jumps in one go. That is why the five shapes are written as 40-point polygon() values, all starting at the top center and going the same way around: the circle is a 40-sided polygon (less than 0.3 px away from a true circle at 140 px), and each side of the square, triangle, hexagon and star carries its in-between points. Measured frame by frame in Chromium 151, Firefox 153 and WebKit 26: on each of the five steps, the clipped area changes on every frame (from circle to square, the area goes from 78% to 64% of the block through 83 to 86 distinct values in Chromium and Firefox, 24 in WebKit, which draws fewer frames in headless mode).

The two gradient colors, --fx0403-c1 and --fx0403-c2, are declared with @property as <color> values: the browser can then blend them over 0.8 s, together with the shape, while a hard-coded linear-gradient() would switch at once. A click during the morph starts from the shape on screen, as the CSS transition retargets by itself. The cutout applies to .shape-shifter and everything inside it: a photo, an illustration or text placed inside takes the shape, like the portrait on the judo card in the examples.

Accessibility

  • Keyboard and screen readers: the shape sits in a real <button> named "Next shape". Measured in Chromium and Firefox: Tab reaches it, Enter and Space move to the next shape. The .shape-label text has aria-live="polite": the new shape's name is announced. In Safari, Tab only stops on buttons if the keyboard setting asks for it (or with Option+Tab).
  • Focus ring: the cutout is on the .shape-shifter child, not on the button, so the focus ring (2 px cyan, 6 px offset) stays whole around the shape.
  • Reduced motion: under prefers-reduced-motion: reduce, clip-path has no transition, only the colors blend over 0.4 s. Measured: the new shape is in place from the first frame.
  • The shape must not be the only information: if it means something (a chosen frame, a pawn), the label says it in text too, as in the examples.

Browser compatibility

CSS (clip-path: polygon(), @property) + 1 KB of JavaScript, no dependencies. Measured frame by frame in Chromium 151, Firefox 153 and WebKit 26: same morph on all five steps, same blending colors, same stop under reduced motion. clip-path: polygon() animates since Chrome 55, Firefox 54 and Safari 13.1. @property exists since Chrome 85, Safari 16.4 and Firefox 128; without it, colors switch at once but the shape still morphs. Cost: a 140 px block repainted for 0.8 s, negligible.

Chrome 85+✓ Full
Firefox 128+✓ Full (shape since Firefox 54, colors without blending before 128)
Safari 16.4+✓ Full (shape since Safari 13.1)
Edge 85+✓ Full
Mobile iOS 16.4+✓ Full (touch)
Android Chrome 85+✓ Full (touch)

Without JavaScript, the block stays a circle. Without @property, the gradient jumps from one hue to the next while the shape morphs. A browser without clip-path shows the 140 px square block with its gradient.

The code

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

index.html — structure
<div class="fx-0403-root">
  <div class="shape-shifter-container">
    <button type="button" class="shape-shifter-btn" aria-label="Next shape"
            data-names="Circle,Square,Triangle,Hexagon,Star">
      <span class="shape-shifter" data-shape="circle"></span>
    </button>
    <span class="shape-label" aria-live="polite">Circle</span>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Size (CSS — width / height of .shape-shifter) 140 × 140 px The polygons use percentages: the shape follows the block's size. Keep the block square, or the circle becomes an ellipse.
Colors per shape (CSS — --fx0403-c1 / --fx0403-c2 in each [data-shape=…] rule) five gradients: cyan, violet, pink, amber, green Put the same two colors in all five rules for a fixed background (a mirror, a photo on a plain color). The properties stay animatable as long as both @property blocks are there.
Duration and bounce (CSS — transition: clip-path .8s cubic-bezier(.68, -.2, .27, 1.2)) 0.8 s, slight back-up then slight overshoot .5s ease-in-out gives a crisper morph without bounce. Change the duration of the two colors too, or they finish after the shape.
Labels (HTML — the button's data-names) "Circle,Square,Triangle,Hexagon,Star" (English version) Five comma-separated names, in shape order. The script rereads the attribute on every click: you can change it on the fly (language, added price).
Shape order and choice (JS — var shapes = ['circle', 'square', 'triangle', 'hexagon', 'star']) five shapes, looping Remove a value to skip a shape (keep data-names in the same order). A new shape needs a [data-shape=…] rule with a 40-point polygon(), starting at the top center, going clockwise.
Shadow that follows the shape (CSS — on .shape-shifter-btn) none (the block's box-shadow is cut off with it) filter: drop-shadow(0 10px 14px rgba(0,0,0,.35)) on the button draws a shadow that hugs the shape throughout the morph.

FAQ

Because a browser does not interpolate from one function to another: from circle() to polygon(), the shape jumps at the end, with no in-between frame. Both sides need the same function and the same number of points. 40 points are enough for a 140 px circle to look smooth (less than 0.3 px off) and split cleanly across the sides: 4 points per star arm, 10 per square side. More points would smooth the circle further on a very large block, at the cost of longer CSS.
Write a 40-point polygon() in percentages, whose first point is at the top center (50% y%) and which goes clockwise, like the other five. Spread the points along the sides, vertices included: a shape with 5 equal sides gets 8 points per side. If the starting points do not match, the shape twists during the morph instead of transforming cleanly.
SVG Path Morph reshapes a vector path: a drawn shape, filled with an SVG gradient, sharp at any size, across three curved shapes (star, heart, circle). Shape Shifter cuts out an HTML block: everything inside it (photo, text, background) takes the shape, with five angular shapes. Pick Shape Shifter to give a shape to existing content, a profile photo or a product preview; SVG Path Morph for an icon or pictogram that transforms.