CSS3 & Responsive Design

Clipping Paths & Masks

25 min Lesson 45 of 60

Introduction to Clipping and Masking

Clipping and masking are two powerful CSS techniques that let you control which parts of an element are visible. Both achieve similar goals -- hiding portions of an element -- but they work in fundamentally different ways. Clipping uses a geometric shape to define a hard boundary: everything inside the shape is visible, everything outside is hidden. Masking uses an image (or gradient) whose luminance or alpha values determine visibility, allowing soft transitions and partial transparency. Together, these tools unlock creative possibilities for hero sections, image reveals, decorative dividers, and artistic layouts that would otherwise require image editing software.

Before CSS clipping and masking, designers had to export pre-cropped images or use complex workarounds with overflow hidden and absolute positioning. Today, the clip-path property and the mask-image property give you full control directly in CSS. You can even animate these properties for stunning reveal effects and transitions. In this lesson, you will learn both clipping and masking in depth, understand when to use each one, and build practical examples that you can use in real projects.

Understanding clip-path

The clip-path property defines a clipping region for an element. Only the portion of the element that falls inside the clipping region is displayed; everything outside is completely hidden and does not receive pointer events. The clipping region can be a basic shape, an SVG path reference, or a combination using geometry box values.

The key thing to understand about clipping is that it creates a hard edge. There is no gradual fade or transparency -- a pixel is either fully visible (inside the clip path) or fully hidden (outside the clip path). This makes clipping ideal for geometric shapes, angular designs, and clean cutouts.

Basic clip-path Syntax

/* No clipping (default) */
.element {
    clip-path: none;
}

/* Using a basic shape */
.element {
    clip-path: circle(50%);
}

