A button with a diagonal band of light that sweeps across on hover and focus, or on a timer to draw the eye, built on the Ballmac button.
import { ShinyButton } from "@/components/ballmac/shiny-button"
export default function ShinyButtonDemo() {
return (
<div className="flex flex-wrap items-center justify-center gap-4">
<ShinyButton size="lg">Hover for shine</ShinyButton>
<ShinyButton size="lg" shine="loop">
Always inviting
</ShinyButton>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/shiny-buttonInstall the dependencies.
$ pnpm add motion@^12Add the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/buttonCopy the source into your project.
components/ballmac/shiny-button.tsx// Ballmac UI: Shiny Button. https://ui.ballmac.com/components/shiny-button "use client" import * as React from "react" import { motion, useReducedMotion } from "motion/react" import { Button, type ButtonProps } from "@/components/ballmac/button" import { cn } from "@/lib/utils" type ShinyButtonProps = Omit<ButtonProps, "asChild"> & { /** "hover" sweeps the shine on hover and keyboard focus. "loop" sweeps it again every few seconds to draw the eye. */ shine?: "hover" | "loop" /** Seconds between sweeps when `shine` is "loop". */ interval?: number } function ShinyButton({ shine = "hover", interval = 3.5, className, children, ...props }: ShinyButtonProps) { const reduce = useReducedMotion() return ( <Button data-slot="shiny-button" className={cn("group/shiny overflow-hidden", className)} {...props} > {children} {!reduce && ( <span aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden rounded-[inherit]"> {shine === "loop" ? ( <motion.span className="absolute inset-y-0 -start-1/3 w-1/3 -skew-x-[20deg] bg-gradient-to-r from-transparent via-white/40 to-transparent dark:via-black/20" animate={{ x: ["0%", "450%"] }} transition={{ duration: 0.9, ease: "easeInOut", repeat: Infinity, repeatDelay: interval }} /> ) : ( <span className="absolute inset-y-0 -start-1/3 w-1/3 -skew-x-[20deg] bg-gradient-to-r from-transparent via-white/40 to-transparent transition-transform duration-700 ease-out group-hover/shiny:translate-x-[450%] group-focus-visible/shiny:translate-x-[450%] rtl:skew-x-[20deg] rtl:group-hover/shiny:-translate-x-[450%] rtl:group-focus-visible/shiny:-translate-x-[450%] dark:via-black/20" /> )} </span> )} </Button> ) } export { ShinyButton, type ShinyButtonProps }Update the import paths to match your project setup.
Usage
import { ShinyButton } from "@/components/ballmac/shiny-button"The full example is in the Code tab above.
Examples
On every variant
import { ShinyButton } from "@/components/ballmac/shiny-button"
export default function ShinyButtonVariants() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<ShinyButton>Default</ShinyButton>
<ShinyButton variant="secondary">Secondary</ShinyButton>
<ShinyButton variant="outline">Outline</ShinyButton>
<ShinyButton shape="pill">Pill</ShinyButton>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
shine"hover" sweeps the shine on hover and keyboard focus. "loop" sweeps it again every few seconds to draw the eye. | "hover" | "loop" | "hover" |
intervalSeconds between sweeps when `shine` is "loop". | number | 3.5 |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Enter / Space | Activates like any button |
| Reduced motion | No shine is drawn |
Use with AI
<ShinyButton shine='hover|loop' interval>Label</ShinyButton>. Accepts every Button prop. No shine under reduced motion. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Shiny Button (@ballmac/shiny-button) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Primary calls to action that deserve a little life
- Pricing and signup buttons
Not for
- Dense toolbars
- Destructive actions
Registry JSON: https://ui.ballmac.com/r/shiny-button.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- @ballmac/buttonshadcn/utils
Pairs well with
Button
A button with six variants, three sizes, a pill shape and a built-in loading state. buttonVariants() styles links the same way.
Rainbow Button
A button with a border of theme colors circling it and a glow on hover, built on the Ballmac button so every size, variant and focus state still works.
Magnetic Button
The Ballmac Button with a magnetic pull: it drifts toward a nearby mouse pointer on a spring, and its label moves a little further for depth. Takes every Button prop.
Animated Beam
An SVG beam that connects two elements with a curved path and sends a glowing gradient pulse along it. Follows layout changes, pauses off-screen, static under reduced motion.