Skip to main content

CSS Shapes and Clipping: clip-path, shape-outside, and Masks

CSS gives you powerful tools for creating non-rectangular elements — clipped shapes, text that flows around irregular regions, and complex masking effects. These are among the most visually impressive CSS techniques and appear frequently in premium web design.


1. clip-path: Clipping Elements to Any Shape

clip-path hides everything outside a defined shape. The element is still in layout — only its visible area changes.

Basic Shape Functions

/* circle(radius at cx cy) */
.c-avatar { clip-path: circle(50%); }
.c-badge { clip-path: circle(40px at center); }
.c-highlight { clip-path: circle(60px at 20% 50%); } /* Off-center circle */

/* ellipse(rx ry at cx cy) */
.c-oval { clip-path: ellipse(60% 40% at center); }
.c-egg { clip-path: ellipse(40% 55% at 50% 45%); }

/* inset(top right bottom left round border-radius) */
.c-card { clip-path: inset(0); } /* No clip */
.c-card { clip-path: inset(0 round 1rem); } /* All corners rounded */
.c-card { clip-path: inset(0 round 1rem 1rem 0 0); } /* Top corners only */
.c-card { clip-path: inset(10px 20px 10px 20px); } /* Shrink visible area */

/* polygon(x1 y1, x2 y2, x3 y3, ...) */
.c-triangle { clip-path: polygon(50% 0%, 0% 100%, 100% 100%); }
.c-diamond { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }
.c-pentagon { clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%); }
.c-arrow-right { clip-path: polygon(0% 0%, 75% 0%, 100% 50%, 75% 100%, 0% 100%); }
.c-notch { clip-path: polygon(0 0, 100% 0, 100% 85%, 90% 100%, 0 100%); }
.c-chevron { clip-path: polygon(0 0, calc(100% - 30px) 0, 100% 50%, calc(100% - 30px) 100%, 0 100%, 30px 50%); }

/* Diagonal section divider */
.c-section-diagonal {
clip-path: polygon(0 0, 100% 0, 100% 90%, 0 100%);
}

The path() Function

/* path(): SVG path data — any shape possible */
.c-blob {
clip-path: path('M 50 0 C 80 10, 100 30, 90 60 C 80 90, 50 100, 20 90 C -10 80, 0 50, 10 30 C 20 10, 30 -5, 50 0 Z');
}

/* Better for blobs: use online generators */
/* blobmaker.app, haikei.app → generate SVG path → paste here */

2. Animating clip-path

clip-path is animatable — and the animations are GPU-accelerated when using basic shapes:

/* Hover clip reveal */
.c-card__overlay {
clip-path: inset(0 100% 0 0); /* Fully hidden (clipped right) */
transition: clip-path 0.4s var(--ease-snappy);
}
.c-card:hover .c-card__overlay {
clip-path: inset(0 0 0 0); /* Fully revealed */
}

/* Circle reveal on click */
.c-menu {
clip-path: circle(0% at 95% 5%); /* Starts hidden */
transition: clip-path 0.5s ease-out;
}
.c-menu.is-open {
clip-path: circle(150% at 95% 5%); /* Expands to cover screen */
}

/* Section entrance with polygon */
.c-hero {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); /* Normal */
}
/* On load animation: wipe in from top */
@keyframes clip-wipe-in {
from { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); } /* Zero height */
to { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); } /* Full height */
}
.c-hero { animation: clip-wipe-in 0.8s ease-out both; }

[!IMPORTANT] To animate between two clip-path values, both must use the same function with the same number of points. You cannot animate between circle() and polygon(), or between polygons with different point counts.


3. clip-path: url() — SVG Clip Paths

For complex shapes, reference an SVG <clipPath> element:

<!-- Define SVG clip path in the HTML (hidden) -->
<svg width="0" height="0" style="position:absolute">
<defs>
<clipPath id="wave-clip" clipPathUnits="objectBoundingBox">
<!-- Curve path using 0-1 coordinate space -->
<path d="M 0 0, 1 0, 1 0.85, 0.75 1, 0.25 0.85, 0 1 Z"/>
</clipPath>
</defs>
</svg>

