Skip to main content

How to Use CSS clip-path for Polygons, Circles, and Insets

A practical guide to CSS clip-path — build polygons, circles, ellipses, and insets, cut diagonal section dividers, make notched ribbons, and animate shapes with copy-paste CSS.

Published By 李雷
#css #clip-path #frontend #design

How to Use CSS clip-path for Polygons, Circles, and Insets

For years the only way I had to crop an image into a hexagon was to open Photoshop, mask the layer, and export a PNG. Then every time a teammate swapped their headshot, the crop broke and I redid the whole thing. CSS clip-path ended that loop. The shape lives in your stylesheet, not in the file, so you crop once and never touch it again. This guide walks through the four shape functions you actually use, then shows how to turn them into diagonal dividers, ribbons, and hover animations.

The four shape functions worth knowing

clip-path clips an element to a region you describe. According to MDN's clip-path reference, the basic shapes you reach for day to day are four:

  • polygon() — a list of x y percentage pairs, one per vertex. The workhorse. Triangles, stars, arrows, cut corners, banners.
  • circle()circle(radius at cx cy). A perfect round crop.
  • ellipse()ellipse(rx ry at cx cy). Independent horizontal and vertical radii.
  • inset()inset(top right bottom left round radius). A rectangle pulled in from each edge, with optional rounded corners.

The mental model: every coordinate is a percentage of the element's own box, where 0% 0% is the top-left corner and 100% 100% is the bottom-right. A triangle pointing up is just three points: polygon(50% 0%, 0% 100%, 100% 100%). That's the whole language for straight-edged shapes.

A real input-to-output example

Say you want a chevron-style banner that points right. Open the CSS clip-path generator, switch to polygon mode, and drag six vertices into place. The handles snap to percentages and the output panel updates on every move. What you drag is what ships — the preview is a real DOM element with the same property your output shows, so there's no preview-versus-production drift.

The result for a right-pointing chevron:

.banner {
  clip-path: polygon(0% 0%, 75% 0%, 100% 50%, 75% 100%, 0% 100%, 25% 50%);
}

Six points: the left edge runs straight, the right side forms an arrow tip at 100% 50%, and the 25% 50% point carves the notch on the left so it reads like a tag. Drop that one line onto a <div> and you have a ribbon — text, padding, and background intact, no SVG element in the DOM.

Cutting a diagonal section divider

This is the pattern I use most. Landing pages love to slope from a dark hero into a lighter section below. Instead of stacking an SVG wave, give the lower section a four-point polygon:

.section-below {
  clip-path: polygon(0 8%, 100% 0, 100% 100%, 0 100%);
}

The top edge runs from 8% on the left down to 0% on the right — a gentle one-to-two-degree slant. Because all four points are percentages, it reflows cleanly from a 1440px desktop down to a 375px phone without a single media query. No extra DOM node, no background image, no aspect-ratio math. When you want the slope to feel deeper, drag the corner and copy the new value.

If your divider needs a curve rather than a straight edge, you've hit the ceiling of polygon() — that's where SVG <clipPath> and <path> take over. For everything geometric and crisp, polygon is faster and lighter.

Animating clip-path the right way

clip-path is a CSS property, so it transitions like any other. The trick is that the browser interpolates point-by-point, which means both keyframes must be the same shape function with the same vertex count. A polygon morphs into another polygon only if they share a vertex count; a circle's radius tweens smoothly; inset edges each tween independently.

That last one powers a clean image reveal with zero JavaScript:

.card img {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 250ms ease;
}
.card:hover img {
  clip-path: inset(0 0 0 0);
}

The collapsed state hides the image behind a 100% right inset; on hover it opens to zero on all sides, wiping left to right in a quarter second. No masking overlay, no animation library. The four inset values animate independently, so you get a smooth directional reveal for free.

Two animation traps to remember. You cannot tween between different shape functions — polygon to circle snaps instead of morphing. And box-shadow on the clipped element itself gets clipped away with everything else; move the shadow to a parent wrapper or swap to filter: drop-shadow(), which respects the clipped silhouette.

Browser support and one legacy gotcha

Support is excellent. polygon(), circle(), ellipse(), and inset() work in every evergreen browser: Chrome, Edge, Safari since 9.1, Firefox since 54, and all mobile WebKit and Blink engines. The one thing to watch is the legacy prefix — modern Safari dropped the -webkit- requirement, but if your audience still includes very old iOS (13 or below), ship both declarations:

.shape {
  -webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

The classic symptom of forgetting this: a user reports a square where you expected a star. Two lines fix it. The path() function (raw SVG path data inside clip-path) has slightly thinner support but is solid in Chrome 88+, Firefox 97+, and Safari 14+.

Where clip-path fits in your toolkit

Reach for clip-path whenever you need crisp geometric cropping — hexagon avatars, angled dividers, notched ribbons, masked hero banners. It composes with border-radius, box-shadow, transform, and filter without the layering gymnastics an SVG mask demands, and it clips at the compositor so it's cheap to render. When you need a soft alpha-gradient edge or organic blobby shapes, an SVG mask or the SVG blob generator is the better fit; for hard edges, clip-path wins on weight and simplicity.

Author your shapes visually, copy the one-line declaration, and paste it into a stylesheet, a Tailwind arbitrary value like [clip-path:polygon(...)], or a React inline style. The geometry lives in CSS where it belongs — swap an image and the crop holds.


Made by Toolora · Updated 2026-06-13