Moving Border
A light laps the button's border via offset-path.
0.9 KB gzippedZero dependenciesReduced-motion safeBase UI & Radix compatible
Installation
npx shadcn@latest add https://innsite.com.mx/r/moving-border.jsonSource
import { cn } from "@/lib/utils";
interface MovingBorderProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/** Seconds for one lap of the border */
duration?: number;
/** Length of the travelling light, in px */
size?: number;
/** Corner radius of the border path, in px */
radius?: number;
children: React.ReactNode;
}
/**
* Button with a light that laps its border, driven by `offset-path`.
* Zero dependencies — one keyframe does the whole thing.
*/
export function MovingBorder({
duration = 4,
size = 90,
radius = 12,
children,
className,
style,
...props
}: MovingBorderProps) {
return (
<button
{...props}
data-slot="moving-border"
style={
{
"--mb-duration": `${duration}s`,
"--mb-size": `${size}px`,
"--mb-radius": `${radius}px`,
borderRadius: radius,
...style,
} as React.CSSProperties
}
className={cn(
"relative inline-flex h-11 cursor-pointer items-center justify-center overflow-hidden bg-background p-px text-sm font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50",
className
)}
>
<span
aria-hidden
className="pointer-events-none absolute inset-0 motion-reduce:hidden"
>
<span
className="absolute aspect-square w-(--mb-size) animate-moving-border bg-[radial-gradient(circle,var(--brand-via),transparent_65%)]"
style={{
offsetPath: `rect(0 auto auto 0 round var(--mb-radius))`,
}}
/>
</span>
<span className="relative z-10 flex h-full w-full items-center justify-center rounded-[inherit] border bg-background px-6">
{children}
</span>
</button>
);
}

