Free Webflow components / Effect: scratch card reveal

Webflow scratch card (scratch to reveal)

A free scratch card for Webflow: visitors rub off a silver layer to reveal a discount code. Copy it, press ⌘V in the Designer, and edit the prize text.

  • JavaScript
  • Responsive

What you receive in Webflow

  • 15 native elements and 11 classes.
  • Breakpoints: mobile landscape (767px).
  • 2 Embed elements keep the CSS or script that Webflow cannot express. To edit an Embed, you must have a paid Webflow site plan.

How to paste it

  1. Click Copy for Webflow.
  2. Open your page in the Webflow Designer.
  3. Press ⌘V or Ctrl+V.

HTML

<section class="scr">
  <div class="scr-inner">
    <h2 class="scr-title">Scratch for your welcome gift</h2>
    <p class="scr-text">Drag across the card with your mouse or finger.</p>
    <div class="scr-card">
      <div class="scr-prize">
        <p class="scr-label">You unlocked</p>
        <p class="scr-code">20% OFF</p>
        <p class="scr-small">Use code <b>HELLO20</b> at checkout</p>
      </div>
      <div class="scr-cover" aria-hidden="true"><span class="scr-cover-text">Scratch here</span></div>
    </div>
  </div>
</section>
<style>
.scr { font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; background: #fdf2f8; color: #500724; padding: 112px 24px; }
.scr-inner { max-width: 560px; margin: 0 auto; text-align: center; }
.scr-title { font-size: 40px; line-height: 1.12; letter-spacing: -0.02em; margin: 0 0 12px; }
.scr-text { font-size: 17px; color: #9d174d; margin: 0 0 40px; }
.scr-card { position: relative; height: 260px; border-radius: 28px; overflow: hidden; background: #ffffff; box-shadow: 0 30px 60px -30px rgba(157, 23, 77, 0.45); user-select: none; touch-action: none; }
.scr-prize { position: absolute; top: 0; right: 0; bottom: 0; left: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; background: #ffffff; }
.scr-label { font-size: 14px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #db2777; margin: 0; }
.scr-code { font-size: 72px; font-weight: 700; letter-spacing: -0.04em; color: #831843; margin: 0; line-height: 1; }
.scr-small { font-size: 15px; color: #9d174d; margin: 8px 0 0; }
.scr-cover { position: absolute; top: 0; right: 0; bottom: 0; left: 0; display: flex; align-items: center; justify-content: center; background-image: linear-gradient(135deg, #c0c0c8 0%, #f4f4f6 30%, #a9a9b3 52%, #ececf0 74%, #b8b8c2 100%); }
.scr-cover-text { font-size: 22px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #6b6b76; }
.scr-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: crosshair; transition: opacity 0.6s ease; }
@media (max-width: 767px) { .scr { padding: 80px 20px; } .scr-title { font-size: 30px; } .scr-code { font-size: 56px; } }
</style>
<script>
document.querySelectorAll(".scr-card").forEach(function (card) {
  var cover = card.querySelector(".scr-cover");
  var cv = document.createElement("canvas");
  cv.className = "scr-canvas";
  card.appendChild(cv);
  var ctx = cv.getContext("2d"), drawing = false, last = null, cleared = false;
  function paint() {
    var r = card.getBoundingClientRect(), dpr = window.devicePixelRatio || 1;
    cv.width = r.width * dpr; cv.height = r.height * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    var g = ctx.createLinearGradient(0, 0, r.width, r.height);
    g.addColorStop(0, "#c0c0c8"); g.addColorStop(0.3, "#f4f4f6"); g.addColorStop(0.52, "#a9a9b3"); g.addColorStop(0.74, "#ececf0"); g.addColorStop(1, "#b8b8c2");
    ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = g; ctx.fillRect(0, 0, r.width, r.height);
    ctx.fillStyle = "#6b6b76"; ctx.font = "700 22px Arial, sans-serif"; ctx.textAlign = "center"; ctx.textBaseline = "middle";
    ctx.fillText("SCRATCH HERE", r.width / 2, r.height / 2);
  }
  paint();
  cover.style.display = "none";
  function pos(e) { var r = cv.getBoundingClientRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; }
  function scratch(p) {
    ctx.globalCompositeOperation = "destination-out"; ctx.lineWidth = 44; ctx.lineCap = "round"; ctx.lineJoin = "round";
    ctx.beginPath(); ctx.moveTo((last || p).x, (last || p).y); ctx.lineTo(p.x, p.y); ctx.stroke(); last = p;
  }
  function check() {
    if (cleared) return;
    var d = ctx.getImageData(0, 0, cv.width, cv.height).data, clear = 0, n = 0;
    for (var i = 3; i < d.length; i += 64) { n++; if (d[i] === 0) clear++; }
    if (clear / n > 0.55) { cleared = true; cv.style.opacity = "0"; setTimeout(function () { cv.style.display = "none"; }, 600); }
  }
  cv.addEventListener("pointerdown", function (e) { drawing = true; last = null; cv.setPointerCapture(e.pointerId); scratch(pos(e)); });
  cv.addEventListener("pointermove", function (e) { if (drawing) scratch(pos(e)); });
  ["pointerup", "pointercancel"].forEach(function (t) { cv.addEventListener(t, function () { drawing = false; last = null; check(); }); });
  window.addEventListener("resize", function () { if (!cleared) paint(); });
});
</script>

Questions

How do I change the prize?

The prize is normal text under the silver layer. Select it in the Navigator and type your own code or message.

When does it clear completely?

Once a little over half the card is scratched, the rest fades away by itself.

Is this effect component free?

Yes. Copy it with no account and use it on client sites and your own sites.

Does it work on the free Webflow plan?

Yes. It pastes on every Webflow plan, including Starter. If it includes an Embed element, editing that Embed's code needs a paid site plan.