Skip to content
Docs
Animated Border Trail

Animated Border Trail

A container with animated border trail, this can be used for buttons as well as cards.

Loading...

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.css

Paste 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.