Recently I had to do a button that had a gradient border. There are a couple of ways of doing this.
Set up
The initial HTML is just a button:
<button>Button</button>
And then I've added some CSS to centre the button on the page, and to create a rainbow gradient. I've also set the font-size and padding to make the button nice and big. And make sure the cursor is a hand when you hover on it (my pet peeve is that buttons don't do that by default).
:root {
--red: rgb(255, 0, 0);
--orange: rgb(255, 165, 0);
--yellow: rgb(255, 255, 0);
--green: rgb(0, 128, 0);
--blue: rgb(0, 0, 255);
--indigo: rgb(75, 0, 130);
--gradient: conic-gradient(var(--yellow), var(--green), var(--blue), var(--indigo), var(--red), var(--orange));
}
html, body {
height: 100%;
}
body{
display: grid;
place-content: center;
}
button {
padding: 1em 2em;
font-size: 2rem;
cursor: pointer;
}
Gradient as a border
We can add the gradient as a border, using the CSS used to add images to your border.
button {
background: white;
border: 0.15em solid;
border-radius: .25em;
border-image-source: var(--gradient);
border-image-slice: 1;
}
I've made the background white, as opposed to the button's default grey. And added a small border, as well as a border-radius.
Then border-image-source
allows you to use an image in the border. Similar to the way you can add a background-image
. Except that instead of giving it an image, we're giving it a gradient. Same as you do if you want a background gradient.
The clever part comes with the border-image-slice
. Used with an image, it allows you to define how much of the image should be seen. With a gradient, if you leave this off, it adds the gradient at the corners. Adding border-image-slice: 1
means that the gradient fills the border.
And then it looks like this:
Although it looks fine, you might recall that the CSS included a border-radius
. But the borders are not rounded. This is because you can't have a border-image with a border-radius. But there is an alternative method that will allow you both.
Gradient as a background
The alternative is to set the gradient as a background and then cover up everything apart from the edges.
We start with the same set up, but then add the background and borders:
button {
border: none;
border-radius: 0.25em;
background: var(--gradient);
}
This gives us something that looks hard to read, but it is a button filled with a background, with rounded corners.
Then we can use a pseudo-element to cover it up:
button {
position: relative;
z-index: 1;
}
button::before {
content: '';
position: absolute;
inset: 0.15em;
background-color: white;
z-index: -1;
}
We're making the button relatively positioned, so we can position the before element relative to it. inset: 0.15em
means you want top, right, bottom and left all to be 0.15em. Which means that this background will leave a gap of 0.15em around it. Which makes it look like the gradient is a border.
Then we need the z-indexes to make sure that the text, which is on the button, is still visible above the white background pseudo-element.
And then we get this, which looks very similar to the last example, but with rounded corners:
Another difference
To those codepens I've added a hover/focus state that makes the border twice the size. In the first example the border grows outwards. In the second example the border grows inwards. This is because one is a border and the other is not. I could have increased the padding on the second one on hover/focus, which would have made it look the same, but how I've done it highlights the difference: the first button is bigger. They're the same size, but one has an additional border, the other doesn't.
Top comments (0)