Published on: Tue Jan 17 2023
Gradients aren’t supported for borders in out of the box in CSS, but there’s a few ways to achieve it. Here’s an example of an animating gradient border on an element with border radius.
Here is what we’re going to be making:
The technique for doing gradients on borders has a few variations, but as far as I know they all require you to render an extra element behind the element where you want the border - which is where we’re going to start.
Let’s start by laying out a new element underneath the element where we want the border, and make it overflow by 2px
to each side. We can use the after pseudo-element and apply the styles for the ‘border’ to that element
.element-with-gradient-border:after {
content: "";
position: absolute;
z-index: -1;
inset: 0;
margin: -2px;
border-radius: calc(var(--border-radius) + 2px);
background: linear-gradient(60deg, cyan, pink, darkgreen);
}
In order for the pseudo-element
to show we have to give it some content, an empty string is fine, otherwise the element won’t show.
We position it with absolute
and a negative z-index
to make it sit right underneath our original element.
Then there’s a few ways of sizing it correctly. You can use width
and height
set to 100%
+ the amount you want to overflow (with the calc()
function). But the element will then be impacted by padding on the original element. I think it’s nicer to give it inset: 0;
which is the shorthand for setting the top/right/bottom/left properties (not supported by Safari < 15 though). This makes the element’s sides snap to the borders of the original element. Then you can simply add a negative margin corresponding to the border thickness you want, and it will overflow correctly.
For border-radius you could just use border-radius: inherit;
to make the property match the original element, effectively making it the same size and get the same position. I think it looks better to add a slightly higher (1-2 px) border-radius than the original element though. Otherwise the corners look slightly square. We can a CSS variable for the border-radius and then add a few pixels to that value with the calc
function.
Finally add a background with a linear-gradient and a few colors. This is then what we get (the original element is transparent here to make the background more visible - and yes I could’ve spent more time finding a cool color palette, I blame my children!):
Let’s look at animating the ‘border’ a bit. The trick we will use here is to use the background-size
which set the size of an element´s background image. We will use this to make the background (the gradient color) larger than the original element. Then we make add an animation that moves it back and forth a bit. It’s sort of like looking through a narrow lens at a large picture, and then moving that picture back and forth, showing you a small part of the image at a time. If we don’t set a high background size, then the background is gonna wrap to the other side, and we would see a sudden change from one color to the other, instead of the gradient we want.
.element-with-gradient-border-and-animation:after {
/* ... */
background-size: 300% 300%;
animation: animatedgradient 4s ease alternate infinite;
}
@keyframes animatedgradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
Here is how the animation looks like, with the original element hidden and shown: