Free CSS clip-path editor
Clip path generator with draggable vertices
Shape an element by dragging its outline. This clip-path generator is free and needs no signup: choose polygon(), circle(), ellipse() or inset(), move the handles that sit on top of the element they are clipping, and take away a value in percentages rather than pixels nailed to one canvas size. Polygons run from 3 to 24 vertices with 18 outlines to start from, and the preview keeps a real border and padding on screen so you can watch one value land in three different places as the reference box changes.
- 100% free
- No signup
- 18 outlines
- 3-24 vertices
- Percent output
The orange dashed rectangle is the border-box the percentages resolve against — every handle is placed inside it, not inside the element. Drag with the pointer, or focus a handle and use the arrow keys; hold Alt while dragging to ignore the 1% grid.
Turn on the shadow to watch box-shadow vanish, and the layer promotion to check whether your browser stops anti-aliasing the diagonal edges once the element is composited.
polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)Press Ctrl+V anywhere on the page to drop in a clip-path value from a stylesheet or devtools — no need to click the field first.
Start from an outline
| # | x % | y % | Remove |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 |
Transition check
6 vertices against 3: CSS cannot interpolate lists of different length, so the browser swaps the two shapes at the halfway point instead of morphing.
How to generate a clip-path
Three steps from a rectangle to a value that survives the layout it lands in.
Pick the function before you pick the shape
Four buttons, four grammars. polygon() is the one with draggable vertices and the only one that takes a fill rule; circle() and ellipse() give you a centre and one or two radii; inset() is a rectangle described by its four offsets and takes the whole border-radius grammar after the round keyword, which makes it the right choice for a rounded card rather than a cut corner. Switching function keeps the geometry you already set for the others, so you can compare an inset against a polygon of the same silhouette without redrawing either.
Drag the handles on the element itself, not on a separate canvas
Every handle sits on top of the thing being clipped. Drag a vertex with the pointer, or focus it and use the arrow keys — 1% a press, 5% with Shift, Delete to remove it. The small dashed + on each edge midpoint inserts a vertex there, the coordinate table takes exact numbers when a value has to be 33.3% rather than nearly, and the snap dropdown quantises drags to 1% or 5% while Alt temporarily ignores it.
Set the reference box, then copy the value, the keyframes or the SVG
Switch between border-box, padding-box and content-box with the border and padding sliders up: the same percentages land in three different places, and choosing the wrong one is the usual reason a value that looked right in a generator looks wrong in the component. Then Copy value for the bare function, Copy CSS rule for a declaration with an optional -webkit-clip-path duplicate, Copy keyframes for a from/to pair, or Download SVG for a clipPath element written at the preview's pixel size.
Technical specifications
| Shape functions | polygon(), circle(), ellipse() and inset(), each with an optional reference box. path(), rect(), xywh() and url(#id) are valid CSS clip-paths but have no handles to drag, so a pasted one is reported rather than mangled |
|---|---|
| Vertices and outlines | 3 to 24 vertices per polygon and 18 outlines to start from, running from a 3-point triangle to a 12-point cross and including both stars — the 5-vertex self-intersecting pentagram and the 10-vertex one |
| Coordinate output | Percentages at 0, 1 or 2 decimals, 1 by default; x resolves against the reference box width and y against its height. The only px in the output is the inset corner radius, because a radius that scales with the box stops looking like a radius |
| Reference boxes | border-box (the initial value), padding-box and content-box, shown against a live border of 0-32px and padding of 0-48px so the three land visibly apart. margin-box is in the grammar and implemented by no engine for an HTML element |
| Circle radius maths | A percentage radius resolves against sqrt(w² + h²) / sqrt(2), so on the default 400 × 300 box the reference length is 353.6px and circle(50%) draws 176.8px |
| Fill rule | polygon() takes nonzero (the default) or evenodd as its first argument; it changes nothing on a simple outline and everything on the 5-point pentagram, where evenodd is what hollows the middle out |
| Input accepted | A full declaration or a bare function, the -webkit- spelling, a trailing reference box, percentages, px lengths converted against the box currently on screen, and unitless numbers — so the points attribute copied off an SVG polygon pastes straight in. Up to 4,000 characters |
| Outputs | The bare value, a rule with an optional -webkit-clip-path duplicate, a from/to @keyframes pair, and an SVG download whose clipPath is written in userSpaceOnUse units at the preview's pixel size — all four assembled in this tab, none of them sent anywhere |
Frequently asked questions
Why are the diagonal edges of my clipped element jagged?
Because a clip is geometry rather than an image: a pixel is inside the shape or outside it, and some engines resolve that a whole pixel at a time. Chromium is where it usually shows, and it gets worse once the element is promoted to its own compositor layer — a transform animation, will-change, a 3D translate — because the clip is then applied on the compositor with no anti-aliasing at all. The two fixes that hold up are to keep the clip and the animation on different elements (clip a wrapper, animate the child inside it), or to swap clip-path for mask-image, which samples an SVG or a gradient and therefore has soft edges by construction. The layer toggle under the preview lets you check what your own browser does instead of trusting a bug report.
Why does my shape distort when the element resizes?
Because polygon percentages resolve per axis: the x of every vertex against the reference box width, the y against its height, independently. A corner cut at 45 degrees stays 45 degrees only while the aspect ratio holds — press Banner 640 × 200 in the preview and watch the same value flatten. When an angle has to be exact regardless of width, mix units instead of scaling: polygon(0 0, 100% 0, calc(100% - 24px) 100%, 24px 100%) pins the slant to 24px at any size, and aspect-ratio on the element pins the whole outline.
Where did my box-shadow go after I added a clip-path?
It was clipped away, and it was never going to survive. A box-shadow is painted outside the border box while the clip keeps only what falls inside the shape, so the shadow goes out with the corners you cut. outline and the focus ring disappear for the same reason, which is an accessibility bug rather than a cosmetic one. Put the clip on a child and leave the shadow and the outline on the parent, or use filter: drop-shadow() on the parent — it reads the alpha of the already-clipped child, so the shadow traces the real silhouette instead of a rectangle.
Can I animate or transition between two clip-path shapes?
Yes, if both values use the same function and, for polygon(), list the same number of vertices in the same direction. CSS interpolates the two lists position by position; when the counts differ there is nothing to pair up and the browser falls back to a discrete swap at the 50% mark, which reads as a flicker. Padding the shorter outline with points that sit on the midpoints of its own edges fixes it without moving anything, and that is what Match vertex counts does here. Mixing functions — polygon() to circle(), or either to inset() — is not interpolatable in any engine, so a shape that has to morph should be authored as a polygon from the start.
Why doesn't circle(50%) reach the sides of my rectangle?
Because a circle radius is a percentage that refers to both axes at once, and CSS resolves that class of percentage against sqrt(width² + height²) / sqrt(2) rather than against either edge. On a 400 × 300 box the reference length is 353.6px, so circle(50%) is a radius of 176.8px — smaller than half the width and larger than half the height. If you want the circle to touch the nearer pair of edges use circle(closest-side), which needs no maths, and if you want an oval that follows the box use ellipse(50% 50%), whose radii do resolve per axis.
Do clicks still land on the part that was clipped away?
No — hit testing follows the clip, so pointer events in the removed region pass straight through to whatever is behind the element. That is genuinely useful for a diagonal hero section that would otherwise swallow the clicks of the block underneath it, and it is a trap for a clipped button whose visible label sits near an edge you trimmed. Worth knowing alongside it: any clip-path other than none makes the element a stacking context, so a z-index on a descendant is from then on measured inside the clipped element rather than against the rest of the page.
When is overflow: hidden or a mask the better property?
Use overflow: hidden when the shape is just the box — it is cheaper, it clips scrolling descendants and it keeps the border radius, which a clip-path replaces rather than respects. Use clip-path when the region is a shape the box cannot describe: a triangle, a chevron, a hexagon, a rectangle with one corner sliced off. Use mask-image when the edge needs to fade or vary in opacity, because a clip is binary and a mask is a grayscale image — a feathered edge, a torn-paper texture or a gradient wipe is a mask, not a clip.
About clip-path, reference boxes and the percentages inside them
clip-path is defined in CSS Masking Module Level 1 and borrows its four editable functions from CSS Shapes 1: polygon(), circle(), ellipse() and inset(), optionally followed by the box those coordinates are measured in. The initial box is border-box, and on an SVG element — which has no CSS box to speak of — border-box resolves to fill-box. What follows from the definition matters more than the syntax: the clip is a hard region, so a pixel is either kept or removed with nothing in between. That single property explains why hit testing follows the outline, why anything painted outside the border box goes with it, and why any value other than none turns the element into a stacking context. Percentages are the other half of the standard worth reading twice. In polygon() every x resolves against the reference box width and every y against its height, independently, which is what makes the output resize-proof and also what lets a 45-degree cut flatten out when the box gets wider. The exception is the circle radius: it refers to both axes at once, so CSS resolves it against sqrt(w² + h²) / sqrt(2) — 353.6px on a 400 × 300 box, meaning circle(50%) draws 176.8px and touches no edge at all.
Three things trip people up often enough to be worth designing around. The reference box is the first: percentages are measured against the border box unless you say otherwise, so a shape drawn over a bare rectangle in a generator moves once the real component has 16px of padding and an 8px border, and switching to content-box moves it back. The second is everything the clip takes with it — the elevation you tuned in the box shadow generator is painted outside the border box and is therefore removed, and so is the focus ring, which is an accessibility failure rather than a styling detail. The third is the assumption that a clip is a rounded corner: inset(0 round 24px) and a plain 24px radius look identical until a border or an outline is involved, at which point the radius keeps the border curving and the clip slices through it, so the border radius generator remains the right tool whenever the corner is meant to stay a corner.
Animation has one rule and it is unforgiving: the two values must use the same function, and two polygons must list the same number of vertices in the same winding direction, or the browser stops interpolating and swaps the shapes at the halfway point of the transition. The fix is not to redraw either outline but to pad the shorter list with points that sit on the midpoints of its own edges — the vertex count changes and the rendered shape does not move a pixel, which is exactly what the transition check here does before it hands back a keyframes pair. Where generators tend to disappoint is the output rather than the editor: one that draws on a fixed canvas and emits pixel coordinates gives you a shape that is correct in a single viewport, and one that ships a ten-vertex outline as its star never has to admit that polygon(evenodd, …) exists, even though the fill rule is the only way to hollow out a real pentagram. Clipping a flat color is rarely the point either, which is why the preview clips a ramp: build it in the gradient generator or the CSS gradient generator, clip it here, and if the clipped thing is a control, wire its states in the CSS button generator. For a tooltip arrow in particular, compare both approaches in the CSS arrow generator — the border-width trick costs no extra element, while a clipped arrow can carry the parent’s gradient across the join.
Where the shape is calculated
Every number on this page is worked out by JavaScript running in the tab you are reading it in. Nothing you type, paste or open is uploaded, logged or kept, which is also why the tools carry on working after you disconnect from the network.
The SVG you download is assembled from the same numbers the preview is drawn with, as a string, in this tab — there is no render server behind the Download button and no round-trip to fetch the file.