CSS gradient border

There is no gradient value for border-color, so every gradient border is a workaround. Four of them work; the one you want depends on whether the corners are rounded.

A CSS gradient border cannot be written as border-color: linear-gradient(...), because border-color only accepts flat colour values.

Every gradient border is therefore a workaround. Four of them work in current browsers, and they differ mainly in how they handle rounded corners.

Four boxes in one document, each using a different gradient border method.
Four boxes in one document, each using a different gradient border method.

The four methods compared

Method Rounded corners Content behind stays visible Complexity
border-image No Yes Lowest
Two background layers Yes No, needs a solid inner colour Low
Pseudo-element ring Yes Yes Medium
Padding on a gradient box Yes No Lowest

Pick by the first two columns. If the box has border-radius, border-image is out before you start.

Method 1: border-image

The shortest route when corners are square.

.card {
  border: 4px solid transparent;
  border-image: linear-gradient(120deg, #2563eb, #db2777) 1;
}

The trailing 1 is the slice value. It tells the browser to take the whole generated image and stretch it across the border box.

The limitation is absolute: border-image ignores border-radius. Add a radius and the corners stay square while the background curves.

Method 2: two background layers

This is the usual answer for rounded corners. One rule paints two layers with different clipping boxes.

.card {
  border: 4px solid transparent;
  border-radius: 14px;
  background-image:
    linear-gradient(#0b1220, #0b1220),
    linear-gradient(120deg, #2563eb, #db2777);
  background-origin: border-box;
  background-clip: padding-box, border-box;
}

The first layer is a flat colour clipped to the padding box, which covers the middle. The second is the gradient clipped to the border box, so only the four-pixel rim shows.

The first colour has to match whatever sits behind the card. That is the trade-off: the interior is opaque, so you cannot see a page background through it.

Method 3: pseudo-element ring

Use this when the interior must stay transparent, for example over a photograph.

.card { position: relative; border-radius: 14px; }
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 4px;
  background: linear-gradient(120deg, #22d3ee, #a78bfa);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}

The mask pair punches the middle out of the gradient, leaving a ring of exactly the padding width. inset: 0 makes it cover the parent without stated dimensions.

A pseudo-element gradient ring over a photo, with the photo visible inside the ring.
A pseudo-element gradient ring over a photo, with the photo visible inside the ring.

Method 4: padding on a gradient box

The least code, and it is fine when the inner area is a solid block anyway.

.wrap {
  padding: 4px;
  border-radius: 14px;
  background: linear-gradient(120deg, #f59e0b, #ef4444);
}
.wrap > .inner {
  border-radius: 10px;
  background: #0b1220;
}

Set the inner radius smaller than the outer one by the padding amount. If both are 14px the corners look pinched.

This needs an extra wrapper element, which is why it is not always the right answer inside content you do not control.

Choosing between the four

Work down these questions in order and you land on one method.

  1. Does the box have border-radius? If no, border-image is the shortest code and you are done.
  2. Must the interior stay transparent? If yes, the pseudo-element ring is the only option, because the other two paint a solid inner layer.
  3. Can you add a wrapper element? If yes, padding on a gradient box is the least code with rounded corners.
  4. Otherwise, use the two background layers method. It needs no extra element and handles radius correctly.

Most real cases land on step four, which is why the background-layers version appears in so many component libraries.

Gradient borders on tables and inputs

Tables collapse their borders by default, which removes the border box the image needs.

table { border-collapse: separate; border-spacing: 0; }

Without that line a gradient border on a cell either disappears or renders on one side only. The trade-off is that adjacent cell borders no longer merge, so you get a double line unless you zero one side.

Inputs are simpler but have a second problem: the focus ring. If you replace the border with a gradient, keep a visible focus indicator.

.field:focus-within { outline: 2px solid #60a5fa; outline-offset: 2px; }

Removing the focus outline without replacing it makes the form unusable by keyboard, which is a larger cost than the border is worth.

Thickness and radius arithmetic

Outer radius Border width Inner radius to use
8px 2px 6px
14px 4px 10px
20px 6px 14px
999px (pill) Any 999px

Subtract the border width from the outer radius for the inner one. A pill is the exception, since a very large radius is clamped to half the height on both layers anyway.

Getting this wrong is what produces the pinched corner people describe as the gradient looking thicker at the corners than along the edges.

Common failures

  • Border disappears entirely. border: 4px solid transparent is missing, so there is no border box for the image to fill.
  • Corners are square despite border-radius. The page is using border-image. Switch to method 2 or 3.
  • A grey ring appears. The inner flat colour does not match the surface behind the card.
  • Layout shifts when the border appears. Set box-sizing: border-box, covered in box sizing.

Animating the gradient

Gradients are images, so they do not interpolate on their own. Animate the position instead.

.card {
  background-size: 200% 200%;
  animation: slide 6s linear infinite;
}
@keyframes slide {
  to { background-position: 200% 0; }
}

Oversize the background and move it. The visible section changes, which reads as the gradient rotating.

Checking it survives outside your editor

The gradient border page opened from a share link in a separate window.
The gradient border page opened from a share link in a separate window.

Gradient borders use no external assets, so a page that works locally works for a reader. Confirm it in the HTML file opener, which has never seen your project.

Then paste the HTML into a document and create a link. The page keeps its address when you adjust the colours, so a correction does not mean resending anything.

For the underlying gradient syntax see CSS gradient background. For the other common decorative rule people ask about next, see CSS drop shadow.

Questions people ask

Why does border-color: linear-gradient() not work?

border-color accepts colour values only, and a gradient is an image. The browser discards the declaration entirely, so the border falls back to the current text colour or disappears. Use border-image or a background layer method instead.

What is the simplest gradient border with rounded corners?

The two-layer background-clip method. Set a white padding-box layer and a gradient border-box layer in one background-image rule, make the border transparent, and border-radius applies cleanly. border-image ignores border-radius, which is why it fails here.

Can I put a gradient on only one side?

Yes. Set border-width to zero on three sides and give the fourth a width, then use border-image with a matching slice. A simpler route is a pseudo-element positioned along that edge with the gradient as its background.

Does a gradient border survive when the HTML file is shared?

It does, because gradients need no external file. Anything that renders in a fresh browser window will render for a reader. Confirm it by opening the file in a window that has never seen your project, then share the page as a link.

Keep reading