GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

CSS Sticker Peel Effect: 9 Styles, Border to Holo

Among nine css sticker peel effect ideas, one fakes a die-cut look with a white border and shadow, one sweeps rainbow light by pointer angle, and one tilts as

Auto-plays · click a tile to jump to its section · all nine in one zip

Nine sticker peel animation css patterns sit side by side below, each one a plain HTML/CSS/JS file with no library and no build step, so you can drag any single folder out of the archive and open its index.html straight in a browser.

Demo

Hover, click, or drag each card above to see its own version of a sticker peeling, tilting, or snapping into place — every reaction below comes from reading that demo's actual source, not a description written from memory.

Anatomy of nine patterns — where the one layer of calculation runs

Every row below comes from the same 24 sampled frames per pattern, measured after render rather than guessed. Cumulative area is how much of the frame the moving parts covered, added up across those frames; average intensity is how strong the pixel-level change was per moved frame; and frames moved (of 23) counts how many of the 23 frame-to-frame comparisons actually differed.

# Pattern Cumulative area Avg. intensity Frames moved (of 23)
01 White-Border Die-Cut Sticker Card 7.18% 100.8 15
02 Sticker With a Peeling Corner 0.11% 71.9 22
03 Angle-Tracking Holographic Sticker 14.42% 24.9 23
04 Sticker Held Down With Tape Strips 10.05% 78.3 20
05 Folder-Tab Shaped Sticker 1.11% 127.5 15
06 Sticker That Tilts With the Cursor 5.16% 57.7 23
07 Randomly Scattered Sticker Pile 18.39% 101.0 17
08 Drag-to-Place Sticker 21.61% 128.2 16
09 Sticker Sheet With Visible Cut Lines 8.43% 72.3 23

Pattern 08 covers the most cumulative area of the nine at 21.61%, since dragging a chip a full grid cell displaces far more pixels than a corner curling back barely 0.11% under pattern 02, the smallest of the set. Pattern 08 also has the highest average intensity at 128.2, just 0.7 above pattern 05's 127.5 — a snapping chip and a canvas-measured tab edge both change pixels sharply even though their footprints differ in size. Patterns 03, 06, and 09 each move across 23 of the 23 frames, tied for the most, while pattern 03 itself sits at 24.9, the lowest average intensity of the nine, since a slow color and light shift barely registers next to a hard snap or a marching dash line.

Cluster A — a pointer vector becomes an angle every frame (02, 03, 06)

Patterns 02, 03, and 06 each read the pointer's live coordinates and run them through Math.atan2() or a -1..1 normalization to produce an angle or a rotation on every frame — the peel's clip-path angle, the sheen's hue and sweep direction, and the card's rotateX/rotateY all come straight from wherever the pointer currently sits, not from a fixed set of positions.

Cluster B — one scalar drives a fixed formula (01, 04, 09)

Patterns 01, 04, and 09 never need to read a pointer coordinate at paint time — a lift ratio, a corner index, or the elapsed time on its own is enough to compute the box-shadow stack, the tape's tilt angle, or the dash-march offset through a single arithmetic formula.

Cluster C — an input snaps to one of a fixed set of states (05, 07, 08)

Patterns 05, 07, and 08 each resolve their input down to one of a small, fixed set of outcomes — a canvas-measured label width clamped between a minimum and maximum, a seeded hash pulled from a short fixed list, or a pointer position rounded onto a two-by-two grid — rather than settling on a continuous value.

Code per pattern

Nine short, framework-free snippets below — just the one computed layer that decides what actually moves, trimmed out of each pattern's full source file.

01White-Border Die-Cut Sticker Card

Wraps an image or icon in a thick white border with a soft shadow behind it, so it reads like a real printed sticker peeled and stuck onto the page.

White-Border Die-Cut Sticker Card
Open in a new tab

01 · White-Border Die-Cut Sticker Card — Measured area 7.179% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

box-shadowclip-pathpointerenter
  function shadowFor(t) {
    var parts = [];
    for (var i = 1; i <= LIFT_LAYERS; i++) {
      var off = (LAYER_OFFSET * i * t).toFixed(2);
      var blur = (LAYER_BLUR * i * t + 2).toFixed(2);
      var alpha = Math.max(0, LAYER_ALPHA_BASE - LAYER_ALPHA_STEP * i) * t;
      parts.push('0 ' + off + 'px ' + blur + 'px rgba(10,8,14,' + alpha.toFixed(3) + ')');
    }
    return parts.join(', ');
  }

02Sticker With a Peeling Corner

Hovering a card curls one corner back like real paper, revealing a different color hidden underneath — a different demo from 133's combined holographic-badge peel and sweep, since here the peel angle itself follows the pointer position.

Sticker With a Peeling Corner
Open in a new tab

