CSS Shapes & Clip-Path
Create complex geometric shapes, clip elements to custom outlines, wrap text around shapes, and build stunning visual effects — all with pure CSS.
clip-path Basics
The clip-path property clips an element to a specific shape. Only the area inside the shape is visible; everything outside is hidden (but the element still occupies its original space in the layout).
.element { /* Basic shape functions */ clip-path: circle(50%); clip-path: ellipse(50% 30% at center); clip-path: inset(10px 20px 30px 40px round 8px); clip-path: polygon(50% 0%, 100% 100%, 0% 100%); clip-path: path('M0,0 L100,0 L100,100 Z'); /* Reference an SVG clipPath */ clip-path: url(#myClipPath); /* Remove clipping */ clip-path: none; }
circle() & ellipse()
/* circle(radius at position) */ clip-path: circle(50%); /* centered, fills element */ clip-path: circle(100px at 50% 50%); /* 100px radius, centered */ clip-path: circle(40% at 30% 70%); /* offset position */ clip-path: circle(closest-side); /* radius = closest edge */ clip-path: circle(farthest-side); /* radius = farthest edge */
/* ellipse(rx ry at position) */ clip-path: ellipse(50% 30% at 50% 50%); /* horizontal ellipse */ clip-path: ellipse(30% 50% at center); /* vertical ellipse */
circle(50%)
circle(40% at 30% 30%)
ellipse(50% 35%)
ellipse(30% 50%)
inset()
The inset() function clips the element by specifying inward offsets from each edge, with optional border-radius.
/* inset(top right bottom left round border-radius) */ clip-path: inset(10px); /* 10px from all edges */ clip-path: inset(10px 20px); /* TB=10 LR=20 */ clip-path: inset(10px 20px 30px 40px); /* T R B L */ clip-path: inset(10px round 16px); /* with rounded corners */ clip-path: inset(5% 10% round 50%); /* pill/capsule shape */
inset(10px)
inset(10px round 20px)
inset(20% 5% round 50%)
polygon()
The polygon() function defines a clipping shape using a series of x/y coordinate pairs. This is the most versatile shape function.
/* polygon(fill-rule, x1 y1, x2 y2, ...) */ /* Triangle */ clip-path: polygon(50% 0%, 100% 100%, 0% 100%); /* Diamond / Rhombus */ clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); /* Pentagon */ clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%); /* Hexagon */ clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); /* Star (5-pointed) */ clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); /* Arrow pointing right */ clip-path: polygon(0% 20%, 60% 20%, 60% 0%, 100% 50%, 60% 100%, 60% 80%, 0% 80%); /* Cross / Plus */ clip-path: polygon(35% 0%, 65% 0%, 65% 35%, 100% 35%, 100% 65%, 65% 65%, 65% 100%, 35% 100%, 35% 65%, 0% 65%, 0% 35%, 35% 35%);
Live Shape Gallery
All shapes below are created with clip-path applied to simple colored div elements. Hover over each shape to see it grow.
polygon(50% 0%, 100% 100%, 0% 100%)
polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)
polygon(25% 0%, 75% 0%, ...)
polygon(50% 0%, 61% 35%, ...)
polygon(50% 0%, 100% 38%, ...)
polygon(0% 20%, 60% 20%, ...)
polygon(35% 0%, 65% 0%, ...)
clip-path: path()
The path() function accepts an SVG path string, enabling curves, arcs, and any shape SVG can describe.
/* Uses SVG path commands: M (move), L (line), C (curve), Z (close) */ .heart { clip-path: path('M 50,30 A 20,20,0,0,1,90,30 A 20,20,0,0,1,50,75 A 20,20,0,0,1,10,30 A 20,20,0,0,1,50,30 Z'); } .blob { clip-path: path('M 0,50 C 0,20 20,0 50,0 C 80,0 100,20 100,50 C 100,80 80,100 50,100 C 20,100 0,80 0,50 Z'); } /* Note: path() values use the element's own coordinate system. The coordinates are in pixels relative to the element's box. */
polygon() for straight-edged shapes (it's easier and supports percentages). Use path() when you need curves (Bezier curves, arcs). Note that path() only supports pixel coordinates, not percentages.
Animating clip-path
You can animate clip-path between shapes of the same type and same number of points. The browser interpolates between the coordinate values for smooth transitions.
.reveal { clip-path: circle(0% at 50% 50%); transition: clip-path 0.6s ease-out; } .reveal:hover { clip-path: circle(100% at 50% 50%); }
.morph { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); transition: clip-path 0.5s ease; } .morph:hover { /* Same number of points — morphs from diamond to rectangle */ clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }
Circle reveal
circle(30%) → circle(70%)
Diamond → Rect
diamond → rectangle
Star morph
pointy star → rounded star
shape-outside — Text Wrapping
The shape-outside property controls how inline content wraps around a floated element. Instead of wrapping around the element's rectangular box, text flows along the shape you define.
.float-circle { float: left; width: 200px; height: 200px; shape-outside: circle(50%); clip-path: circle(50%); /* also clip visually to match */ } /* All shape functions work: circle(), ellipse(), polygon(), inset(), url() */ .float-polygon { float: left; width: 200px; height: 200px; shape-outside: polygon(0% 0%, 100% 0%, 50% 100%); }
shape-outside only works on floated elements. The element must also have explicit width and height. Pair it with clip-path using the same shape to make the element visually match its text-wrapping boundary.
shape-margin
The shape-margin property adds spacing between the shape boundary and the wrapping text.
.float-shape { float: left; width: 200px; height: 200px; shape-outside: circle(50%); shape-margin: 1rem; /* adds 1rem gap between shape edge and text */ clip-path: circle(50%); }
Pure CSS Shapes with Borders
Before clip-path, CSS shapes were made using border tricks. These are still useful for small UI elements like tooltip arrows.
/* Triangle pointing down */ .triangle-down { width: 0; height: 0; border-left: 30px solid transparent; border-right: 30px solid transparent; border-top: 40px solid #6366f1; } /* Triangle pointing right */ .triangle-right { width: 0; height: 0; border-top: 30px solid transparent; border-bottom: 30px solid transparent; border-left: 40px solid #f472b6; } /* CSS Circle */ .circle { width: 80px; height: 80px; border-radius: 50%; background: #34d399; }
Triangle Up
Triangle Down
Triangle Right
Triangle Left
Circle
Map Pin
Gotchas
circle() to polygon(). For polygon-to-polygon, both must have the same number of points.
box-shadow and outline applied to a clipped element will be clipped too. Apply shadows to a parent wrapper instead.
polygon(), the path() function only accepts pixel values. This makes it harder to create responsive shapes.
Pro Tips
filter: drop-shadow() on a parent element — it follows the clip-path contour.
.shadow-wrapper { filter: drop-shadow(4px 4px 8px rgba(0,0,0,0.3)); } .shadow-wrapper .clipped { clip-path: polygon(50% 0%, 100% 100%, 0% 100%); background: #6366f1; }
clip-path: circle(0%) to circle(150%) on an overlay for a dramatic reveal effect during page transitions or image reveals.
shape-outside: url(image.png) to automatically wrap text around the transparent areas of a PNG image. Set shape-image-threshold to control the alpha cutoff.