Experiment #004
Shimmer Button
A skewed band of light — a functional linear gradient from transparent to white and back — sits clipped inside the button and rides translateX from -150% to 350% when the pointer enters. The sweep is a one-shot CSS animation retriggered on each hover or keyboard focus, animating transform only. No JavaScript, and the band stays parked off-canvas for users who prefer reduced motion.
Scene
Technical details
- Experiment
- FX-004
- Category
- Buttons
- Stack
- Tailwind + CSS
- Difficulty
- Beginner
- Dependencies
- none
- Weight
- HTML 1.2 KB · CSS 1.1 KB
- Added
- 2026-08-23
- Updated
- 2026-08-23
Lab notes
The band width (40%) and skew (-20deg) set the character of the sheen: narrower and steeper reads as glass, wider and flatter reads as satin. Keep the white stop at 0.3–0.4 alpha on dark buttons; anything brighter looks like a glitch. Around 0.8s is the sweet spot — faster feels nervous, slower feels laggy.
Take the code
HTML — code.html
<!-- Shimmer Button — Tailwind CSS -->
<style>
.shimmer-band {
background: linear-gradient(
105deg,
transparent 0%,
rgba(255, 255, 255, 0.35) 50%,
transparent 100%
);
transform: translateX(-150%) skewX(-20deg);
}
@keyframes shimmer-sweep {
from {
transform: translateX(-150%) skewX(-20deg);
}
to {
transform: translateX(350%) skewX(-20deg);
}
}
.shimmer-button:hover .shimmer-band,
.shimmer-button:focus-visible .shimmer-band {
animation: shimmer-sweep 0.8s ease;
}
@media (prefers-reduced-motion: reduce) {
.shimmer-button .shimmer-band {
animation: none !important;
}
}
</style>
<button
type="button"
class="shimmer-button relative overflow-hidden rounded-full bg-zinc-900 px-7 py-3
text-sm font-medium text-white focus-visible:outline-2
focus-visible:outline-offset-4 focus-visible:outline-zinc-500"
>
<span
class="shimmer-band pointer-events-none absolute inset-y-0 left-0 w-2/5"
aria-hidden="true"
></span>
<span class="relative">Hover me</span>
</button>
CSS — styles.css
/* Shimmer Button — vanilla CSS (no Tailwind, no extra markup: the band is a pseudo-element) */
.shimmer-button {
position: relative;
overflow: hidden;
padding: 0.75rem 1.75rem;
border: 0;
border-radius: 9999px;
background: #18181b;
color: #fff;
font: 500 0.875rem/1.25rem system-ui, sans-serif;
cursor: pointer;
}
.shimmer-button:focus-visible {
outline: 2px solid #71717a;
outline-offset: 4px;
}
.shimmer-button::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 40%;
background: linear-gradient(
105deg,
transparent 0%,
rgba(255, 255, 255, 0.35) 50%,
transparent 100%
);
transform: translateX(-150%) skewX(-20deg);
pointer-events: none;
}
@keyframes shimmer-sweep {
from {
transform: translateX(-150%) skewX(-20deg);
}
to {
transform: translateX(350%) skewX(-20deg);
}
}
.shimmer-button:hover::before,
.shimmer-button:focus-visible::before {
animation: shimmer-sweep 0.8s ease;
}
@media (prefers-reduced-motion: reduce) {
.shimmer-button::before {
animation: none;
}
}
Adjacent