Back to CSS Buttons Subscribe Confetti CSS + JS
Share
HTML
<button class="btn-sub">
  <span class="btn-sub-fill" aria-hidden="true"></span>
  <span class="btn-sub-text">Subscribe Now</span>
  <span class="btn-sub-check">✦&nbsp;Subscribed!</span>
</button>
CSS
.btn-sub {
  position: relative;
  min-width: 180px;
  padding: 14px 30px;
  border: 2px solid #7c3aed;
  border-radius: 12px;
  background: transparent;
  color: #7c3aed;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  overflow: hidden;
  user-select: none;
  transition: transform 0.2s;
}
.btn-sub-fill {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.4s cubic-bezier(.77,0,.18,1);
}
.btn-sub-text {
  position: relative;
  z-index: 1;
  transition: color 0.2s 0.15s, opacity 0.2s;
}
.btn-sub-check {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.3s 0.2s, transform 0.3s 0.2s;
}
.btn-sub.is-subscribed { pointer-events: none; transform: scale(0.98); }
.btn-sub.is-subscribed .btn-sub-fill { transform: scaleY(1); }
.btn-sub.is-subscribed .btn-sub-text { opacity: 0; color: #fff; }
.btn-sub.is-subscribed .btn-sub-check { opacity: 1; transform: translateY(0); }
.btn-sub-confetti {
  position: absolute;
  width: var(--w, 6px);
  height: var(--h, 8px);
  background: var(--c, #7c3aed);
  pointer-events: none;
  z-index: 20;
  animation: btn-sub-fall 0.9s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes btn-sub-fall {
  0%   { transform: translate(0,0) rotate(0deg) scale(1); opacity: 1; }
  100% { transform: translate(var(--tx), var(--ty)) rotate(var(--r)) scale(0.5); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .btn-sub-fill, .btn-sub-text, .btn-sub-check { transition: none; }
  .btn-sub-confetti { animation: none; }
}
JS
document.querySelectorAll('.btn-sub').forEach(function (btn) {
  var done = false;
  var colors = ['#7c3aed', '#a855f7', '#ec4899', '#f59e0b', '#10b981', '#3b82f6'];
  btn.addEventListener('click', function () {
    if (done) return;
    done = true;
    btn.classList.add('is-subscribed');
    var w = btn.offsetWidth;
    var h = btn.offsetHeight;
    for (var i = 0; i < 24; i++) {
      var p = document.createElement('span');
      p.className = 'btn-sub-confetti';
      var angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI * 1.6;
      var dist = 40 + Math.random() * 70;
      p.style.left = (w / 2 + (Math.random() - 0.5) * w * 0.6) + 'px';
      p.style.top = (h / 2) + 'px';
      p.style.setProperty('--w', (4 + Math.random() * 6) + 'px');
      p.style.setProperty('--h', (5 + Math.random() * 9) + 'px');
      p.style.setProperty('--c', colors[Math.floor(Math.random() * colors.length)]);
      p.style.setProperty('--tx', (Math.cos(angle) * dist) + 'px');
      p.style.setProperty('--ty', (Math.sin(angle) * dist) + 'px');
      p.style.setProperty('--r', (Math.random() * 360 - 180) + 'deg');
      p.style.animationDelay = (Math.random() * 0.15) + 's';
      p.style.borderRadius = Math.random() > 0.5 ? '50%' : '2px';
      btn.appendChild(p);
      p.addEventListener('animationend', function () { this.remove(); });
    }
  });
});