02 · Sticker With a Peeling Corner — Measured area 0.108% · moving frames 22/23Original by GODRICH · SCSS and React ship in the zip · See the password

clip-pathatan2pointermove
  function applyFromPoint(px, py) {
    var r = card.getBoundingClientRect();
    var pivotX = r.right, pivotY = r.top;
    var dx = pivotX - px, dy = py - pivotY;
    var distance = Math.hypot(dx, dy);
    var amt = Math.max(0, Math.min(1, 1 - distance / PEEL_REACH));
    targetSize = amt * PEEL_CORNER_SIZE;
    targetDeg = (Math.atan2(dy, dx) * 180 / Math.PI + 360) % 360;
  }

03Angle-Tracking Holographic Sticker

Layers a thin rainbow sheen over the surface and drags that light across it in step with the pointer's direction and speed — a different demo from 133's static card and badge holographic set, since pointer movement itself drives the sheen's speed here. This self-contained holographic sticker css example needs no image assets, just a conic-gradient layered with mix-blend-mode.

Angle-Tracking Holographic Sticker
Open in a new tab

03 · Angle-Tracking Holographic Sticker — Measured area 14.422% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

conic-gradientmix-blend-modeatan2
  function applyMove(x, y, now) {
    if (lastX !== null) {
      var dt = Math.max(now - lastTime, 1);
      var dx = x - lastX, dy = y - lastY;
      var speed = Math.hypot(dx, dy) / dt; // px/ms
      hue = (hue + speed * HUE_SPEED * (dt / 1000)) % 360;
      sweepPos = (sweepPos + speed * SWEEP_SPEED * (dt / 1000)) % 200;
      sweepAngleTarget = 90 + (Math.atan2(dy, dx) * 180 / Math.PI) % HOLO_SWEEP_ANGLE_RANGE;
    }
    lastX = x; lastY = y; lastTime = now;
  }

04Sticker Held Down With Tape Strips

Lays two translucent tape strips diagonally across a card's corners, so it looks like a scrapbook page taped down by hand.

Sticker Held Down With Tape Strips
Open in a new tab

04 · Sticker Held Down With Tape Strips — Measured area 10.047% · moving frames 20/23Original by GODRICH · SCSS and React ship in the zip · See the password

box-shadowtransform-originrequestAnimationFrame
  // Each tape's tilt comes from one formula, base + i*step, fed a corner index (i) —
  // the sign alternates left/right per corner, so one arithmetic layer replaces two fixed angles.
  function tapeAngle(i) {
    var sign = i % 2 === 0 ? -1 : 1;
    return sign * (TAPE_BASE_ANGLE + TAPE_ANGLE_STEP * i);
  }
  var baseAngleA = tapeAngle(1);
  var baseAngleB = tapeAngle(2);

05Folder-Tab Shaped Sticker

Cuts the top corner into a folder-tab shape that pokes out, with a label written on the tab to mark a category.

Folder-Tab Shaped Sticker
Open in a new tab

05 · Folder-Tab Shaped Sticker — Measured area 1.111% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

clip-pathmeasureText--tab-w
  // Measures the label's real pixel width on a canvas so the tab always matches its text —
  // the width is measured fresh each time, never a preset guess. The box itself stays fixed
  // at its max width; only the clip-path reveal changes, so no layout reflow is triggered.
  function measureWidth(text) {
    ctx.font = '600 13px -apple-system, "Segoe UI", "Noto Sans KR", sans-serif';
    var w = ctx.measureText(text).width;
    return Math.max(TAB_MIN, Math.min(TAB_MAX, w + TAB_PADDING));
  }

06Sticker That Tilts With the Cursor

Tilts the card slightly in 3D away from wherever the cursor sits on it, while a highlight on the surface moves along so the sticker reads as having real thickness.

Sticker That Tilts With the Cursor
Open in a new tab

06 · Sticker That Tilts With the Cursor — Measured area 5.158% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

rotateXrotateYperspective
  // Normalizes the pointer position to -1..1 around the card's center, then multiplies
  // by MAX_TILT: vertical movement sets rotateX, horizontal sets rotateY (inverted, so pushing up tilts it back).
  function applyFromPoint(px, py) {
    var rect = zone.getBoundingClientRect();
    var nx = ((px - rect.left) / rect.width) * 2 - 1;
    var ny = ((py - rect.top) / rect.height) * 2 - 1;
    nx = Math.max(-1, Math.min(1, nx));
    ny = Math.max(-1, Math.min(1, ny));
    targetRY = nx * MAX_TILT;
    targetRX = -ny * MAX_TILT;
    targetGX = 50 + nx * GLARE_RANGE;
    targetGY = 50 + ny * GLARE_RANGE;
  }

07Randomly Scattered Sticker Pile

