Creating a Button Shine Effect
Published: September 9, 2026
⏱️ 2 min read | 📝 380 words
Micro-interactions matter
A button that only changes color on hover feels flat. The best UIs — game menus especially — reward interaction with small, satisfying details. One of the cheapest wins is a shine sweep: a highlight that glides across the button when you hover over it.
The best part? You can do it with zero JavaScript, using nothing but a pseudo-element and a CSS transition.
The effect
The shine is a ::after pseudo-element layered on top of the button. On hover, a CSS transform slides it from left to right — a single property change that's GPU-friendly and buttery smooth.
The component
---
const { label = "Hover Me" } = Astro.props;
---
<button class="game-btn">{label}</button>
<style>
.game-btn {
position: relative;
padding: 12px 28px;
font-size: 1rem;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #1e293b, #0f172a);
border: 1px solid #334155;
border-radius: 8px;
cursor: pointer;
overflow: hidden;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.game-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.game-btn::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transform: translateX(-100%);
transition: transform 0.4s ease;
pointer-events: none;
}
.game-btn:hover::after {
transform: translateX(100%);
}
</style>How it works
overflow: hiddenon the button clips the shine inside the rounded corners — no stray highlight bleeding out.- The
::afterelement starts off-screen attranslateX(-100%). - On hover it transitions to
translateX(100%), sweeping across the whole surface. pointer-events: noneensures the overlay never blocks clicks on the button.
Tuning it
Want a faster or subtler sweep? Just adjust the numbers:
.game-btn::after {
transition: transform 0.25s ease-out; /* quicker shimmer */
background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.15), transparent); /* subtler shine */
}Changing the angle in linear-gradient changes the direction and shape of the highlight — try 90deg for a straight vertical sweep, or -120deg for a reverse angle.
If you're shipping this, wrap the transition in a @media (prefers-reduced-motion: reduce) query and drop the sweep for users who prefer less animation.
One more UI element
The button lives in the UI Elements Playground, alongside the reusable progress bar. If you want a matching progress component, read the ProgressBar post too.