Tailwind Animation Utilities Playground
by tailkits
HTML
<!-- Head bits go here because JSFiddle merges into one page -->
<head>
<!-- Tailwind v4 browser CDN -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<!-- Enter/exit utilities -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tw-animate.css">
</head>
<body class="antialiased bg-white text-slate-800">
<!-- Safelist classes used by JS so the CDN compiles them -->
<div class="hidden
animate-spin animate-ping animate-pulse animate-bounce
animate-in animate-out fade-in fade-out zoom-in-95 zoom-out-95
slide-in-from-top-4 slide-out-to-top-4
duration-150 duration-300 duration-500 duration-700 duration-1000
delay-0 delay-150 delay-300 delay-500 delay-700 delay-1000
ease-linear ease-in ease-out ease-in-out
motion-reduce:animate-none
"></div>
<div class="mx-auto max-w-4xl p-4 md:p-6">
<header class="mb-4 md:mb-6">
<h1 class="text-xl md:text-2xl font-semibold">Tailwind Animation Utilities Playground</h1>
<p class="text-sm text-slate-600">Built-ins, enter/exit, custom keyframes · tweak duration, delay, easing</p>
</header>
<section class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
<!-- Built-ins -->
<div class="rounded-2xl border p-4">
<h2 class="text-base font-medium mb-2">Built-ins</h2>
<div class="flex flex-wrap gap-2">
<button data-anim="animate-spin" class="px-3 py-1.5 rounded bg-slate-900 text-white">Spin</button>
<button data-anim="animate-ping" class="px-3 py-1.5 rounded bg-slate-900 text-white">Ping</button>
<button data-anim="animate-pulse" class="px-3 py-1.5 rounded bg-slate-900 text-white">Pulse</button>
<button data-anim="animate-bounce" class="px-3 py-1.5 rounded bg-slate-900 text-white">Bounce</button>
</div>
</div>
<!-- Enter/Exit -->
<div class="rounded-2xl border p-4">
<h2...
CSS
/* Custom keyframes + class (duration driven by CSS var) */
@keyframes wiggle { 0%,100% { transform: rotate(-3deg) } 50% { transform: rotate(3deg) } }
.animate-wiggle { animation: wiggle var(--tkw-duration, 600ms) ease-in-out 1; }
JavaScript
const thing = document.getElementById("thing");
const builtInButtons = document.querySelectorAll("[data-anim]");
const btnShow = document.getElementById("btn-show");
const btnHide = document.getElementById("btn-hide");
const btnWiggle = document.getElementById("btn-wiggle");
const durSlider = document.getElementById("dur");
const resetBtn = document.getElementById("reset");
const durationSel = document.getElementById("duration");
const delaySel = document.getElementById("delay");
const easeSel = document.getElementById("ease");
function applyTiming(el) {
el.className = el.className
.split(" ")
.filter(c => !/^duration-|^delay-|^ease-/.test(c))
.join(" ");
el.classList.add(durationSel.value, delaySel.value, easeSel.value);
}
function clearAnimClasses() {
thing.className = thing.className
.split(" ")
.filter(c => !/^animate-|^fade-|^zoom-|^slide-/.test(c))
.join(" ");
}
// Built-ins
builtInButtons.forEach(btn => {
btn.addEventListener("click", () => {
clearAnimClasses();
thing.classList.add(btn.dataset.anim);
applyTiming(thing);
});
});
// Enter
btnShow.addEventListener("click", () => {
clearAnimClasses();
thing.classList.remove("hidden");
thing.classList.add("animate-in","fade-in","zoom-in-95","slide-in-from-top-4");
applyTiming(thing);
});
// Exit
btnHide.addEventListener("click", () => {
clearAnimClasses();
thing.classList.add("animate-out","fade-out","zoom-out-95","slide-out-to-top-4");
applyTiming(thing);
thing.addEventListener("animationend", () => {
thing.classList.add("hidden");
clearAnimClasses();
}, { once: true });
});
// Custom keyframe
btnWiggle.addEventListener("click", () => {
clearAnimClasses();
thing.style.setProperty("--tkw-duration", durSlider.value + "ms");
thing.classList.add("animate-wiggle");
});
// Timing controls
durationSel.addEventListener("change", () =>...