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 — not transparent, not invisible-but-interactive, just gone from the painted output. It's like cutting a shape out of paper: you only see through the cut-out.
This is different from opacity: 0 (invisible but still takes up space and receives events) and different from visibility: hidden (invisible, takes up space). With clip-path, nothing outside the clip region is rendered or interactive.
clip-path works on any HTML element — divs, images, videos, SVGs, buttons, headings. The clipping shape is defined in the element's own coordinate system using percentages, so it scales automatically with the element.
Try it visually — no code needed
Drag points, adjust sliders, and copy the CSS in one click.
Open CSS Clip Path Editor →The four shape functions
clip-path supports four CSS basic shape functions. Each produces a different type of clipping region:
polygon() — custom shapes
polygon() is the most powerful function. It takes any number of x y coordinate pairs, each as a percentage of the element's width and height. The browser connects them in order with straight lines to form the clipping shape.
Minimum is 3 points (a triangle). There's no practical maximum. Complex polygons with dozens of points are fine.
/* Triangle */
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
/* Diamond */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
/* Pentagon */
clip-path: polygon(50% 0%, 97.6% 34.5%, 79.4% 90.5%, 20.6% 90.5%, 2.4% 34.5%);
/* Hexagon */
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
/* Arrow pointing right */
clip-path: polygon(0% 25%, 60% 25%, 60% 0%, 100% 50%, 60% 100%, 60% 75%, 0% 75%);
/* Star (10 points) */
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
Coordinates start from the top-left corner. 0% 0% is top-left, 100% 0% is top-right, 100% 100% is bottom-right. You can also use pixel values and mix units, but percentages are almost always what you want for responsive shapes.
Calculating polygon coordinates by hand is tedious. Use CSSTools.io clip-path editor to drag points visually and copy the generated CSS instantly.
circle() and ellipse()
circle() clips to a perfect circle. The syntax is circle(radius at center-x center-y). The center defaults to 50% 50% if omitted. Radius can be a percentage of the element's diagonal, a pixel value, or the keywords closest-side or farthest-side.
/* Centered circle, 50% radius */ clip-path: circle(50%); /* Smaller circle offset to top-left */ clip-path: circle(35% at 30% 30%); /* Full bleed profile photo crop */ clip-path: circle(50% at 50% 50%); /* Circle that fits the shorter side */ clip-path: circle(closest-side at 50% 50%);
ellipse() is the same idea but with separate radii for x and y, letting you create ovals. The syntax is ellipse(rx ry at center-x center-y).
/* Wide oval */ clip-path: ellipse(55% 35% at 50% 50%); /* Tall oval */ clip-path: ellipse(30% 50% at 50% 50%); /* Oval shifted to the right */ clip-path: ellipse(45% 40% at 65% 50%);
inset() — rectangular clips with rounded corners
inset() clips to a rectangle inset from the element's edges. It takes up to four offset values (top, right, bottom, left) just like margin or padding, plus an optional round keyword for border-radius.
/* 10% inset on all sides */ clip-path: inset(10%); /* Different per side: top right bottom left */ clip-path: inset(5% 15% 5% 15%); /* Inset with rounded corners */ clip-path: inset(0 round 16px); /* Inset with different radii per corner */ clip-path: inset(10% round 0 20px 20px 0); /* Reveal from top — great for animations */ clip-path: inset(0 0 100% 0); /* fully hidden */ clip-path: inset(0 0 0% 0); /* fully revealed */
inset() is particularly useful for scroll reveal animations — start with the bottom inset at 100% (element hidden) and transition it to 0% (element revealed).
clip-path vs overflow: hidden
These two properties both hide parts of elements, but they're fundamentally different tools:
- overflow: hidden always clips to a rectangle. It affects layout (child elements can't overflow), affects scrolling behaviour, and is inherited in the normal stacking context.
- clip-path clips to any shape. It's purely visual — it doesn't affect layout, doesn't affect child scrolling, and doesn't interact with overflow.
Use overflow: hidden when you want standard rectangular content containment. Use clip-path when you want a non-rectangular visual shape, or when you need to animate the clipping region.
One important note: clip-path completely overrides border-radius visually. If you want rounded corners with clip-path, use inset(0 round 12px) rather than combining border-radius with polygon.
Animating clip-path
clip-path is one of the best properties to animate — it's GPU-accelerated, doesn't trigger layout reflow, and produces smooth, high-performance transitions. The browser interpolates between coordinate values frame by frame.
The golden rule: both keyframe states must use the same shape function with the same number of coordinate values.
/* Wipe-in reveal (inset) */
.element {
clip-path: inset(0 100% 0 0);
transition: clip-path 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.element.visible {
clip-path: inset(0 0% 0 0);
}
/* Shape morph (polygon — same number of points!) */
.shape {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
transition: clip-path 0.4s ease-in-out;
}
.shape:hover {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
/* Circle expand on hover */
.reveal {
clip-path: circle(0% at 50% 50%);
transition: clip-path 0.5s ease;
}
.reveal:hover {
clip-path: circle(75% at 50% 50%);
}
Animating polygon() with different point counts
If you want to morph between shapes that naturally have a different number of points (triangle → star, for example), you need to match the point count by duplicating points. A triangle has 3 points; to morph it into a hexagon (6 points) you'd pad the triangle to 6 points by repeating some of its vertices.
/* Triangle padded to 6 points to match hexagon */
.shape {
/* Triangle with duplicated middle points */
clip-path: polygon(50% 0%, 50% 0%, 100% 100%, 100% 100%, 0% 100%, 0% 100%);
transition: clip-path 0.5s ease;
}
.shape:hover {
/* Hexagon — also 6 points */
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}
Real-world use cases
Angled hero sections
The most common use of clip-path in the wild. Cut the bottom of your hero at a diagonal to create visual flow into the next section, rather than a flat horizontal edge.
.hero {
clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
padding-bottom: 120px; /* extra space for the angle */
}
/* Symmetric V-cut at the bottom */
.hero-v {
clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 85%, 0 100%);
}
Custom image shapes
Apply clip-path directly to <img> elements for profile photos, card thumbnails and gallery layouts without needing SVG masks or extra wrapper elements.
/* Circular avatar */
.avatar {
clip-path: circle(50%);
}
/* Hexagonal profile photo */
.profile-hex {
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}
/* Diagonal card thumbnail */
.card-image {
clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
}
Scroll reveal animations
Combine inset() with an Intersection Observer to reveal elements as they scroll into view. This is one of the most polished scroll effects you can achieve with pure CSS transitions.
/* CSS */
.reveal {
clip-path: inset(0 0 100% 0);
transition: clip-path 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.visible {
clip-path: inset(0 0 0% 0);
}
// JavaScript
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
Hover reveal effects on cards
Combine clip-path with a background image or gradient overlay to create hover effects where content is revealed through a growing shape.
.card-overlay {
clip-path: circle(0% at 50% 50%);
transition: clip-path 0.4s ease;
background: rgba(124, 111, 255, 0.85);
position: absolute;
inset: 0;
}
.card:hover .card-overlay {
clip-path: circle(75% at 50% 50%);
}
Using SVG clip paths
For curves, arcs, and complex paths that are hard to express as a CSS polygon, you can reference an SVG <clipPath> element using clip-path: url(#id). This unlocks curved clipping regions, text-shaped masks and reusable clip shapes across multiple elements.
<!-- Hidden SVG with clip path definition -->
<svg width="0" height="0" style="position:absolute">
<defs>
<clipPath id="waveClip" clipPathUnits="objectBoundingBox">
<path d="M0,0 Q0.5,0.1 1,0 L1,0.9 Q0.5,1 0,0.9 Z" />
</clipPath>
</defs>
</svg>
<!-- Apply it in CSS -->
<div style="clip-path: url(#waveClip)">...</div>
Note the clipPathUnits="objectBoundingBox" attribute — this makes the SVG path coordinates relative to the element being clipped (0 to 1), so it scales correctly with any element size.
Browser support & performance
clip-path with CSS basic shapes (polygon, circle, ellipse, inset) is supported in all modern browsers with no prefix required. Chrome, Firefox, Safari and Edge all support it fully.
The newer path() function — which accepts an SVG path string directly in CSS — has excellent support in Chrome and Firefox, and good support in Safari 14+. For the widest compatibility, stick to the four basic shape functions.
Performance: clip-path is GPU-composited and does not trigger layout or paint. Animating it is safe and performant. For elements that animate frequently, add will-change: clip-path as a hint to the browser to promote the element to its own compositor layer.
/* Hint for frequently animated elements */
.animated-clip {
will-change: clip-path;
transition: clip-path 0.4s ease;
}
FAQ
Does clip-path affect click/touch events?
Yes — by default, pointer events on the clipped-away areas are disabled. Clicking outside the visible clip region won't trigger click handlers. This is usually what you want. If you need pointer events on the full bounding box regardless of the clip, you can add pointer-events: all, but be aware this creates invisible clickable areas.
Can I combine clip-path with border-radius?
clip-path overrides the visual effect of border-radius. If you apply both, only clip-path takes effect for the visual shape. To get rounded corners inside a clip-path, use the inset() function's round keyword: clip-path: inset(0 round 16px).
Why isn't my clip-path animating smoothly?
The most common cause is mismatched shape functions or point counts. A circle() cannot smoothly transition to a polygon(). Two polygons with different point counts won't animate smoothly either. Ensure both states use the same function and the same number of coordinate values.
Can I use clip-path on pseudo-elements?
Yes, ::before and ::after fully support clip-path. This is a common pattern for decorative shape effects where you don't want to add extra HTML elements.
Build clip-path shapes visually
Drag, click and adjust — then copy the CSS. No math required.
Open Free Clip Path Editor →