What is CSS clip-path?
The clip-path CSS property defines a clipping region on an element. Anything inside the region is visible. Anything outside is hidden — completely removed from the painted output, not just transparent. Unlike overflow: hidden which clips to a rectangle, clip-path can clip to any shape including triangles, pentagons, hexagons, and complex custom polygons.
An important distinction: elements clipped with clip-path still occupy their layout space. The clipping is purely visual — neighbouring elements still see the original dimensions. If you need the layout to collapse around a clip, you also need to adjust the element's dimensions separately.
The Four Shape Functions
clip-path supports four CSS basic shape functions, plus the ability to reference SVG <clipPath> elements for complex curves. Here is what each function does:
| Function | Shape | Use case |
|---|---|---|
| polygon() | Any polygon with n vertices | Angled sections, custom shapes, icons |
| circle() | Circle with a radius and centre | Profile images, avatar masks, circle reveals |
| ellipse() | Ellipse with rx, ry and centre | Oval crops, organic masks |
| inset() | Rectangle with optional round corners | Inner cropping, reveal animations |
polygon() — Custom Shapes
polygon() takes any number of coordinate pairs as percentages of the element's width and height. The browser connects them in order to form the clipping polygon. Minimum is 3 points (triangle); there is no practical maximum.
/* Triangle pointing up */ clip-path: polygon(50% 0%, 0% 100%, 100% 100%); /* Angled hero section — parallelogram-style bottom */ clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%); /* Angled from top-right */ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 90%); /* Diamond */ clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); /* Arrow pointing right */ clip-path: polygon(0 20%, 70% 20%, 70% 0%, 100% 50%, 70% 100%, 70% 80%, 0 80%); /* Hexagon */ clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
circle() and ellipse()
circle(radius at x y) clips to a circle. ellipse(rx ry at x y) clips to an ellipse. Both are much simpler than polygon() for circular cropping — the go-to technique for profile image circles in CSS without needing border-radius:
/* Circle crop on an image */
.avatar {
clip-path: circle(50% at center);
/* Same result as border-radius: 50% but clips child content too */
}
/* Spotlight effect — circle from off-centre */
.spotlight {
clip-path: circle(60% at 30% 40%);
}
/* Expanding circle reveal animation */
.reveal {
clip-path: circle(0% at 50% 50%);
transition: clip-path 0.6s ease;
}
.reveal.active {
clip-path: circle(150% at 50% 50%);
}
/* Ellipse crop */
.oval-image {
clip-path: ellipse(50% 40% at center);
}
inset() — Rectangular Clips with Rounded Corners
inset() clips to a rectangle defined by inset distances from the element's edges. It supports the round keyword to add border-radius to the clip. It is the right choice for reveal animations where a rectangle expands or contracts:
/* Clip 20px from all sides */
clip-path: inset(20px);
/* Different values per side: top right bottom left */
clip-path: inset(10px 20px 30px 20px);
/* Rounded corners */
clip-path: inset(0 round 16px);
/* Wipe-in reveal animation */
.wipe-reveal {
clip-path: inset(0 100% 0 0); /* fully hidden: right edge at left */
transition: clip-path 0.6s ease;
}
.wipe-reveal.active {
clip-path: inset(0 0 0 0); /* fully shown */
}
Animating clip-path
clip-path is animatable, but with one critical constraint: the shape function and the number of polygon points must match in both keyframes. You cannot animate from a circle() to a polygon(), and you cannot animate between polygons with different numbers of vertices. The browser interpolates point positions, so both shapes must have the same structure.
/* ✅ Works — same function, same point count */
@keyframes morph {
from { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
to { clip-path: polygon(10% 10%, 90% 5%, 95% 90%, 5% 95%); }
}
/* ✅ Circle reveal */
@keyframes reveal {
from { clip-path: circle(0% at 50% 50%); }
to { clip-path: circle(150% at 50% 50%); }
}
/* ❌ Won't animate — different functions */
@keyframes broken {
from { clip-path: circle(50%); }
to { clip-path: polygon(0 0, 100% 0, 100% 100%); }
}
/* Hover shape morph */
.shape {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transition: clip-path 0.4s ease;
}
.shape:hover {
clip-path: polygon(10% 10%, 90% 5%, 95% 90%, 5% 95%);
}
Real-World Use Cases
Angled hero sections
.hero {
clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
background: linear-gradient(135deg, #7c6fff, #ff6fb0);
min-height: 70vh;
}
/* Opposite angle */
.hero-reverse {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 88%);
}
Custom image shapes
/* Hexagon image crop */
.hex-image {
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}
/* Diagonal crop */
.diag-image {
clip-path: polygon(0 0, 100% 0, 80% 100%, 0 100%);
}
clip-path vs overflow: hidden
| clip-path | overflow: hidden | |
|---|---|---|
| Shape | Any CSS shape | Rectangle only |
| Animatable | Yes (matching shapes) | Not practically |
| Affects layout | No | Yes (hides scrollbar content) |
| Child elements | Clipped visually | Clipped visually |
| Performance | Repaint (use will-change) | Cheap |
Browser Support
clip-path with basic shapes (polygon, circle, ellipse, inset) is supported in all modern browsers. The path() function for SVG path syntax has growing support (Chrome 88+, Firefox 97+, Safari 13.1+).
Create clip-path shapes without writing coordinates
Drag control points, choose presets, preview on real images. Copy the CSS in one click.
Open Clip Path Generator →