You Might Also Like
Shimmer Button — Free HTML CSS JS Animated Border Snippet
Shimmer Button · Buttons · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Shimmer Button — Rotating Spark Border and Sheen Sweep

The shimmer button is the premium call-to-action seen across modern AI and developer products: a dark pill wrapped in a thin animated light that travels around its border, with a glossy sheen that sweeps across the label on hover. This snippet builds it in plain HTML, CSS, and a touch of vanilla JavaScript, using a conic-gradient technique that stays cheap for the GPU.
The rotating spark border
The animated edge is created with two layers. The outer button holds a .sh-spark element whose ::before is an oversized square (160% of the button, kept square with aspect-ratio: 1) filled with a conic-gradient that is transparent for most of its sweep and bright purple-to-cyan for the final slice. Rotating that square 360° with the shSpin keyframe drags the bright slice around the perimeter. The inner .sh-body sits with a 1.5px margin on top of the spark, so only a thin rim of the rotating gradient peeks out as a glowing animated border — the body itself covers the rest.
Why conic-gradient over an SVG stroke
A rotating conic gradient masked by an inner panel is far cheaper than animating an SVG stroke or a box-shadow: it's a single transform on one element, which the compositor handles on the GPU without repainting. That's why the border can spin continuously without hurting scroll performance, even on a page full of other animations.
The sheen sweep
On hover, a .sh-shine overlay — a diagonal linear-gradient with a bright band in the middle — animates from translateX(-120%) to 120%, sweeping a glossy highlight across the label once. Because it starts and ends fully off the button, you only see the band crossing the face. The arrow icon also nudges right on hover, reinforcing the forward call to action.
A position-aware click ripple
The JavaScript adds tactile feedback. On click it reads the cursor position relative to the button, drops a tiny span there, and animates it with the Web Animations API (element.animate) scaling from 0 to 26× while fading out — a Material-style ripple that emanates from exactly where you clicked. The ripple element removes itself in the onfinish callback, so no nodes accumulate. Using animate() instead of CSS keyframes keeps the ripple self-contained and avoids managing extra classes.
Layering and isolation
The button uses overflow: hidden to clip the spark and ripple to its rounded shape, and isolation: isolate to keep the stacking context self-contained. The z-index order — spark at 0, body at 1, ripple at 2 — guarantees the ripple draws over the body while the spark stays behind it. The :active scale gives a subtle physical press.
Customizing it
Recolor the conic stops to change the spark, slow or speed the shSpin duration, widen the body margin for a thicker border, or adjust the sheen angle and speed. Make it a link by swapping <button> for <a> — the effects don't depend on the element. Pair it with a neo-brutalist card or place it inside an animated gradient CTA for a cohesive, high-end look.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't need to work out the layered conic-gradient border trick by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the oversized rotating square behind sh-spark produces a thin traveling border rather than a spinning square, or why the inner sh-body's 1.5px margin is what makes only a rim of the gradient visible. The same assistant can help optimize it, for example checking whether the conic-gradient rotation is truly GPU-composited in this exact stacking setup or whether isolation and overflow are doing more repaint work than they need to. It's also useful for extending the feature: ask it to vary the spark colors based on a data attribute for different button variants, add a disabled state that stops the spin and mutes the sheen, or make the ripple's color match the spark's gradient instead of plain white. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a premium call-to-action button with a rotating animated border and a hover sheen sweep in plain HTML, CSS, and a small amount of JavaScript, no libraries.
Requirements:
- Create the animated border using a rotating conic-gradient, not an SVG stroke or an animated box-shadow: an outer layer holds an oversized square pseudo-element (larger than the button, kept square with aspect-ratio) filled with a conic-gradient that is transparent for most of its rotation and brightly colored for one slice, animated with a CSS transform rotation.
- Layer an inner content panel on top of that outer layer with only a 1-2px margin, so the rotating gradient is visible solely as a thin traveling rim around the edge, not as a spinning shape across the whole button face.
- On hover, sweep a diagonal light band across the button label using a linear-gradient overlay that translates from fully off one side to fully off the other side, so the sheen crosses the face exactly once per hover and is invisible when idle.
- Use CSS overflow hidden and an isolated stacking context on the outer button so the rotating gradient and any ripple effect are clipped to the button's rounded shape and layer in the correct order (border behind, content in the middle, ripple effects on top).
- In JavaScript, add a click ripple that originates from the exact cursor position (computed via getBoundingClientRect relative to the click event), animated with the Web Animations API's element.animate method scaling from zero to a large multiple while fading out, and have the ripple element remove itself from the DOM in the animation's finish callback so nodes never accumulate.
- The button must also support being rendered as an anchor element instead of a button element without losing any of the border, sheen, or ripple behavior.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA dark button renders with a thin light traveling around its border.
- 2Hover the buttonA glossy sheen sweeps across the label and the arrow slides right.
- 3Click itA ripple expands from exactly where you clicked.
- 4Recolor the sparkEdit the conic-gradient stops to match your brand.
- 5Adjust the speedChange the shSpin duration for a faster or calmer border.
- 6Use as a linkSwap button for an anchor; the effects still work.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
An outer spark layer has a ::before that is an oversized square filled with a conic-gradient — transparent for most of its sweep and bright for one slice. Rotating that square 360 degrees drags the bright slice around the perimeter. An inner body panel with a 1.5px margin covers everything except a thin rim, so only that glowing edge shows.
Rotating one conic-gradient element is a single GPU transform with no repaint, whereas animating an SVG stroke or a box-shadow forces repaints each frame. That's why the border can spin continuously without hurting scroll or competing animations, even on lower-end devices.
The click handler reads the pointer position relative to the button via getBoundingClientRect, places a small span at that point, and animates it with the Web Animations API from scale 0 to 26 while fading out. The element removes itself in the animation's onfinish callback, so ripples never accumulate in the DOM.
Yes. None of the effects depend on the button element — swap <button> for an <a href>. Keep the same class structure so the spark, body, sheen, and ripple markup line up. If you do, ensure the link has a visible focus style for keyboard users since the demo relies on the native button focus ring.
The markup and CSS port directly as a reusable Button component. Implement the ripple in the click handler using element.animate on a ref, or extract it to a small hook/composable. In Tailwind, define the conic spin keyframe in the config, build the border with an absolute spark layer plus an inset body, and animate the sheen with a translate-x utility on group-hover.