CSS Tip! ✨
It's 2024 and you have a new way to make animated borders 🚀
.glow::after {
offset-path: rect(0 100% 100% 0 round var(--radius));
animation: loop;
}
@keyframes loop { to { offset-distance: 100%; }}
Using the offset-* properties you can animate elements along the perimeter of others 😍
The rect() value gained support in Safari 17.2 🙌
To start, you create an element and put it inside your main element. For example, you put a span inside the button 🤙
<button>
<span data-glow />
Click me!
</button>
Make the element fill its parent with absolute positioning and inset
[data-glow] {
position: absolute;
inset: 0;
}
Now the good part, you use a pseudoelement on that element and define an offset-path
[data-glow]::after {
content: "";
offset-path: rect(0 auto auto 0 round var(--radius));
animation: loop 2.6s infinite linear;
}
With the rect value, you are saying the path fills the parent container:
top: 0
right: auto || 100%
bottom: auto || 100%
left: 0
Then you can use round to make sure the path uses the same radius as whatever the parent has
The @keyframes animation merely animates the offset-distance of that pseudoelement to 100%
@keyframes loop { to { offset-distance: 100%; }}
You can see this more clearly in the video 🫶
The offset-* properties also include an offset-anchor property. This allows you to dictate which point of the element follows the path. For example:
anchor-offset: 100% 50%;
This means that the "right, center" of the element will follow the perimeter of the parent element 🤙
Lastly, the visuals 🎨 For color, you can use a gradient such as a linear gradient to fill the pseudo-element.
[data-glow]::after {
background:
radial-gradient(
circle at right,
hsl(320 90% 100%),
transparent 50%
);
}
Then clip away everything so you only have the border and can still have translucent backgrounds, etc. Use a mask with mask-composite ✨ A little transparent border trick:
[data-glow] {
border: 2px solid transparent;
mask:
linear-gradient(transparent, transparent),
linear-gradient(white, white);
mask-clip: padding-box, border-box;
mask-composite: intersect;
}
Bit of a long one. Hope you find it useful 🙏
@CodePen link below 👇