← Back to gallery
CSS

Clip-path Reveal Transition

A layout stable reveal transition that keeps the element box fixed while clip-path shapes expose content. Three expression methods separate inset wipes, circle-origin reveals, and polygon shutters before mapping them to product surfaces.

clip-pathrevealtransitioninsetcirclepolygonprefers-reduced-motion

Visual effects / clip-path / reveal

Clip-path Reveal Transition

Three fixed-box reveal transitions that differ by clip-path expression method: an inset wipe, a circle-origin open, and a polygon shutter. Geometry stays stable; only the painted visible region changes.

inset() / edge wipe

Inset Wipe

A rectangular edge wipe using inset() for stable button, card, or link states.

  • inset()
  • edge wipe
  • fixed box

circle() / radial origin

Circle Origin

A radial open using circle() so the origin point becomes the expression method.

  • circle()
  • origin
  • radial

polygon() / angled points

Polygon Shutter

An angular shutter using polygon() points for a sharper editorial reveal.

  • polygon()
  • shutter
  • points

Clip-path reveal inspector

Inset Wipe

hover/focus target
  • inset()
  • edge wipe
  • fixed box

The reveal layer starts clipped to the right edge with clip-path: inset(). On hover or focus it wipes across a fixed-width target without changing padding, width, or line-height.

Helped you ship something? 🐟 Send my cat a churu

/* Inset wipe: reveal a rectangular paint layer without changing width, padding, or line-height. */
.clip-reveal {
  position: relative;
  display: inline-grid;
  place-items: center;
  min-inline-size: 13rem;
  aspect-ratio: 4 / 1;
  overflow: hidden;
  border: 1px solid #334155;
  border-radius: 16px;
  color: #dff8ff;
  text-decoration: none;
  isolation: isolate;
}

.clip-reveal__base,
.clip-reveal__panel {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  font: 800 .9rem/1 system-ui, sans-serif;
}

