Skip to content

Party Popper

A copy-paste confetti & celebration component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.

Confetti & CelebrationHTMLCSSJavaScriptany framework

Copy into your project

HTML
<button class="nuda-confetti-popper" aria-label="Pop the party popper">
  <span class="nuda-confetti-popper__label">Pop</span>
  <span class="nuda-confetti-popper__streamers" aria-hidden="true">
    <span class="nuda-confetti-popper__streamer" style="--a:-15deg; --c:#e4ff54; --dist:70px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-23deg; --c:#ff5db1; --dist:84px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-31deg; --c:#5dd0ff; --dist:98px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-39deg; --c:#fafafa; --dist:112px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-47deg; --c:#e4ff54; --dist:70px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-55deg; --c:#ff5db1; --dist:84px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-63deg; --c:#5dd0ff; --dist:98px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-71deg; --c:#fafafa; --dist:112px"></span>
    <span class="nuda-confetti-popper__streamer" style="--a:-79deg; --c:#e4ff54; --dist:70px"></span>
  </span>
</button>
CSS
/* Party Popper
   Streamers shoot from the bottom-left corner on pop.
   Customize: per-streamer --a angle, --c color, --dist distance. */

.nuda-confetti-popper {
  position: relative;
  width: 150px;
  height: 100px;
  background: #09090b;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  cursor: pointer;
  outline: none;
  overflow: hidden;
}

.nuda-confetti-popper:focus-visible {
  box-shadow: 0 0 0 3px rgba(228, 255, 84, 0.4);
}

.nuda-confetti-popper__label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 600 14px/1 system-ui, sans-serif;
  color: #e4ff54;
  pointer-events: none;
}

.nuda-confetti-popper__streamers {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 0;
}

.nuda-confetti-popper__streamer {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 4px;
  height: 12px;
  border-radius: 2px;
  background: var(--c, #e4ff54);
  transform-origin: bottom left;
  opacity: 0;
  transform: rotate(var(--a, -45deg)) translateX(0);
  will-change: transform, opacity;
}

.nuda-confetti-popper--go .nuda-confetti-popper__streamer {
  animation: nuda-confetti-popper-shoot 0.9s cubic-bezier(0.15, 0.7, 0.3, 1) forwards;
}

@keyframes nuda-confetti-popper-shoot {
  0%   { opacity: 0; transform: rotate(var(--a)) translateX(0) scaleY(0.4); }
  15%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(var(--dist, 90px)) scaleY(1.4); }
}

@media (prefers-reduced-motion: reduce) {
  .nuda-confetti-popper--go .nuda-confetti-popper__streamer { animation: none; opacity: 0; }
}
JavaScript
/* Party Popper — vanilla JS
   Re-fires the streamers on each click. */

(function () {
  document.querySelectorAll(".nuda-confetti-popper").forEach(function (btn) {
    btn.addEventListener("click", function () {
      btn.classList.remove("nuda-confetti-popper--go");
      void btn.offsetWidth;
      btn.classList.add("nuda-confetti-popper--go");
      setTimeout(function () {
        btn.classList.remove("nuda-confetti-popper--go");
      }, 1100);
    });
  });
})();

How to use Party Popper

Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.

Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.

More confetti & celebration components

← Browse all NudaUI components