Build an animated border with Tailwind CSS

“Effects with Tailwind” series · 1 of 4
- Build an animated border with Tailwind CSS ← you are here
- Glassmorphism without wrecking legibility
- Scroll-driven animations
- View transitions in Astro
CSS can’t animate the border property natively. What we can do is fake it: take a div, give it an animated background, and put the content inside a child element with padding. That padding becomes the “thickness” of the border.
If you don’t spend much time in CSS or in frameworks like Tailwind, this might sound convoluted. It isn’t, and the payoff looks great.
Gradient types in CSS
To build the effect we need to know what CSS gives us to work with:
- Linear gradient: blends along a straight line in whatever direction you choose.
- Radial gradient: blends outward from a center point.
- Conic gradient: sweeps around a center point, like a color wheel.
We’ll use the conic gradient, because it’s the one that can be made to spin.
Building the border
<div
class="w-full max-w-lg bg-conic/[from_0deg] from-white to-white dark:from-black via-green-400 dark:to-black rounded-2xl p-px"
>
<div class="p-10 rounded-2xl bg-transparent">
<p class="text-white text-center font-semibold ">
Parent container background
</p>
</div>
</div>
Parent container background
Give the child container a background of its own and the border appears:
<div
class="w-full max-w-lg bg-conic/[from_0deg] from-white to-white dark:from-black via-green-400 dark:to-black rounded-2xl p-px"
>
<!-- Parent container -->
<div class="p-10 rounded-2xl dark:dark:bg-zinc-900 bg-mint-50">
<!-- Child container -->
<p class="text-white text-center font-semibold">
Once the child container has a background, the border effect shows up
</p>
</div>
</div>
Once the child container has a background, the border effect shows up
Adding motion with @property
@property lets us declare a custom property that the browser knows how to animate:
@property --border-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
Then we write the animation with @keyframes and register it in the Tailwind theme:
@theme {
--animate-rotate-border: border-rotate 3s linear infinite;
@keyframes border-rotate {
to {
--border-angle: 360deg;
}
}
}
Now we wire it into the parent container’s classes:
<div
class="w-full max-w-lg bg-conic/[from_var(--border-angle)] from-white to-white dark:from-black via-green-400 dark:to-black animate-rotate-border rounded-2xl p-px"
>
<div class="p-10 rounded-2xl bg-transparent">
<p class="text-white text-center font-semibold">
Animated parent container background
</p>
</div>
</div>
Animated parent container background
And here it is with a background behind the content
Controlling the border thickness
The padding is what sets the thickness, so that’s the knob to turn:
<div
class="w-full max-w-lg bg-conic/[from_var(--border-angle)] from-white to-white dark:from-black via-green-400 dark:to-black animate-rotate-border rounded-2xl p-[3px]"
>
<div class="p-10 rounded-2xl dark:bg-zinc-900 bg-zinc-50">
<p class="text-white text-center font-semibold">
Bumping the padding "thickens the border"
<br />
<code>p-[3px]</code>
</p>
</div>
</div>
Bumping the padding "thickens the border"
p-[3px]
Tweaking the gradient
Tailwind gives us control over where each color sits in the gradient:
from-*→ the color it starts on.via-*→ the color in the middle.to-*→ the color it ends on.
And we can move those stops around:
<div
class="w-full max-w-lg bg-conic/[from_var(--border-angle)] from-white to-white dark:from-black via-green-400 dark:to-black from-30% to-60% animate-rotate-border rounded-2xl p-px"
>
<div class="p-10 rounded-2xl bg-transparent">
<p class="text-white text-center font-semibold">
Moving the color stops gives you a different effect
<br />
<code>from-30% to-60%</code>
</p>
</div>
</div>
Moving the color stops gives you a different effect
from-30% to-60%
The finished thing
A couple of small adjustments to wrap up: I’ll change the from and to colors for something that reads more naturally, and drop the padding back to 1px.
<div
class="w-full max-w-lg bg-conic/[from_var(--border-angle)] from-green-200/20 via-green-400 to-green-200/20 from-30% to-60% animate-rotate-border rounded-2xl p-px"
>
<div class="p-10 rounded-2xl dark:bg-zinc-900 bg-zinc-50">
<p class="text-white text-center font-semibold">
This is the final result
</p>
</div>
</div>
This is the final result
From here it’s a matter of playing with the gradient’s colors and positions until you land on an effect that fits your design.
The next part of the series covers another effect: glassmorphism without wrecking legibility.

