A short beam of light that travels around the inside edge of its parent's border at a constant speed. Decorative, token-colored, hidden under reduced motion.
Rolling out to 3 regions
Step 2 of 4: running migrations on the primary database.
import { BorderBeam } from "@/components/ballmac/border-beam"
export default function BorderBeamDemo() {
return (
<div className="relative w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground">
<div className="flex items-center justify-between">
<span className="font-mono text-xs text-muted-foreground">deploy · main</span>
<span className="inline-flex items-center gap-1.5 text-xs font-medium">
<span className="size-1.5 rounded-full bg-ring" aria-hidden="true" />
Building
</span>
</div>
<p className="mt-4 text-base font-semibold tracking-tight">Rolling out to 3 regions</p>
<p className="mt-1 text-sm text-muted-foreground">Step 2 of 4: running migrations on the primary database.</p>
<BorderBeam />
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/border-beamInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/border-beam.tsx// Ballmac UI: Border Beam. https://ui.ballmac.com/components/border-beam // Based on Magic UI's Border Beam (MIT, Copyright (c) Magic UI), rewritten: span markup, token colors, Motion loop, reduced-motion and support checks. "use client" import * as React from "react" import { animate, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type BorderBeamProps = Omit<React.ComponentProps<"span">, "children"> & { /** Seconds for one lap around the border. */ duration?: number /** Seconds before the first lap. Use different delays to offset two beams. */ delay?: number /** Length of the beam in pixels. */ size?: number /** Color at the head of the beam. Any CSS color. */ colorFrom?: string /** Color the beam fades through before its transparent tail. Any CSS color. */ colorTo?: string /** Travel counter-clockwise. */ reverse?: boolean /** Thickness of the beam in pixels; match your border width. */ borderWidth?: number } // Keeps only the border ring: the padding box is cut out of the full box. const borderOnlyMask: React.CSSProperties = { WebkitMask: "linear-gradient(black, black) padding-box, linear-gradient(black, black)", WebkitMaskComposite: "xor", mask: "linear-gradient(black, black) padding-box exclude, linear-gradient(black, black)", } function BorderBeam({ duration = 8, delay = 0, size = 80, colorFrom = "var(--ring)", colorTo = "color-mix(in oklch, var(--ring) 45%, transparent)", reverse = false, borderWidth = 1, className, style, ...props }: BorderBeamProps) { const beamRef = React.useRef<HTMLSpanElement>(null) const reduceMotion = useReducedMotion() React.useEffect(() => { const beam = beamRef.current if (!beam || reduceMotion) return // Browsers without offset-path rect() would park the beam in a corner; hide it instead. if (typeof CSS !== "undefined" && !CSS.supports("offset-path", "rect(0 auto auto 0)")) { beam.style.display = "none" return } const controls = animate( beam, { offsetDistance: reverse ? ["100%", "0%"] : ["0%", "100%"] }, { duration, delay, ease: "linear", repeat: Infinity } ) return () => controls.stop() }, [duration, delay, reverse, reduceMotion]) return ( <span aria-hidden="true" data-slot="border-beam" className={cn( "pointer-events-none absolute inset-0 block overflow-hidden rounded-[inherit] border-solid border-transparent motion-reduce:hidden", className )} style={{ ...borderOnlyMask, borderWidth, ...style }} > <span ref={beamRef} data-slot="border-beam-light" className="absolute block aspect-square" style={{ width: size, offsetPath: `rect(0 auto auto 0 round ${size}px)`, offsetDistance: "0%", // The head of the beam faces the direction of travel. background: `linear-gradient(${reverse ? "to right" : "to left"}, ${colorFrom}, ${colorTo}, transparent)`, }} /> </span> ) } export { BorderBeam, type BorderBeamProps }Update the import paths to match your project setup.
Usage
import { BorderBeam } from "@/components/ballmac/border-beam"The full example is in the Code tab above.
Examples
Button
import { Sparkles } from "lucide-react"
import { BorderBeam } from "@/components/ballmac/border-beam"
import { Button } from "@/components/ballmac/button"
export default function BorderBeamButton() {
return (
<Button variant="outline" size="lg" shape="pill">
<Sparkles /> Generate summary
<BorderBeam size={48} duration={4} />
</Button>
)
}API reference
| Prop | Type | Default |
|---|---|---|
durationSeconds for one lap around the border. | number | 8 |
delaySeconds before the first lap. Use different delays to offset two beams. | number | 0 |
sizeLength of the beam in pixels. | number | 80 |
colorFromColor at the head of the beam. Any CSS color. | string | "var(--ring)" |
colorToColor the beam fades through before its transparent tail. Any CSS color. | string | "color-mix(in oklch, var(--ring) 45%, transparent)" |
reverseTravel counter-clockwise. | boolean | false |
borderWidthThickness of the beam in pixels; match your border width. | number | 1 |
Also accepts the standard attributes of its root element.
Use with AI
Drop <BorderBeam /> as the last child of any element with position: relative and a border radius; it inherits the radius and draws only in a borderWidth-thick ring (aria-hidden, pointer-events-none). Renders a span, so it is valid inside buttons. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Border Beam (@ballmac/border-beam) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Drawing attention to one highlighted card, such as the recommended pricing plan
- Marking a live or running state on a panel (an agent working, a deploy in progress)
- A featured call-to-action button
Not for
- Several elements on the same screen; one beam per view
- Communicating status on its own (pair it with text; it is hidden under reduced motion)
Registry JSON: https://ui.ballmac.com/r/border-beam.json
Credits
Based on Magic UI Border Beam, adapted by Ballmac. Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Spotlight Card
A card whose surface and border pick up a soft glow that follows the pointer, driven by CSS variables with no re-render per move. Keyboard focus shows a steady glow.
Button
A button with six variants, three sizes, a pill shape and a built-in loading state. buttonVariants() styles links the same way.
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.