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.
import { RainbowButton } from "@/components/ballmac/rainbow-button"
export default function RainbowButtonDemo() {
return <RainbowButton size="lg">Get started free</RainbowButton>
}Installation
$ pnpm dlx shadcn@latest add @ballmac/rainbow-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/rainbow-button.tsx// Ballmac UI: Rainbow Button. https://ui.ballmac.com/components/rainbow-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 RainbowButtonProps = Omit<ButtonProps, "asChild"> & { /** Seconds for the colors to travel once around the border. */ duration?: number /** Show a soft glow of the same colors under the button. */ glow?: boolean /** Classes for the wrapper that draws the border. */ wrapperClassName?: string } const GRADIENT = "conic-gradient(from 0deg, var(--chart-1), var(--chart-2), var(--chart-3), var(--chart-4), var(--chart-5), var(--chart-1))" function RainbowButton({ duration = 4, glow = true, shape, className, wrapperClassName, disabled, children, ...props }: RainbowButtonProps) { const reduce = useReducedMotion() const radius = shape === "pill" ? "rounded-full" : "rounded-md" const innerRadius = shape === "pill" ? "rounded-full" : "rounded-[calc(var(--radius-md)-2px)]" return ( <span data-slot="rainbow-button" className={cn("group/rainbow relative inline-flex isolate", radius, disabled && "opacity-60", wrapperClassName)} > {glow && ( <span aria-hidden="true" className={cn("pointer-events-none absolute -inset-1 -z-10 overflow-hidden opacity-0 blur-md transition-opacity duration-300 group-hover/rainbow:opacity-60 group-focus-within/rainbow:opacity-60 motion-reduce:transition-none", radius)}> <motion.span className="absolute -inset-[100%]" style={{ background: GRADIENT }} animate={reduce ? undefined : { rotate: 360 }} transition={{ duration, repeat: Infinity, ease: "linear" }} /> </span> )} <span aria-hidden="true" className={cn("pointer-events-none absolute inset-0 overflow-hidden", radius)}> <motion.span className="absolute -inset-[100%]" style={{ background: GRADIENT }} animate={reduce ? undefined : { rotate: 360 }} transition={{ duration, repeat: Infinity, ease: "linear" }} /> </span> <Button shape={shape} disabled={disabled} className={cn("m-[2px] border-0", innerRadius, className)} {...props} > {children} </Button> </span> ) } export { RainbowButton, type RainbowButtonProps }Update the import paths to match your project setup.
Usage
import { RainbowButton } from "@/components/ballmac/rainbow-button"The full example is in the Code tab above.
Examples
Sizes and pill
import { RainbowButton } from "@/components/ballmac/rainbow-button"
export default function RainbowButtonSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-5">
<RainbowButton size="sm">Small</RainbowButton>
<RainbowButton>Default</RainbowButton>
<RainbowButton shape="pill" size="lg" variant="outline">
Pill outline
</RainbowButton>
<RainbowButton disabled>Disabled</RainbowButton>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
durationSeconds for the colors to travel once around the border. | number | 4 |
glowShow a soft glow of the same colors under the button. | boolean | true |
wrapperClassNameClasses for the wrapper that draws the border. | string | — |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Enter / Space | Activates like any button |
| Focus | The standard focus ring and the glow both appear on keyboard focus |
| Reduced motion | The border is a still gradient |
Use with AI
Use like <Button>: <RainbowButton size variant shape duration glow>Label</RainbowButton>. Best with default, secondary or outline variants (the inside needs a background). With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Rainbow Button (@ballmac/rainbow-button) to this project with the shadcn MCP, then use it where it fits.
Use it for
- The single most important call to action on a page
- Upgrade and launch prompts
Not for
- Several buttons on one screen
- Ghost buttons (no background to sit on the border)
Registry JSON: https://ui.ballmac.com/r/rainbow-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.
Shiny Button
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.
Pulse Button
A call-to-action button that sends soft rings outward to ask for attention, calming as soon as it is hovered or focused, and switchable off once the person acts.
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.