Animated Border Trail
A container with animated border trail, this can be used for buttons as well as cards.
Installation
CLI
pnpm dlx shadcn@latest add https://animata.design/r/container/animated-border-trail.json
Manual
Run the following command
It will create animated-border-trail.tsx and the co-located animated-border-trail.css inside components/animata/container.
mkdir -p components/animata/container && touch components/animata/container/animated-border-trail.tsx components/animata/container/animated-border-trail.cssPaste the code
@layer components {
@property --border-trail-angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
@keyframes border-trail {
0% {
--border-trail-angle: 0deg;
}
100% {
--border-trail-angle: 360deg;
}
}
}import { cn } from "@/lib/utils";
import "./animated-border-trail.css";
interface AnimatedTrailProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* The duration of the animation.
* @default "10s"
*/
duration?: string;
contentClassName?: string;
trailColor?: string;
trailSize?: "sm" | "md" | "lg";
}
const sizes = {
sm: 5,
md: 10,
lg: 20,
};
export default function AnimatedBorderTrail({
children,
className,
duration = "10s",
trailColor = "purple",
trailSize = "md",
contentClassName,
...props
}: AnimatedTrailProps) {
return (
<div
{...props}
className={cn("relative h-fit w-fit overflow-hidden rounded-2xl bg-gray-200 p-px", className)}
>
<div
className="absolute inset-0 h-full w-full"
style={{
animation: `border-trail ${duration ?? "10s"} linear infinite`,
background: `conic-gradient(from var(--border-trail-angle) at 50% 50%, transparent ${100 - sizes[trailSize]}%, ${trailColor})`,
}}
/>
<div
className={cn(
"relative h-full w-full overflow-hidden rounded-[15px] bg-white",
contentClassName,
)}
>
{children}
</div>
</div>
);
}How it works
Keyframes and the typed @property --border-trail-angle live in the co-located animated-border-trail.css import — Tailwind utilities cannot register typed custom properties or animate them.
The trail layer is an absolutely positioned conic-gradient whose start angle reads --border-trail-angle. Keyframes sweep that property from 0deg to 360deg; duration comes from the duration prop via an inline animation style so each instance can run at its own speed without a separate @theme token.
The inner panel sits above the gradient (relative + inset rounding) so only a 1px ring of the spinning conic shows through as the border trail. Adjust trailColor and trailSize to change the highlight colour and arc length.
prefers-reduced-motion: consider wrapping with a static border variant in your app — the component does not pause the loop automatically.
Credits
Built by hari
Inspired by @jh3yy on Twitter
I tried using his method but the transition in the border looked weird in my case so, I created this version.
This can also be created using clip-path but I like this method better.