Button with border gradient
Tags: css
Published: 2023-05-07 10:36:58

---
title: Button with border gradient
published: true
date: 2023-05-07 10:29:59 UTC
tags: css
canonical_url: https://blog.nicm42.co.uk/button-with-border-gradient
---

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:

```html
<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).

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

```css
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:

{% codepen https://codepen.io/nicm42/pen/eYPgQoM %}

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:

```css
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:

```css
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:

{% codepen https://codepen.io/nicm42/pen/ExdZGPG %}

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