/* Using an SVG clipPath reference */
.element {
    clip-path: url(#myClipPath);
}

/* Using a geometry box (clips to content-box, padding-box, etc.) */
.element {
    clip-path: padding-box;
}

/* Combining a shape with a geometry box */
.element {
    clip-path: circle(50%) border-box;
}
Note: The clip-path property replaces the older clip property (which only accepted rect() and only worked on absolutely positioned elements). The clip property is deprecated and should not be used in new projects. Always use clip-path instead.

Basic Shapes with clip-path

CSS provides four basic shape functions that you can use with clip-path: circle(), ellipse(), inset(), and polygon(). Each creates a different geometric shape, and you can customize them with parameters to achieve exactly the shape you need.

circle()

The circle() function creates a circular clipping region. It accepts a radius and an optional position. The radius can be a length, a percentage, or the keywords closest-side and farthest-side. The position defaults to the center of the element.

Clipping with circle()

/* Circle at center with 50% radius (fills the smaller dimension) */
.avatar {
    clip-path: circle(50%);
}

/* Circle with a fixed radius */
.profile-pic {
    clip-path: circle(80px);
}

/* Circle with custom center position */
.spotlight {
    clip-path: circle(40% at 30% 50%);
}

/* Circle positioned at top-left */
.corner-reveal {
    clip-path: circle(100px at 0% 0%);
}

/* Using closest-side: radius extends to the nearest edge */
.fit-circle {
    clip-path: circle(closest-side at 50% 50%);
}

/* Using farthest-side: radius extends to the farthest edge */
.full-circle {
    clip-path: circle(farthest-side at 50% 50%);
}

ellipse()

The ellipse() function creates an elliptical clipping region. It accepts two radii (horizontal and vertical) and an optional position. This is useful when you need an oval shape rather than a perfect circle.

Clipping with ellipse()

/* Ellipse centered with 50% horizontal and 40% vertical radius */
.oval-frame {
    clip-path: ellipse(50% 40%);
}

/* Wide ellipse for a banner effect */
.banner {
    clip-path: ellipse(60% 50% at 50% 50%);
}

/* Ellipse positioned off-center */
.artistic {
    clip-path: ellipse(45% 55% at 70% 40%);
}

/* Tall narrow ellipse */
.portal {
    clip-path: ellipse(30% 50% at 50% 50%);
}

inset()

The inset() function creates a rectangular clipping region with optional rounded corners. It accepts one to four inset values (similar to margin/padding shorthand) measuring how far inward from each edge to clip. You can also add round followed by a border-radius value to create rounded rectangles.

Clipping with inset()

/* Inset 20px from all sides */
.trimmed {
    clip-path: inset(20px);
}

/* Different insets: top right bottom left */
.custom-inset {
    clip-path: inset(10px 20px 30px 40px);
}

/* Inset with rounded corners */
.rounded-clip {
    clip-path: inset(10px round 20px);
}

/* Inset with different corner radii */
.fancy-clip {
    clip-path: inset(5% round 20px 0 20px 0);
}

/* No inset but with rounded corners (clips just the corners) */
.corner-clip {
    clip-path: inset(0 round 50px);
}

/* Pill shape */
.pill {
    clip-path: inset(0 round 999px);
}
Tip: The inset() function with round is extremely useful for creating rounded clipping regions that differ from the element's actual border-radius. Unlike border-radius which clips the background but not child elements that overflow, clip-path: inset(0 round ...) clips everything -- including overflowing children, box shadows, and pseudo-elements.

polygon()

The polygon() function is the most versatile basic shape. It creates a clipping region defined by a series of coordinate pairs (vertices). You can create any shape that can be described by straight edges connecting points. Each point is defined as a pair of x and y percentages or lengths.

Clipping with polygon()

/* Triangle pointing up */
.triangle-up {
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* Triangle pointing right */
.triangle-right {
    clip-path: polygon(0% 0%, 100% 50%, 0% 100%);
}

/* Diamond shape */
.diamond {
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}

/* Pentagon */
.pentagon {
    clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
}

/* Hexagon */
.hexagon {
    clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}

/* Star shape */
.star {
    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 */
.arrow-right {
    clip-path: polygon(0% 20%, 70% 20%, 70% 0%, 100% 50%, 70% 100%, 70% 80%, 0% 80%);
}

/* Slanted section (parallelogram) */
.slanted {
    clip-path: polygon(0% 0%, 100% 0%, 100% 85%, 0% 100%);
}

/* Angled bottom edge */
.angled-bottom {
    clip-path: polygon(0% 0%, 100% 0%, 100% 80%, 0% 100%);
}

Custom Shapes with polygon()

The polygon() function truly shines when you create custom shapes tailored to your design. The key is understanding the coordinate system: 0% 0% is the top-left corner, 100% 0% is the top-right, 100% 100% is the bottom-right, and 0% 100% is the bottom-left. By placing points between these corners, you can create virtually any shape with straight edges.

Creative Custom Shapes

/* Chevron/ribbon shape */
.chevron {
    clip-path: polygon(0% 0%, 85% 0%, 100% 50%, 85% 100%, 0% 100%, 15% 50%);
}

/* Notched rectangle (price tag) */
.price-tag {
    clip-path: polygon(
        15% 0%, 100% 0%, 100% 100%, 15% 100%, 0% 50%
    );
}

/* Cross/plus shape */
.cross {
    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%
    );
}

/* House shape */
.house {
    clip-path: polygon(50% 0%, 100% 35%, 100% 100%, 0% 100%, 0% 35%);
}

/* Message bubble (speech bubble without the tail) */
.bubble {
    clip-path: polygon(
        0% 0%, 100% 0%, 100% 75%,
        75% 75%, 65% 100%, 55% 75%, 0% 75%
    );
}

/* Decorative wave-like bottom */
.wavy-section {
    clip-path: polygon(
        0% 0%, 100% 0%, 100% 85%,
        90% 90%, 80% 85%, 70% 90%,
        60% 85%, 50% 90%, 40% 85%,
        30% 90%, 20% 85%, 10% 90%, 0% 85%
    );
}
Tip: Designing polygon shapes by hand can be tricky. Use online tools like Clippy (bennettfeely.com/clippy/) to visually create clip-path polygons. You can drag the points, see the result in real time, and copy the generated CSS code. This is especially helpful for complex shapes with many vertices.

clip-path with SVG url()

For shapes that require curves (not just straight lines), you can reference an SVG <clipPath> element. SVG clip paths support curved paths via the <path> element, enabling smooth organic shapes, curves, and complex outlines that polygon() cannot achieve.

Using SVG clipPath with clip-path

<!-- Define the SVG clipPath in your HTML (can be hidden) -->
<svg width="0" height="0" style="position: absolute;">
    <defs>
        <clipPath id="blobClip" clipPathUnits="objectBoundingBox">
            <path d="M0.5,0 C0.75,0 1,0.25 1,0.5
                       C1,0.75 0.75,1 0.5,1
                       C0.25,1 0,0.75 0,0.5
                       C0,0.25 0.25,0 0.5,0" />
        </clipPath>
    </defs>
</svg>

<!-- Apply the clip path in CSS -->
<style>
.blob-image {
    clip-path: url(#blobClip);
    width: 300px;
    height: 300px;
}
</style>

More SVG Clip Path Examples

<svg width="0" height="0" style="position: absolute;">
    <defs>
        <!-- Rounded wave clip path -->
        <clipPath id="waveClip" clipPathUnits="objectBoundingBox">
            <path d="M0,0 H1 V0.8
                       C0.85,0.95 0.65,0.7 0.5,0.85
                       C0.35,1 0.15,0.75 0,0.9 Z" />
        </clipPath>

        <!-- Heart shape -->
        <clipPath id="heartClip" clipPathUnits="objectBoundingBox">
            <path d="M0.5,0.15
                       C0.5,0 0.05,-0.1 0.05,0.3
                       C0.05,0.6 0.5,1 0.5,1
                       C0.5,1 0.95,0.6 0.95,0.3
                       C0.95,-0.1 0.5,0 0.5,0.15 Z" />
        </clipPath>
    </defs>
</svg>

<style>
.hero-image {
    clip-path: url(#waveClip);
}

.heart-photo {
    clip-path: url(#heartClip);
}
</style>
Note: When using clipPathUnits="objectBoundingBox", all coordinates in the SVG path must be between 0 and 1 (representing 0% to 100% of the element's bounding box). This makes the clip path scale proportionally with the element. If you omit clipPathUnits, the coordinates use the SVG's viewport units, which does not scale automatically.

Animating clip-path

One of the most exciting features of clip-path is that basic shapes can be animated with CSS transitions and keyframe animations. When you transition between two clip-path values that use the same shape function with the same number of points, the browser smoothly interpolates between them. This enables stunning reveal effects, morphing shapes, and interactive hover animations.

Animating clip-path on Hover

/* Circle expand reveal on hover */
.image-reveal {
    clip-path: circle(0% at 50% 50%);
    transition: clip-path 0.6s ease-out;
}

.image-reveal:hover {
    clip-path: circle(75% at 50% 50%);
}

/* Polygon morph: rectangle to diamond */
.morph-shape {
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
    transition: clip-path 0.5s ease-in-out;
}

.morph-shape:hover {
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}

/* Inset reveal animation */
.card-image {
    clip-path: inset(50% round 20px);
    transition: clip-path 0.4s ease-out;
}

.card-image:hover {
    clip-path: inset(0% round 20px);
}

Keyframe Animations with clip-path

/* Circular reveal animation */
@keyframes circleReveal {
    from {
        clip-path: circle(0% at 50% 50%);
    }
    to {
        clip-path: circle(100% at 50% 50%);
    }
}

.page-enter {
    animation: circleReveal 1s ease-out forwards;
}

/* Wipe from left to right */
@keyframes wipeRight {
    from {
        clip-path: inset(0 100% 0 0);
    }
    to {
        clip-path: inset(0 0 0 0);
    }
}

.text-reveal {
    animation: wipeRight 0.8s ease-out forwards;
}

/* Polygon shape morphing animation */
@keyframes morphShape {
    0% {
        clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    }
    25% {
        clip-path: polygon(20% 0%, 100% 20%, 80% 100%, 0% 80%);
    }
    50% {
        clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
    }
    75% {
        clip-path: polygon(20% 20%, 80% 0%, 100% 80%, 0% 100%);
    }
    100% {
        clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    }
}

.morphing-element {
    animation: morphShape 6s ease-in-out infinite;
}
Warning: When animating polygon(), both the start and end states must have the exact same number of vertices. If the polygon at 0% has 4 points and the polygon at 100% has 6 points, the animation will not interpolate -- it will snap abruptly. If you need to morph between shapes with different point counts, add extra points to the simpler shape that overlap with existing points.

CSS Masks: mask-image

While clipping creates hard edges, CSS masking allows soft, gradient-based visibility control. The mask-image property applies an image as a mask to an element. The mask image's alpha channel (or luminance, depending on mask-mode) determines which parts of the element are visible. Where the mask is fully opaque, the element is fully visible. Where the mask is fully transparent, the element is hidden. Where the mask is semi-transparent, the element is partially visible.

This is conceptually similar to how layer masks work in Photoshop or GIMP: a white mask reveals, a black mask conceals, and gray values produce partial transparency.

Basic mask-image Syntax

/* No mask (default) */
.element {
    -webkit-mask-image: none;
    mask-image: none;
}

/* Mask with a gradient */
.element {
    -webkit-mask-image: linear-gradient(to bottom, black, transparent);
    mask-image: linear-gradient(to bottom, black, transparent);
}

/* Mask with an image */
.element {
    -webkit-mask-image: url("mask-shape.png");
    mask-image: url("mask-shape.png");
}

/* Mask with an SVG */
.element {
    -webkit-mask-image: url("mask.svg");
    mask-image: url("mask.svg");
}
Warning: CSS masking still requires the -webkit- prefix for Chrome, Safari, and Edge. Always include both the prefixed and unprefixed properties. Firefox supports the unprefixed version. Omitting the prefix will cause the mask to not render in WebKit-based browsers, which represent the majority of users.

Masking with Gradients

Gradient masks are the most common and practical use of CSS masking. By applying a gradient as a mask, you can create fade-out effects, vignettes, and soft-edged reveals without any image files. The gradient's color stops control visibility: fully opaque areas (like black or rgba(0,0,0,1)) show the element, and fully transparent areas (like transparent or rgba(0,0,0,0)) hide it.

Gradient Mask: Fade Out to Bottom

/* Content fades out at the bottom -- perfect for "read more" previews */
.text-preview {
    max-height: 200px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 60%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 60%,
        transparent 100%
    );
}

/* Fade out on both top and bottom */
.scrollable-list {
    -webkit-mask-image: linear-gradient(
        to bottom,
        transparent 0%,
        black 10%,
        black 90%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        transparent 0%,
        black 10%,
        black 90%,
        transparent 100%
    );
}

Gradient Mask: Horizontal and Radial Fades

/* Fade from left to right */
.side-fade {
    -webkit-mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 20%,
        black 80%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 20%,
        black 80%,
        transparent 100%
    );
}

/* Vignette effect using radial gradient */
.vignette {
    -webkit-mask-image: radial-gradient(
        ellipse 70% 70% at center,
        black 0%,
        black 50%,
        transparent 100%
    );
    mask-image: radial-gradient(
        ellipse 70% 70% at center,
        black 0%,
        black 50%,
        transparent 100%
    );
}

/* Spotlight effect -- only center is visible */
.spotlight {
    -webkit-mask-image: radial-gradient(
        circle 150px at var(--x, 50%) var(--y, 50%),
        black 0%,
        transparent 100%
    );
    mask-image: radial-gradient(
        circle 150px at var(--x, 50%) var(--y, 50%),
        black 0%,
        transparent 100%
    );
}

/* Diagonal fade */
.diagonal-fade {
    -webkit-mask-image: linear-gradient(
        135deg,
        black 0%,
        black 40%,
        transparent 70%
    );
    mask-image: linear-gradient(
        135deg,
        black 0%,
        black 40%,
        transparent 70%
    );
}

Masking with Images

Image-based masks use the alpha channel of a PNG image (or the luminance of any image) to control visibility. This is perfect for complex organic shapes like paint splatters, brush strokes, textures, and ornamental frames that cannot be achieved with gradients alone. The mask image should typically be a PNG with transparency -- the opaque areas reveal the masked element, and the transparent areas hide it.

Image-Based Masks

/* Mask with a PNG image (alpha channel) */
.artistic-photo {
    -webkit-mask-image: url("brush-stroke.png");
    mask-image: url("brush-stroke.png");
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* Mask with an SVG shape */
.decorative-frame {
    -webkit-mask-image: url("ornate-frame.svg");
    mask-image: url("ornate-frame.svg");
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
}

/* Texture mask for a grungy effect */
.grunge-overlay {
    -webkit-mask-image: url("grunge-texture.png");
    mask-image: url("grunge-texture.png");
    -webkit-mask-size: cover;
    mask-size: cover;
}

Mask Sizing, Repeating, and Positioning

Just like background images, mask images have companion properties that control their size, repetition, and position within the element. These properties mirror the background-* equivalents and follow the same syntax.

mask-size, mask-repeat, and mask-position

/* mask-size: controls the size of the mask image */
.element {
    -webkit-mask-size: cover;          /* Scale to cover the entire element */
    mask-size: cover;

    -webkit-mask-size: contain;        /* Scale to fit within the element */
    mask-size: contain;

    -webkit-mask-size: 200px 100px;    /* Explicit width and height */
    mask-size: 200px 100px;

    -webkit-mask-size: 50%;            /* Percentage of the element */
    mask-size: 50%;
}

/* mask-repeat: controls tiling behavior */
.tiled-mask {
    -webkit-mask-image: url("dot-pattern.png");
    mask-image: url("dot-pattern.png");
    -webkit-mask-size: 50px 50px;
    mask-size: 50px 50px;
    -webkit-mask-repeat: repeat;       /* Tile in both directions */
    mask-repeat: repeat;
}

.no-tile {
    -webkit-mask-repeat: no-repeat;    /* Show once, no tiling */
    mask-repeat: no-repeat;
}

.horizontal-tile {
    -webkit-mask-repeat: repeat-x;     /* Tile horizontally only */
    mask-repeat: repeat-x;
}

/* mask-position: where to place the mask */
.positioned-mask {
    -webkit-mask-image: url("shape.png");
    mask-image: url("shape.png");
    -webkit-mask-position: center;     /* Center the mask */
    mask-position: center;

    -webkit-mask-position: top right;  /* Position at top-right */
    mask-position: top right;

    -webkit-mask-position: 20px 50px;  /* Offset from top-left */
    mask-position: 20px 50px;
}

mask-composite

The mask-composite property controls how multiple mask layers are combined when you apply more than one mask image. This enables complex masking effects by combining, subtracting, intersecting, or excluding mask regions. You can create donut shapes, combine geometric and gradient masks, and build intricate reveal patterns.

Combining Multiple Masks with mask-composite

/* Donut shape: subtract an inner circle from an outer circle */
.donut {
    -webkit-mask-image:
        radial-gradient(circle 50px at center, black 100%, transparent 100%),
        radial-gradient(circle 100px at center, black 100%, transparent 100%);
    mask-image:
        radial-gradient(circle 50px at center, black 100%, transparent 100%),
        radial-gradient(circle 100px at center, black 100%, transparent 100%);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
}

/* Intersection: only show where both masks overlap */
.intersection {
    -webkit-mask-image:
        linear-gradient(to right, black, transparent),
        linear-gradient(to bottom, black, transparent);
    mask-image:
        linear-gradient(to right, black, transparent),
        linear-gradient(to bottom, black, transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

/* Add multiple mask shapes together */
.combined {
    -webkit-mask-image:
        radial-gradient(circle 60px at 30% 50%, black 100%, transparent 100%),
        radial-gradient(circle 60px at 70% 50%, black 100%, transparent 100%);
    mask-image:
        radial-gradient(circle 60px at 30% 50%, black 100%, transparent 100%),
        radial-gradient(circle 60px at 70% 50%, black 100%, transparent 100%);
    -webkit-mask-composite: source-over;
    mask-composite: add;
}
Note: The -webkit-mask-composite property uses different keyword values than the standard mask-composite. WebKit uses source-over, source-in, source-out, xor, while the standard uses add, intersect, subtract, exclude. Always include both properties with their respective values for cross-browser compatibility.

Clipping vs Masking: When to Use Each

Both clipping and masking hide parts of an element, but they serve different purposes. Understanding when to use each will help you choose the right tool for each design situation.

Comparison: Clipping vs Masking

/*
 * CLIPPING (clip-path)
 * ====================
 * - Creates HARD edges (fully visible or fully hidden)
 * - Uses geometric shapes (circle, ellipse, polygon, SVG paths)
 * - Better browser support (works without prefix in modern browsers)
 * - Can be animated between same-type shapes
 * - Lighter on performance
 * - Best for: geometric shapes, angular designs, shape morphing
 *
 * MASKING (mask-image)
 * ====================
 * - Creates SOFT edges (supports partial transparency)
 * - Uses images and gradients as masks
 * - Requires -webkit- prefix for most browsers
 * - Can create fade effects, textures, vignettes
 * - Slightly heavier on performance
 * - Best for: fade effects, texture overlays, organic shapes
 */

/* Use clip-path for geometric shapes */
.hexagon-avatar {
    clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}

/* Use mask for fade effects */
.fade-preview {
    -webkit-mask-image: linear-gradient(to bottom, black 70%, transparent);
    mask-image: linear-gradient(to bottom, black 70%, transparent);
}

Creative Hero Sections and Dividers

Clipping and masking are ideal for creating visually striking hero sections with angled edges, diagonal dividers between page sections, and decorative transitions that break away from the typical rectangular layout.

Angled Section Divider

/* Hero section with angled bottom */
.hero-section {
    background: linear-gradient(135deg, #667eea, #764ba2);
    padding: 100px 20px 150px;
    clip-path: polygon(0% 0%, 100% 0%, 100% 85%, 0% 100%);
}

/* Next section overlaps the angle */
.content-section {
    margin-top: -80px;
    padding: 100px 20px;
    position: relative;
    z-index: 1;
}

/* Curved section divider */
.curved-hero {
    background: url("hero-bg.jpg") center/cover;
    padding: 100px 20px 120px;
    clip-path: ellipse(120% 100% at 50% 0%);
}

/* V-shaped divider */
.v-divider {
    background: #2c3e50;
    padding: 80px 20px 100px;
    clip-path: polygon(0% 0%, 100% 0%, 100% 80%, 50% 100%, 0% 80%);
}

/* Wave-shaped hero bottom */
.wave-hero {
    position: relative;
    background: #3498db;
    padding: 100px 20px 80px;
}

.wave-hero::after {
    content: "";
    position: absolute;
    bottom: -1px;
    left: 0;
    width: 100%;
    height: 80px;
    background: white;
    clip-path: polygon(
        0% 100%, 100% 100%, 100% 0%,
        85% 40%, 70% 10%, 55% 50%,
        40% 20%, 25% 60%, 10% 30%, 0% 70%
    );
}

Creative Hero with Mask Fade

/* Hero image that fades into the background */
.hero-with-fade {
    position: relative;
    min-height: 100vh;
}

.hero-background {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: url("hero.jpg") center/cover;
    -webkit-mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 50%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 50%,
        transparent 100%
    );
}

/* Split hero: image on one side fading into color */
.split-hero {
    background: linear-gradient(135deg, #1a1a2e, #16213e);
}

.split-hero .hero-image {
    position: absolute;
    right: 0;
    top: 0;
    width: 60%;
    height: 100%;
    background: url("hero-photo.jpg") center/cover;
    -webkit-mask-image: linear-gradient(
        to left,
        black 0%,
        black 40%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to left,
        black 0%,
        black 40%,
        transparent 100%
    );
}

/* Diagonal clip with animated reveal */
@keyframes diagonalReveal {
    from {
        clip-path: polygon(0% 0%, 0% 0%, 0% 100%, 0% 100%);
    }
    to {
        clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
    }
}

.hero-image-reveal {
    animation: diagonalReveal 1.2s ease-out 0.3s both;
}

Browser Support and -webkit- Prefixes

Browser support for clipping and masking varies. The clip-path property with basic shapes has excellent support across all modern browsers without prefixes. SVG-based clip-path (using url()) has some inconsistencies in older browsers. CSS masking requires the -webkit- prefix in all WebKit/Blink-based browsers (Chrome, Edge, Safari, Opera), while Firefox supports the unprefixed version.

Cross-Browser Clipping and Masking

/* clip-path: good support, no prefix needed for basic shapes */
.clipped {
    /* Works in all modern browsers */
    clip-path: polygon(0% 0%, 100% 0%, 100% 80%, 0% 100%);
}

/* mask-image: ALWAYS include -webkit- prefix */
.masked {
    /* WebKit/Blink browsers (Chrome, Edge, Safari, Opera) */
    -webkit-mask-image: linear-gradient(to bottom, black, transparent);
    -webkit-mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;

    /* Standard (Firefox) */
    mask-image: linear-gradient(to bottom, black, transparent);
    mask-size: 100% 100%;
    mask-repeat: no-repeat;
}

/* Provide a fallback for older browsers */
.clipped-with-fallback {
    /* Fallback: use overflow hidden with border-radius */
    border-radius: 50%;
    overflow: hidden;

    /* Modern browsers will use clip-path and ignore border-radius for clipping */
    clip-path: circle(50%);
}

/* Feature query for mask support */
@supports (-webkit-mask-image: linear-gradient(black, transparent)) or
          (mask-image: linear-gradient(black, transparent)) {
    .enhanced-fade {
        -webkit-mask-image: linear-gradient(to bottom, black 80%, transparent);
        mask-image: linear-gradient(to bottom, black 80%, transparent);
    }
}
Tip: When writing mask properties, always put the -webkit- prefixed version first, followed by the standard unprefixed version. This ensures that browsers that support the standard will use it (the last declaration wins), while WebKit browsers fall back to the prefixed version. Apply this to all mask sub-properties: mask-image, mask-size, mask-repeat, mask-position, and mask-composite.

Practical Examples

Let us bring everything together with complete, real-world examples that combine clipping, masking, animations, and responsive design.

Image Gallery with Clip-Path Hover Effects

/* Gallery grid with clip-path reveal on hover */
.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
}

.gallery-item {
    position: relative;
    overflow: hidden;
    aspect-ratio: 4/3;
}

.gallery-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.gallery-item .overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    clip-path: circle(0% at 50% 50%);
    transition: clip-path 0.5s ease-out;
}

.gallery-item:hover .overlay {
    clip-path: circle(75% at 50% 50%);
}

.gallery-item:hover img {
    transform: scale(1.1);
}

Scroll-Triggered Fade with Mask

/* Scrollable content area with fade edges */
.scroll-container {
    max-height: 400px;
    overflow-y: auto;
    -webkit-mask-image: linear-gradient(
        to bottom,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
}

/* Horizontal scrolling strip with edge fade */
.horizontal-scroll {
    display: flex;
    overflow-x: auto;
    gap: 16px;
    padding: 20px 0;
    -webkit-mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
}

Animated Page Transition with clip-path

/* Full-page circular reveal transition */
@keyframes pageRevealCircle {
    0% {
        clip-path: circle(0% at 50% 50%);
    }
    100% {
        clip-path: circle(150% at 50% 50%);
    }
}

/* Full-page diagonal wipe transition */
@keyframes pageRevealDiagonal {
    0% {
        clip-path: polygon(0% 0%, 0% 0%, 0% 100%, 0% 100%);
    }
    100% {
        clip-path: polygon(0% 0%, 200% 0%, 100% 100%, 0% 100%);
    }
}

.page-transition-circle {
    animation: pageRevealCircle 0.8s ease-out forwards;
}

.page-transition-diagonal {
    animation: pageRevealDiagonal 0.6s ease-in-out forwards;
}

/* Staggered reveal for child elements */
@keyframes slideClipReveal {
    from {
        clip-path: inset(0 100% 0 0);
    }
    to {
        clip-path: inset(0 0 0 0);
    }
}

.reveal-item {
    animation: slideClipReveal 0.5s ease-out both;
}

.reveal-item:nth-child(1) { animation-delay: 0.1s; }
.reveal-item:nth-child(2) { animation-delay: 0.2s; }
.reveal-item:nth-child(3) { animation-delay: 0.3s; }
.reveal-item:nth-child(4) { animation-delay: 0.4s; }

Exercise 1: Profile Card with Clip-Path Shape

Create a profile card component with a hexagonal avatar. The card should have a background gradient at the top that is clipped with an angled bottom edge using clip-path: polygon(). Center a circular avatar image on the boundary where the gradient meets the white card body. On hover, transition the avatar from clip-path: circle(50%) to clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%) (hexagon) over 0.4 seconds. Below the avatar, show the user's name, title, and social links. Add a subtle polygon-based decorative element in the background that morphs its shape on hover. Ensure the card works well at different screen sizes by using percentage-based clip-path values rather than fixed pixel values.

Exercise 2: Masked Image Gallery with Fade Transitions

Build a responsive image gallery where each image has a gradient mask that fades out the bottom 20% of the image (using mask-image: linear-gradient(to bottom, black 80%, transparent)). The image title and description should appear in the faded area, creating a text-over-image effect without darkening overlays. On hover, animate the mask so the entire image becomes fully visible (transition the mask to linear-gradient(to bottom, black 100%, black)) while simultaneously scaling the image up slightly. Add a second layer of masking using mask-composite that creates a subtle vignette around the edges using a radial gradient. The gallery should use a CSS Grid layout with auto-fill columns. Include a @supports query that provides a solid-background fallback for browsers that do not support CSS masks.