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.