3 Tailwind3 with JSPublished 2026-10-05Free · no sign-up
Six buttons that move with purpose: a save button with a spinner, a bell with a pinging badge, a pulsing call to action, a bouncing scroll-down arrow, a heart that pops when liked and a download button that fills as it progresses.
Click and the button shows an animate-spin spinner with Saving… and becomes busy for two seconds. aria-busy and the disabled state stop double submits, and the spinner is drawn with a border trick, no image needed.
A bell button with a red dot that sends out a ping ring using animate-ping, a quiet way to say there is something new. The count is in the accessible name, and the ring is skipped for reduced motion.
A pill button with a soft halo pulsing out behind it, for the one action you really want noticed. The halo is a separate animate-ping span under the button, so the text itself never moves.
A scroll-down button for the bottom of a hero section, with an arrow that bounces using animate-bounce. Its label says where it goes, and the bounce stops under prefers-reduced-motion.
A heart toggle that pops and fills red when liked, with a counter beside it. aria-pressed holds the state, the aria-pressed: variant styles it, and a small keyframe gives the pop only when motion is allowed.
var b = document.getElementById('twab5'), c = document.getElementById('twab5c');
b.onclick = function () { var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); c.textContent = on ? 42 : 41; };
06. Tailwind Download Button with Progress
TailwindButtonProgress
A download button that fills with a progress bar from left to right, then reads Downloaded. The fill is a span whose width the script updates; a status region announces the start and the end rather than every percent.
var b = document.getElementById('twab6'), bar = b.children[0], tx = b.children[1], st = document.getElementById('twab6s');
b.onclick = function () { var p = 0; b.disabled = true; st.textContent = 'Download started'; var t = setInterval(function () { p += 10; bar.style.width = p + '%'; tx.textContent = p + '%'; if (p >= 100) { clearInterval(t); tx.textContent = 'Downloaded'; st.textContent = 'Download complete'; setTimeout(function () { b.disabled = false; bar.style.width = '0'; tx.textContent = 'Download report'; }, 2500); } }, 200); };
About these Tailwind Animated Buttons
Built-in animations
animate-spin, animate-ping, animate-pulse and animate-bounce cover most button motion. Change speed with [animation-duration:2s].
Custom keyframes
For anything else, define keyframes in your CSS or tailwind.config and reference them with animate-[name_.35s_ease-out].
Announce state, not motion
Use aria-busy, aria-pressed and a status region so the change is clear without seeing the animation.
Reduced motion
Decorative motion such as pings and bounces gets motion-reduce:hidden or motion-reduce:animate-none; a spinner can slow down instead, since it carries meaning.
Frequently asked questions
Do these submit real data?
No. The loading and download states are simulated with timers for the demo.
Can I style aria-pressed?
Yes. Tailwind v3.4 has aria-pressed: and group-aria-pressed: variants built in.