Scatters several stickers at slightly different overlapping angles, but always pulls those angles from a fixed, pre-set order so the same layout reappears every time the page loads.

Randomly Scattered Sticker Pile
Open in a new tab

07 · Randomly Scattered Sticker Pile — Measured area 18.395% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · See the password

Math.sinbox-shadowrequestAnimationFrame
  // deterministic scatter (seeded, no Math.random) — a sine hash pulls the same fractional
  // value from one seed every time; equal seeds always produce equal results, nothing random.
  function hash(seed) {
    var v = Math.sin(seed * 12.9898 + 78.233) * 43758.5453;
    return v - Math.floor(v);
  }

  function layoutFor(seed) {
    var h1 = hash(seed);
    var h2 = hash(seed + 100);
    var h3 = hash(seed + 200);
    return {
      rot: (h1 * 2 - 1) * SCATTER_ROTATE_RANGE,
      dx: (h2 * 2 - 1) * SCATTER_POS_RANGE,
      dy: (h3 * 2 - 1) * SCATTER_POS_RANGE
    };
  }

08Drag-to-Place Sticker

Press a sticker and drag it anywhere on the board; releasing sticks it in place with a slight rotation, stacking over whichever stickers are already there.

Drag-to-Place Sticker
Open in a new tab

08 · Drag-to-Place Sticker — Measured area 21.61% · moving frames 16/23Original by GODRICH · SCSS and React ship in the zip · See the password

setPointerCapturetouch-actionMath.round
  // Divides the pointer's coordinate by the GRID cell size, rounds it, and multiplies back —
  // the result always snaps onto a grid cell's center, never a free-floating coordinate.
  function snap(px, py) {
    var col = Math.max(0, Math.min(1, Math.round((px - GRID / 2) / GRID)));
    var row = Math.max(0, Math.min(1, Math.round((py - GRID / 2) / GRID)));
    return { x: col * GRID + GRID / 2, y: row * GRID + GRID / 2 };
  }

09Sticker Sheet With Visible Cut Lines

Draws dashed cut lines across a grid sheet of stickers, and clicking one makes it pop slightly forward as if punched out along that dashed line.

Sticker Sheet With Visible Cut Lines
Open in a new tab

09 · Sticker Sheet With Visible Cut Lines — Measured area 8.425% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

strokeDasharraystrokeDashoffsetaria-pressed
  // Multiplies elapsed time by a march speed and wraps it modulo the dash pattern length,
  // producing a continuously marching stroke-dashoffset, the same cut-line effect used in print layouts.
  function loop(ts) {
    if (!last) last = ts;
    last = ts;
    if (isDemo && !reduce) autoAdvance(ts);
    if (!reduce) {
      var offset = (ts * MARCH_SPEED / 1000) % PATTERN;
      rects.forEach(function (r) { r.style.strokeDashoffset = (-offset).toFixed(2); });
    }
    requestAnimationFrame(loop);
  }

Where values change the UI — angle, size, timing, order

Every pattern above hangs off one variable that does the real work — change that one variable and the whole pattern's feel shifts with it.

01 — the lift ratio

The badge's box-shadow stack comes from shadowFor(t), where t runs from 0 (stuck down) to 1 (fully lifted), so every offset, blur, and opacity across the five layers grows or shrinks in lockstep with that single ratio.

02 — the corner vector

applyFromPoint() turns the distance and Math.atan2() angle between the pointer and the card's top-right corner into the peel size and gradient angle, so the corner only lifts when the pointer is actually close to it.

03 — the pointer velocity

applyMove() divides the pointer's frame-to-frame movement by the elapsed time to get a speed in pixels per millisecond, and that one speed value scales both the hue rotation and the sweep position.

04 — the index arithmetic

tapeAngle(i) returns sign * (TAPE_BASE_ANGLE + TAPE_ANGLE_STEP * i), so the two tape strips' angles come from one formula fed a different corner index, not two separately chosen numbers.

05 — the measured width

measureWidth() reads the label's actual pixel width from a hidden canvas context and clamps it between TAB_MIN and TAB_MAX, so the folder tab's width always matches its real text instead of a guessed value.

06 — the normalized position

applyFromPoint() normalizes the pointer's position inside the card to a -1..1 range on each axis and multiplies it by MAX_TILT, so the card's rotation and the glare's position are both direct reads of where the pointer sits.

07 — the seeded hash

hash(seed) runs a fixed seed through Math.sin() and keeps only its fractional part, so layoutFor() always returns the exact same rotation and offset for the exact same seed — nothing here is drawn fresh.

08 — the grid snap

snap() divides the pointer's raw coordinate by GRID, rounds it, and multiplies back, turning any free pointer position into the nearest grid cell's center.

09 — the elapsed-time modulo

