clip-path tricks
clip-path is animatable, GPU-accelerated, and unlocks 5+ patterns that would otherwise require canvas or SVG.
Raw markdown On GitHubreferences/motion/clip-path-tricks.md
Taught in
clip-path is the most underrated animatable CSS property. It's GPU-accelerated, interpolatable when both states use the same shape function, and unlocks effects that would otherwise need canvas, SVG, or complex JS.
The inset shape — primary tool
clip-path: inset(top right bottom left);
clip-path: inset(0 0 0 0); /* fully visible */
clip-path: inset(0 100% 0 0); /* clipped from the right */
clip-path: inset(0 0 50% 0); /* bottom half hidden */inset() is the workhorse. It clips from each edge. Both ends of a transition must use the same inset() for interpolation to work.
Pattern 1 — tabs with perfect color transition
The classic. A tab pill background that slides between tabs and the text inside flips color correctly during the transition (not before, not after).
.tab-pill {
background: var(--accent);
clip-path: inset(0 0 0 0 round 9999px);
transition: clip-path 250ms var(--ease-out-quart);
}
/* Position the pill behind the active tab; clip everything except active region */The text uses color: var(--text-primary) on transparent regions and color: var(--text-on-accent) on the clipped region — mix-blend-mode or two layered text layers gives the perfect color flip.
Pattern 2 — hold-to-delete
Press and hold a button; the destructive action fills from one side over ~2s, releasing aborts.
.confirm-fill {
position: absolute; inset: 0;
background: var(--danger);
clip-path: inset(0 100% 0 0);
transition: clip-path 200ms var(--ease-out-quart); /* release: snaps back */
}
.confirm-button:active .confirm-fill {
clip-path: inset(0 0 0 0);
transition: clip-path 2s linear; /* press: slow, deliberate */
}The timing is asymmetric on purpose: the press is slow because the user is deciding; the release answers fast. Put a single duration on the base rule and the abort crawls back at the same 2s. linear is right for the fill because it is progress, and it stays interruptible because it's a CSS transition, not a keyframe.
Pattern 3 — image reveal on scroll
img {
clip-path: inset(20% 0 20% 0);
transition: clip-path 800ms var(--ease-out-quart);
}
img.in-view {
clip-path: inset(0 0 0 0);
}Triggered by IntersectionObserver adding .in-view. Reveals from horizontal letterbox to full image. Feels cinematic.
Pattern 4 — comparison slider
The "before/after" image slider. The "after" image is clipped to the right of the slider position.
.after-image {
clip-path: inset(0 var(--slider-x) 0 0);
}--slider-x is updated by JS on pointer move. Because clip-path updates are GPU-cheap, the slider is silky even on 60Hz constrained devices.
Pattern 5 — directional content swap
Two pieces of content stacked; one clips in from the right, the other clips out to the left.
.panel-in { clip-path: inset(0 100% 0 0); }
.panel-in.active { clip-path: inset(0 0 0 0); }
.panel-out.exiting { clip-path: inset(0 0 0 100%); }Gives a directional feel (fly-not-teleport) without transform: translate and without layout shift.
Browser support
clip-path: inset() is universal. clip-path: polygon(), circle(), ellipse(), path() are all supported in modern browsers. Both endpoints of a transition must use the same shape function — you can't transition from inset() to circle().
When to apply
- Any "reveal" or "cover" effect.
- Color-flipping text under a sliding pill.
- Hold-to-confirm interactions.
- Comparison sliders.
- Scroll-driven reveals.
Gotcha
clip-path with border-radius is finicky — combine via clip-path: inset(... round Xpx) instead of using both properties. Mixing them produces strange double-clipping on Safari.
Also: clip-path on a parent affects all children. Want only one child clipped? Apply it to that child directly.
Sources
- Emil Kowalski — clip-path section of emilkowalski/skill.
- Emil Kowalski, skills (MIT, github.com/emilkowalski/skills, a81dbca): skills/animate/RECIPES.md (hold-to-confirm timing).
- MDN —
clip-pathreference. - Related: transform-opacity-only for the GPU-acceleration baseline.
motionclip-pathcss