Create a Shimmer Button

A shimmer button is just a light sweep across a clipped button. The trick is not the gradient. The trick is knowing when to create it, when to ignore extra triggers, and when to clean it up.

We should create the shimmer only when the button is hovered or focused, let one shimmer finish before starting another, and remove the shimmer element when the animation is done.

The Result

Hover the button, or tab to it with the keyboard.

Shimmer Button

What We Need

The button needs four pieces:

  1. A real <button> so clicks, focus, and keyboard behavior work without extra code.
  2. position: relative and overflow: hidden so the shimmer can move inside the pill shape.
  3. A generated .shimmer element that runs one animation and then disappears.
  4. A small JavaScript guard so repeated hover or focus events do not pile up extra shimmer elements.

The markup stays boring on purpose:

<button class="buyButton" type="button" data-shimmer-button>Buy Now</button>

The Button Shell

Start with the button itself. The important properties are not the colors. They are the layout rules that let the effect work.

.buyButton {
  position: relative;
  overflow: hidden;
  border-radius: 1000px;
}

position: relative gives the shimmer a positioning context. overflow: hidden clips the shimmer to the rounded button.

The Shimmer Layer

The shimmer is a temporary span. It covers the button, starts outside the left edge, and slides across.

.shimmer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform: translateX(-100%);
  animation: shimmer-sweep 800ms ease-out forwards;
}

@keyframes shimmer-sweep {
  to {
    transform: translateX(100%);
  }
}

pointer-events: none matters. The shimmer should never steal hover, focus, or click behavior from the button.

The JavaScript Guard

The simple version appends a new shimmer on every mouseenter. That works once, then gets messy if the pointer moves in and out quickly.

Instead, mark the button while the shimmer is running:

if (btn.dataset.shimmering === "true") {
  return;
}

btn.dataset.shimmering = "true";

Then clean up after the animation:

shimmer.addEventListener("animationend", () => {
  shimmer.remove();
  delete btn.dataset.shimmering;
}, { once: true });

Hover And Focus

Hover is not enough. Keyboard users should get the same effect when the button receives focus.

btn.addEventListener("mouseenter", generateShimmer);
btn.addEventListener("focus", generateShimmer);

That is the whole pattern: create the effect on demand, let one run at a time, and remove it when it is done.