.clip-reveal__panel {
  color: #04111f;
  background: linear-gradient(90deg, #67e8f9, #22d3ee);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.36s cubic-bezier(.2,.8,.2,1);
}

.clip-reveal:is(:hover, :focus-visible) .clip-reveal__panel {
  clip-path: inset(0 0% 0 0);
}

.clip-reveal:focus-visible {
  outline: 2px solid #67e8f9;
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .clip-reveal__panel {
    transition-duration: 0s;
    clip-path: inset(0 0% 0 0);
  }
}

How to make this

A clip-path reveal keeps the element box fixed and changes only the visible paint region, so content can wipe, open, or shutter without reflowing nearby UI.

html
1<a class="clip-reveal-card" href="/work">  <span class="clip-reveal-card__base">View case study</span>  <span class="clip-reveal-card__panel" aria-hidden="true">View case study</span></a> <style>.clip-reveal-card {  position: relative;  display: inline-grid;  place-items: center;  min-inline-size: 13rem;12  aspect-ratio: 4 / 1;  overflow: hidden;  border: 1px solid #334155;  border-radius: 16px;  color: #dbeafe;  text-decoration: none;  isolation: isolate;} .clip-reveal-card__base,.clip-reveal-card__panel {  grid-area: 1 / 1;  display: grid;  place-items: center;  width: 100%;  height: 100%;  font: 800 .9rem/1 system-ui, sans-serif;} .clip-reveal-card__panel {  color: #04111f;  background: linear-gradient(90deg, #67e8f9, #a78bfa);34  clip-path: inset(0 100% 0 0);  transition: clip-path .36s cubic-bezier(.2,.8,.2,1);} 38.clip-reveal-card:is(:hover, :focus-visible) .clip-reveal-card__panel {  clip-path: inset(0 0 0 0);} .clip-reveal-card:focus-visible {  outline: 2px solid #67e8f9;  outline-offset: 4px;} 47@media (prefers-reduced-motion: reduce) {  .clip-reveal-card__panel {    transition-duration: 0s;    clip-path: inset(0 0 0 0);  }}</style>

Annotated snippet

  1. Line 1Use one real interactive element as the stable target. The reveal layer is decorative, so duplicate label text stays aria-hidden and the link remains the semantic control.
    PitfallShould the clipped layer contain the only readable content?

    No. Keep the real label or media available in the base element and make duplicate decorative reveal layers aria-hidden. If clip-path fails or motion is disabled, the control should still make sense.

  2. Line 12Reserve the final box before the reveal starts. Clip-path should change paint visibility only; width, height, padding, and line-height must not become part of the animation.

    BEFORE animates width to fake a reveal, so the neighboring control moves. AFTER keeps the same height: 86px row and animates clip-path inside a fixed box, so surrounding UI stays put.

    PitfallWhy is clip-path better than animating width for this reveal?

    Clip-path changes the painted visible region without changing the element box. Width, padding, height, and line-height changes can push nearby controls and make the reveal feel like a layout bug.

  3. Line 34The hidden state lives in clip-path. Swap inset(), circle(), or compatible polygon() points when the expression method changes; do not create separate slugs just for color or domain variants.

    Both examples use the same fixed target size and timing. The difference is the expression method: inset() reads as a directional wipe, while circle() reads as an origin-based reveal.

    PitfallHow should I choose between inset, circle, and polygon reveals?

    Choose by expression method. Use inset() for directional wipes, circle() for origin-based opens, and polygon() when the points create an angular shutter. Domain or color alone is not enough to make a new pattern.

  4. Line 38Hover and focus-visible share the same reveal. Keyboard users should get the same state change, and the separate focus outline remains the location cue.
    PitfallHow do I keep a clip-path reveal keyboard-accessible?

    Pair :hover with :focus-visible and keep an outline outside the clipped surface. The reveal can decorate the state, but the focus ring should remain visible even when the panel is fully revealed.

  5. Line 47Reduced motion pins the final visible state. Do not replace a wipe with a looped fade or pulse; remove the transition and keep the state legible.
    PitfallWhat should reduced motion do for clip-path reveals?

    Remove the transition and show the final revealed state immediately. The user still sees the active state without watching a wipe, circle, or shutter move.

Other pitfalls

Can clipped content still receive pointer events?
Yes. Clipping changes paint, not necessarily your intended interaction model. Keep the interactive target on the stable parent and set decorative reveal layers to pointer-events: none if they can intercept input.
Can every clip-path shape interpolate safely?
No. Basic inset() and circle() are the safest choices. Polygon animation needs compatible point counts and ordering, and older engines can fall back to a static full reveal.
Can clip-path animation become expensive?
It can, especially on large media or many simultaneous cards. Keep the clipped surface small, avoid continuous background loops underneath it, and profile dense grids on lower-powered devices.
Advanced

Swap inset() for a polygon() diagonal slash — the same property, a sharper edge

Same 2.6s cycle, same panel element, same clip-path property, same final fully-visible state. BEFORE animates inset() — the reveal edge is a perfectly vertical line moving from left to right. AFTER animates polygon() between two parallelogram states with matched vertex order — the reveal edge is a 14° diagonal slash. Both fill the same box at the same beat; the slash just gives the wipe a sense of forward motion the vertical edge can not match.

View explanation and full code21 lines

The base recipe uses clip-path: inset() to wipe horizontally from left to right — solid, but the reveal edge is a vertical line, which is the most predictable wipe shape. Premium product buttons (Apple, Linear, Stripe) use a diagonal slash that gives the panel a sense of weight and forward motion. Same clip-path property the base teaches, same :hover/:focus-visible trigger, same layout-stable box — what changes is the shape function: inset() rectangle becomes polygon() parallelogram. The two parallelogram states have the same point order, so clip-path interpolates smoothly between them.

Append these rules inside the <style> block from the base snippet above.

css
/* Advanced: polygon() diagonal slash reveal — extends the base recipe.   Same .clip-reveal-card__panel selector, same :hover/:focus-visible   trigger, same transition timing. The clip-path value swaps from a   rectangular inset() wipe to a parallelogram polygon() — the start   state collapses the parallelogram to zero width on the left edge,   the hover state expands it to cover the full box. Because both   states have the same point count and order (4 vertices), clip-path   can interpolate smoothly. */.clip-reveal-card__panel {  clip-path: polygon(0 0, 0 0, -18% 100%, -18% 100%);  transition: clip-path .42s cubic-bezier(.2, .8, .2, 1);}.clip-reveal-card:is(:hover, :focus-visible) .clip-reveal-card__panel {  clip-path: polygon(0 0, 118% 0, 100% 100%, -18% 100%);}@media (prefers-reduced-motion: reduce) {  .clip-reveal-card__panel {    transition-duration: 0s;    clip-path: polygon(0 0, 118% 0, 100% 100%, -18% 100%);  }}

Notes

Overview

A clip-path reveal transition exposes an already-laid-out element by changing its painted visible region. The target box keeps the same dimensions while a child layer moves from a hidden shape to a visible shape, so neighboring UI does not reflow during the effect.

When to use it

Use it for cards, links, media thumbnails, command buttons, and panel previews where the reveal should feel more authored than a fade but must not push layout. Choose the expression method first: inset() for a directional wipe, circle() for an origin-based open, and polygon() for an angular shutter. Do not split patterns only because the domain or color changes.

How it works

The parent reserves the final geometry with fixed dimensions or aspect-ratio. The reveal layer sits in the same grid area as the base content and starts with a hidden clip-path. Hover and :focus-visibletransition that layer to a compatible visible shape. For polygon animation, keep the same number and order of points so interpolation is predictable.

Production gotchas

Avoid animating width, padding, border thickness, height, or line-height to fake the reveal; those properties can push adjacent controls. Decorative clipped overlays can still sit above the target, so keep input handling on the stable parent and use pointer-events: none when an overlay should never intercept clicks. Large clipped media can repaint heavily, especially in dense grids.

Accessibility

The real label, image alternative, or content summary should remain available outside the decorative clipped layer. Pair :hover with :focus-visible and keep a visible outline outside the clipped area. Under prefers-reduced-motion: reduce, skip the transition and show the final revealed state.

References

Implementation depth

Clip-path reveal is a paint-region transition, not a layout transition. The reusable mechanism is a fixed parent box with a reveal layer whose inset, circle, or polygon shape moves from hidden to visible while the box metrics stay unchanged.

The expression method should drive the example set. Inset wipes, circle-origin reveals, and polygon shutters are different patterns of visibility; changing only product domain or color is not enough. Test focus-visible, pointer handling, reduced motion, and dense-grid paint cost before shipping it widely.