Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Pro

Shine Button for React

A button that a diagonal band of light sweeps across when the pointer arrives. For React with Base UI, when a call to action deserves a little polish.

Framework
UI library

When the pointer arrives, the button takes keyboard focus or it's pressed, a diagonal band of light sweeps across it once, like a gleam on a polished surface. Leave and come back, and it sweeps again.

Use it on a pricing page's Upgrade button or a hero's main action, where a small reward for hovering fits. It's subtle enough for a serious product, and it doesn't loop. For light that keeps moving on its own, use the Shimmer Button. It wraps the Button, so every variant and size works. The sweep is a CSS keyframe animation the install adds to your stylesheet, with no JavaScript and no animation library; when the system asks for reduced motion, the light fades in where it stands instead of moving.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { ShineButton } from "@/components/ui/shine-button";
<ShineButton>Upgrade now</ShineButton>

Demos

Color

shineColor takes any CSS color. By default the light is the label's color at a little over a third of its strength, which reads on every variant. Here an outline button shines in the primary color, and a filled one gets a brighter gleam from a stronger mix of its label color.

Preview width

Duration

duration is how long one sweep takes to cross the button, in milliseconds; the default is 700. A quick sweep suits a small button, and a slow one a large hero button.

Preview width

Accessibility

The light is decoration and hidden from assistive tech: the button is announced and activated as any Button. It sweeps on keyboard focus as well as on hover, so people who tab through the page see it too.

Tab
Move focus to the button; the light sweeps across.
Enter
Activate the button.
Space
Activate the button; the light sweeps while it's held.

FAQ

How do I make the shine stronger?

Pass a stronger color to shineColor, such as color-mix(in oklch, var(--primary-foreground) 70%, transparent). The Color demo does that on its second button.

Why doesn't the light sweep back when the pointer leaves?

On purpose: the sweep is an animation that runs while the hover lasts and ends with the band out of sight, and when the pointer leaves the band goes back unseen. So it only ever sweeps one way, the way a reflection does.

How do I change the speed?

Set duration in milliseconds, 700 by default, as in the Duration demo. It's written to a CSS variable on the button's light, --shine-button-duration, which the keyframes read.

Can it shine on a loop?

No, it sweeps once per hover or focus. For light that keeps moving on its own, use the Shimmer Button.

Does it shine on touch screens?

Touch screens have no hover, so the light sweeps while a finger presses the button: a long press shows the whole sweep, a quick tap only its start. The button works the same either way.

Which way does the light move in a right-to-left layout?

Right to left, the direction the page reads. In a left-to-right layout it moves left to right.

What happens with reduced motion?

The band doesn't move: it fades in across the middle of the button on hover, focus or a press, and fades out when they end.

API Reference

ShineButton

PropType
children?ReactNode

The label, above the light.

duration?number

How long one sweep of light takes to cross the button, in milliseconds.

Default
700
focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
shineColor?string

The light's color, any CSS color. By default it's the label's color at a little over a third of its strength, which reads on every variant.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--shine-button-colorstring

The light's color.

--shine-button-durationstring

One sweep across the button (700ms).

All 35 buttons