CSS Reference Guide

All TopicsPlayground

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.

Visual EffectsShapesClippingText Wrapping

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).

clip-path Shape Functions
.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() Syntax
/* 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() Syntax
/* 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() & ellipse() Demos
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() Syntax
/* 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() Demos
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() Syntax
/* 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%);

clip-path: path()

The path() function accepts an SVG path string, enabling curves, arcs, and any shape SVG can describe.

path() Syntax
/* 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. */
path() vs polygon(): Use 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.

Hover Transition: Circle to Full
.reveal {
  clip-path: circle(0% at 50% 50%);
  transition: clip-path 0.6s ease-out;
}

.reveal:hover {
  clip-path: circle(100% at 50% 50%);
}
Polygon Morph Animation
.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%);
}
Hover to Animate clip-path
Hover me!
Circle reveal
circle(30%) → circle(70%)
Hover me!
Diamond → Rect
diamond → rectangle
Hover me!
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.

shape-outside Syntax
.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: circle() — Text Wrapping Demo

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra.

shape-outside: polygon() — Triangle Text Wrap

Notice how the text wraps along the diagonal edge of the triangle shape rather than the rectangular bounding box. This creates a much more dynamic and interesting layout. The shape-outside property defines the invisible boundary, while clip-path makes the element visually match. You can combine these with images to create magazine-style layouts where text flows around the actual contour of a photo subject. This technique works with floated elements only.

Important: 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.

shape-margin
.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.

CSS Triangle (Border Trick)
/* 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;
}
Pure CSS Shapes (Border Trick)
Triangle Up
Triangle Down
Triangle Right
Triangle Left
Circle
Map Pin

Gotchas

clip-path hides content but doesn't remove it from layout. The clipped element still occupies its full rectangular space. Use it for visual effects, not layout changes.
Animating between different shape types doesn't work. You cannot animate from circle() to polygon(). For polygon-to-polygon, both must have the same number of points.
clip-path clips everything — including shadows and outlines. box-shadow and outline applied to a clipped element will be clipped too. Apply shadows to a parent wrapper instead.
shape-outside only works on floated elements. It has no effect on grid items, flex items, or normally positioned elements. The element must be floated and have explicit dimensions.
path() doesn't support percentages. Unlike polygon(), the path() function only accepts pixel values. This makes it harder to create responsive shapes.

Pro Tips

Use Clippy for polygon coordinates. bennettfeely.com/clippy is an interactive tool that lets you visually create clip-path polygons and copy the CSS.
Wrap clip-path in a parent for shadows. Since clip-path clips box-shadow, apply filter: drop-shadow() on a parent element — it follows the clip-path contour.
drop-shadow on clipped element
.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;
}
Use clip-path for page transitions. Animate clip-path: circle(0%) to circle(150%) on an overlay for a dramatic reveal effect during page transitions or image reveals.
Combine shape-outside with images. Use 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.
PreviousLogical Properties