The dash offset comes from (ts * MARCH_SPEED / 1000) % PATTERN, so the cut line's march speed is a direct function of elapsed time rather than a step-by-step keyframe animation.

Where it breaks — accessibility, reduced-motion, keyboard

Pattern 03's card is a native <button>, so a keyboard user can tab to it — but the hue rotation and light sweep only respond to pointermove, unlike patterns 01, 02, and 06, which also listen for focus and blur. Tabbing to the holographic card and pressing Enter changes nothing about the sheen at all, since applyMove() needs an actual coordinate delta to compute a speed; a keyboard-only visitor never sees the effect this pattern exists to show. That gap is real, not a rendering quirk, and it's worth closing with a focus handler before this pattern ships in a real product.

What reduced motion leaves behind

  • 01 die-cut lift — only the lerp easing and the auto-demo loop stop; the box-shadow stack still jumps straight to its lifted or resting state on pointerenter/focus.
  • 02 peeling corner — the size and angle snap to their target instantly instead of gliding, and the demo's own auto-orbit loop stops running.
  • 03 holographic sheen — the hue and sweep position are forced to a fixed zero outright, so the rainbow color cycle and light sweep never play at all, leaving only a static angled sheen.
  • 04 tape strips — the tilt and lift jump straight to their pinned or peeled state, and the auto-demo's alternating peel loop stops.
  • 05 folder tab — the tab width jumps to the new label's width immediately with no glide, and the auto-cycle through the three labels stops.
  • 06 cursor tilt — rotateX/rotateY and the glare position snap to target instantly, and the auto-orbiting demo loop stops.
  • 07 scatter pile — each chip's rotation and offset jump straight to the newly hashed layout, and the timed auto-reshuffle stops.
  • 08 drag-to-place — the chip still snaps to the nearest grid cell on release or arrow key, just without the glide in between, and the auto-cycling demo loop stops entirely.
  • 09 cut lines — the marching dash offset freezes in place entirely instead of animating, though clicking a sticker still punches it forward immediately.

All nine sources above sit behind one shared password, dkbn5f83, so unzipping the archive once gives you matching HTML, CSS, and JS files on disk for every pattern in this article, ready to open directly in a browser.

Three variants and accessibility

Variant Value Feel
Widen the holographic sweep angle range 03 HOLO_SWEEP_ANGLE_RANGE 45deg → 90deg The sweep's base angle range doubles, so even a small pointer move sends the light band swinging sharply across the diagonal — the reaction reads as exaggerated compared to 45deg.
Enlarge the peeling corner size 02 PEEL_CORNER_SIZE 24px → 44px The revealed triangle at full peel nearly doubles in size, so the hidden backing color reads as part of the card's content rather than a corner accent — a subtle hint becomes an obvious reveal.
Narrow the random scatter angle range 07 SCATTER_ROTATE_RANGE 30deg → 10deg With the same hash values, the narrower rotation range leaves stickers looking almost aligned — a carelessly tossed pile turns into a display that's only slightly askew.

Patterns 01, 02, 03, 06, and 08 are each a native <button type="button"> carrying a descriptive aria-label, so Enter and Space already trigger or focus them without any extra keydown code. Pattern 04's pin toggle and pattern 09's two die buttons both carry aria-pressed to announce their on/off state, and pattern 08 additionally listens for arrow-key keydown events so its chip can move between grid cells without a pointer at all.

FAQ

Do these nine css sticker peel effect ideas need a framework or a build step?

No — each pattern is a single self-contained HTML/CSS/JS file with no build step and no framework, so you can open its index.html directly or drop the markup into any page and it behaves the same way.

How do I check whether prefers-reduced-motion is actually respected in this sticker peel effect design?

Turn on "reduce motion" in your OS accessibility settings, then reload any of the nine demos above — each one checks window.matchMedia('(prefers-reduced-motion: reduce)') on load and either drops its animation outright or holds it at a fixed state, a change you can verify directly in that pattern's CSS or JS.

Which of these are stickers that peel off easily, and which need a click or a drag?

Patterns 01, 02, 03, and 06 respond the moment a pointer hovers or moves over them, needing no extra action to react. Patterns 04, 05, 07, 08, and 09 instead wait for a discrete click, drag, or keypress, since a tape strip, a folder tab, a scattered pile, a drag target, and a cut line are each a one-time action rather than a continuous hover state.

Sources: MDN — clip-path, MDN — mix-blend-mode, MDN — Pointer events, MDN — conic-gradient(), MDN — prefers-reduced-motion.

Related reading: 9 CSS Holographic Card Effects (Copy-Paste) · Cute CSS Animations: 9 Kawaii Character Demos · Paper Cutout Design: 9 Layered Landing Sections

Enter the archive password

The password is inside this article. You will find it as you read.