# CSS gradient border: four ways to draw one

[Canonical HTML page](https://gradiently.design/guide/css-gradient-border)

border-color does not take a gradient, so every gradient outline is a workaround. Here are the four that matter, what each can and cannot do, and which to reach for.

## The short version

- CSS cannot put a gradient in border-color, so a CSS gradient border is always drawn with another technique.
- border-image with a gradient is the shortest method, but it ignores border-radius, so it only suits square corners.
- Two background layers, one clipped to the padding box and one to the border box, give a rounded gradient border as long as the inside is a solid colour.
- The mask technique cuts the middle out of a gradient layer, which gives a rounded gradient border with a transparent inside.
- A rotating conic gradient, driven by a custom property registered with @property, is the usual way to animate a gradient border.

A **CSS gradient border** is an outline whose colour blends along its length. The catch is that `border-color` only takes colours, not images, so there is no single property for it. Instead you draw the gradient somewhere else and show only a thin band of it. There are four reliable ways to do that, and the right one depends on two questions: does the element have rounded corners, and does its inside need to be transparent?

| Method | Rounded corners | Transparent inside | Best for |
| --- | --- | --- | --- |
| `border-image` | No | Yes | Square cards, dividers, tables |
| Two backgrounds | Yes | No, needs a solid fill | Most cards and buttons |
| Mask on a pseudo-element | Yes | Yes | Cards over images or gradients |
| Pseudo-element behind | Yes | No | Glows and animated rings |

Choose by the two questions: corners and inside.

- Violet to cyan: `linear-gradient(135deg, #7c3aed, #22d3ee)`
- Tangerine to magenta: `linear-gradient(135deg, #f97316, #db2777)`
- Lime to teal: `linear-gradient(135deg, #a3e635, #0d9488)`
- Silver edge: `linear-gradient(135deg, #e5e7eb, #6b7280, #f9fafb)`

Gradients that read well as thin outlines. High saturation matters more on a 2px line than on a whole background.

## Method 1: border-image

`border-image` accepts any image, including a gradient. Set a border width and style, then the gradient, then a slice of `1`, which tells the browser to use the whole image as the edge. It is two lines, works everywhere, and keeps the inside of the element untouched.

```css
.panel {
  border: 3px solid;
  border-image: linear-gradient(135deg, #7c3aed, #22d3ee) 1;
}
```

The shortest gradient border. The `1` is the slice value; without it the border draws only in the corners.

> **No rounded corners** With `border-image`, `border-radius` still clips the background, but the border itself stays square. If the element must be rounded, use one of the other methods.

## Method 2: two backgrounds with background-clip

Give the element a transparent border, then stack two backgrounds. The top layer is the inner fill, clipped to the `padding-box`, so it stops at the inside edge of the border. The bottom layer is the gradient, clipped to the `border-box`, so it extends under the border. Where the fill stops, the gradient shows, and it follows `border-radius` perfectly.

```css
.card {
  border: 2px solid transparent;
  border-radius: 16px;
  background:
    linear-gradient(#0f172a, #0f172a) padding-box,
    linear-gradient(135deg, #7c3aed, #22d3ee) border-box;
}
```

The fill is written as a one-colour gradient because only images can be layered like this.

This is the method to reach for by default. Its one limit is that the inside must be opaque, since a transparent fill would reveal the gradient across the whole card. For cards sitting on a flat page colour, that is no limit at all: use the page colour as the fill.

> **Why the shorthand matters** Writing `border-box` once in the `background` shorthand sets both `background-origin` and `background-clip`, so the gradient is sized to the outer edge. If you set only `background-clip: border-box` in longhand, the gradient is sized to the padding box and repeats under the border, leaving thin slivers of the wrong colour along the edges.

## Method 3: the mask technique for transparent insides

When the card sits over a photo or another gradient, you need the middle to be see-through. Put the gradient on a pseudo-element that covers the card, give it padding equal to the border width, then mask it with two layers: one covering only the content box and one covering everything. Excluding one from the other leaves just the padding ring visible.

```css
.glass-card {
  position: relative;
  border-radius: 16px;
}

.glass-card::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 2px; /* border width */
  border-radius: inherit;
  background: linear-gradient(135deg, #7c3aed, #22d3ee);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box exclude,
    linear-gradient(#000 0 0);
  pointer-events: none;
}
```

A gradient ring with a transparent centre. The prefixed pair covers older WebKit; the standard `mask` line covers everything current.

`pointer-events: none` stops the pseudo-element swallowing clicks meant for the card's content. The [MDN mask-composite reference](https://developer.mozilla.org/en-US/docs/Web/CSS/mask-composite) explains the compositing keywords. This is the method behind most [glassmorphism](https://gradiently.design/guide/glassmorphism) cards with glowing edges.

## Method 4: a pseudo-element behind, for glows and animation

Place a gradient pseudo-element slightly larger than the card, behind its content, and cover the middle with a second pseudo-element holding the card's solid fill. The visible edge is the gradient. Because the gradient lives on its own layer, you can blur a copy of it for a glow, or rotate it.

```css
@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.ring {
  position: relative;
  isolation: isolate;
  border-radius: 16px;
}

.ring::before {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: 18px;
  background: conic-gradient(from var(--angle), #7c3aed, #22d3ee, #f472b6, #7c3aed);
  animation: spin 6s linear infinite;
}

/* the card's fill, painted over the middle of the ring */
.ring::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: #0f172a;
}

@keyframes spin {
  to { --angle: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .ring::before { animation: none; }
}
```

An animated gradient border. The registered `--angle` lets the browser interpolate the conic start angle smoothly, and `::after` paints the fill over the middle.

The rotating ring's gradient: `conic-gradient(from 0deg, #7c3aed, #22d3ee, #f472b6, #7c3aed)`

Repeating the first colour at the end hides the seam as it turns. [CSS conic gradients](https://gradiently.design/guide/css-conic-gradient) covers the function in depth.

The outer radius should equal the inner radius plus the border width, which is why the pseudo-element uses 18px around a 16px card. For more ways to put motion into gradients, see [CSS animated gradients](https://gradiently.design/guide/css-animated-gradient).

## Design tips for gradient borders

### Avoid

- Gradient borders on every card in a grid
- Low-saturation gradients on a 1px line, which read as grey
- A gradient border and a gradient fill on the same element
- Fast spinning rings on content people need to read

### Do

- Reserve the gradient edge for one highlighted item, such as a featured plan
- Use 2px or more and saturated stops
- A calm fill with a vivid edge, or the reverse
- Slow rotation, paused for reduced motion

Gradient borders work best as emphasis. The same logic applies to buttons, covered in [gradient buttons in CSS](https://gradiently.design/guide/css-gradient-button).

## FAQ

### How do I make a gradient border in CSS?

The quickest way is `border: 3px solid; border-image: linear-gradient(135deg, #7c3aed, #22d3ee) 1;`. For rounded corners, use two backgrounds clipped to `padding-box` and `border-box`.

### Why doesn't border-radius work with border-image?

By specification, a border drawn with `border-image` is not rounded by `border-radius`. Use the background-clip or mask method for rounded gradient borders.

### How do I make a gradient border with a transparent background?

Use the mask technique: a pseudo-element with the gradient, padding equal to the border width, and a mask that excludes its content box.

### How do I animate a gradient border?

Register an angle with `@property`, use it as the `from` angle of a conic gradient on a pseudo-element, and animate that property from 0deg to 360deg.

### Which gradient border method should I use?

Use two backgrounds for most rounded cards and buttons, `border-image` for square edges, and the mask technique only when the inside must stay transparent.
