This Mouse-Tracking Linear Shine Button adds a high-end, tactile feel to your UI. Unlike standard hover effects that simply swap colors, this component tracks the user’s cursor position to drag a realistic light reflection across the button’s surface. Built with React and Tailwind CSS, it utilizes reactive CSS variables to control gradient positions, creating a dynamic lighting effect that feels physical and responsive.
Core Technique
The effect relies on passing the mouse coordinates from React state to CSS variables.
- Coordinate Normalization: The
mouseMovefunction calculates the cursor’s position within the button as a percentage (0.0 to 1.0) usinggetBoundingClientRect. - Reactive Styles: These values are passed to the inline
styleattribute as--mx(mouse X) and--my(mouse Y). - The Shine: The
.shineclass uses arepeating-linear-gradientwithmix-blend-mode: screen. Itsbackground-positionis directly linked to the CSS variables, causing the stripes to follow the mouse. - The Glow: The
.glowclass creates two opposingradial-gradientlights on the top and bottom edges. Interestingly, the calculationcalc(50% - var(--mx))moves these glows in the opposite direction of the mouse, adding depth and parallax.
Customization
To change the look of the reflection, modify the CSS gradient in the .shine class. You can adjust the angle (125deg) or the colors.
.shine {
/* Change 125deg to vertical (180deg) or horizontal (90deg) */
background-image: repeating-linear-gradient(
125deg,
transparent 0%,
rgba(255, 215, 0, 0.3) 25%, /* Gold tint */
transparent 50%
);
}
To control the intensity of the effect, adjust the opacity in the Tailwind classes or the CSS.
// Change opacity-100 to opacity-50 for a subtler effect
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 ...">
Tips
1. Performance Optimization:
Currently, this component uses useState to track mouse movement. This triggers a React re-render on every pixel the mouse moves, which can be heavy if you have many buttons. A more performant approach is to manipulate the DOM directly via ref.
const mouseMove = (e) => {
if (!buttonRef.current) return;
const rect = buttonRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
// Update CSS variables directly without re-render
buttonRef.current.style.setProperty("--mx", `${x * 100}%`);
buttonRef.current.style.setProperty("--my", `${y * 100}%`);
};
2. Accessibility:
The group-hover utility in Tailwind only targets mouse interaction. To make this keyboard accessible, ensure you add group-focus:opacity-100 alongside group-hover, so keyboard users (Tab navigation) can also see the active state (though the specific mouse-tracking effect won’t apply, the visibility will).