<!-- Apply to CSS element -->
<div class="c-section-wave"></div>
.c-section-wave {
clip-path: url(#wave-clip);
}

4. shape-outside: Text Flows Around Shapes

shape-outside makes text content flow around a non-rectangular floated element:

/* Only works on floated elements */
.c-pull-quote {
float: left;
width: 200px;
height: 200px;
shape-outside: circle(50%); /* Text flows around a circle */
margin: 0 1.5rem 1rem 0;
}

/* Text wraps around a triangle-shaped float */
.c-callout {
float: right;
width: 0;
height: 0;
border-style: solid;
border-width: 0 150px 200px 0;
border-color: transparent var(--color-primary) transparent transparent;
shape-outside: polygon(100% 0, 100% 100%, 0 100%);
margin-left: 1rem;
}

/* Use shape-margin to add space between shape and text */
.c-image-float {
float: left;
clip-path: circle(50%); /* Visual clip */
shape-outside: circle(50%); /* Text flow follows same shape */
shape-margin: 1rem; /* Space between edge and text */
}

/* Image-based shape — text follows transparent areas of image */
.c-cutout-float {
float: left;
shape-outside: url('cutout-image.png'); /* Must be CORS-accessible */
shape-image-threshold: 0.5; /* Alpha threshold: 0-1 */
width: 250px;
}

5. CSS Masking vs Clipping

clip-path vs mask:
clip-path: hard edge — pixel is either visible or invisible
mask: soft edge — pixels can be partially transparent
→ grayscale of mask image = opacity of element
/* mask-image: using a gradient */
.c-fade-bottom {
mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
}

/* Text fade out effect */
.c-text-fade {
mask-image: linear-gradient(to right, black 80%, transparent 100%);
}

/* mask-image: using an SVG shape */
.c-masked-image {
mask-image: url('mask-shape.svg');
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
}

/* Circular vignette */
.c-vignette {
mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
}

6. Native CSS Masking (Newer)

/* mask shorthand: image position / size repeat origin clip composite mode */
.c-masked {
mask: url('mask.svg') center / cover no-repeat;
}

/* Multiple masks */
.c-multi-mask {
mask:
linear-gradient(to bottom, black, transparent) top / 100% 20%,
linear-gradient(to top, black, transparent) bottom / 100% 20%;
mask-composite: intersect; /* Where both masks overlap: visible */
}

7. Common Real-World Patterns

Diagonal Section Divider

.c-section {
position: relative;
}
.c-section::after {
content: '';
position: absolute;
bottom: -2px;
left: 0; right: 0;
height: clamp(40px, 5vw, 80px);
background: var(--color-bg); /* Same as next section's background */
clip-path: polygon(0 100%, 100% 0, 100% 100%);
}

Hexagon Avatar Grid

.c-hex-avatar {
width: 120px;
height: 138px; /* height = width * 1.1547 for regular hexagon */
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
object-fit: cover;
}

Hero Image with Angled Bottom

.c-hero {
position: relative;
clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
/* Adds angled bottom to hero section */
}

8. Tools for Generating clip-path Values

Generators (copy-paste ready values):
clippy.bharat.run — Visual polygon, circle, inset generator
bennettfeely.com/clippy — Classic clip-path generator
haikei.app — Wave, blob, stacked wave SVG generator
cssgenerator.pl/clip-path — Animated clip-path generator
blobmaker.app — Organic blob path() values

Online SVG path editors:
figma.com — Draw shape → Export SVG → Copy path data
inkscape.org — Free, offline SVG path editor
svg-path-visualizer.netlify.app — Visualize and edit SVG paths

9. AI Prompt for Shapes

Create these CSS visual effects:

1. Section divider: angled bottom edge pointing down-right
Use polygon() clip-path on ::after pseudo-element

2. Circular reveal animation for mobile menu
clip-path: circle(0%) → circle(150%) on toggle

3. Card image with soft bottom fade
mask-image: gradient fade to transparent

My tokens: [PASTE :root]
Output: HTML + CSS with BEM naming
Note: Include -webkit-mask-image alongside mask-image
Include @keyframes for any animations
Respect prefers-reduced-motion