How to create a gradient border with rounded corners using CSS

March 23, 2024

I recently needed to create a shiny gradient border for a box that had rounded corners and while there are a few different approaches, I couldn’t find anything documenting using an inset box-shadow to achieve the effect, so that’s what this short post is about.

If you don’t need rounded corners using border-radius then the border-image property is probably the easiest approach.

This is what the end result will look like:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

If you don’t care about the specifics, here’s a Codepen

This approach works by setting the background of the box to a gradient and then applying an inset box shadow, starting at the top of the box and stretching to the bottom of the box, that is the same colour as the background of the page, that shadow then fills the box all the way to the edges leaving only the border showing the gradient, instead of the entire background.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

First you’ll need an outer wrapper and an inner wrapper.

<div class="outer">
  <div class="inner">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
    tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
    consequat.
  </div>
</div>

Then, define your border width and border radius (I’m using css variables to make it easier to read, but feel free to just put the numbers inline)

.outer {
    --outer-border-radius: 8px;
    --border-width: 2px;
}

.inner {
    padding: 16px;
}

Work out the border-radius for the inner wrapper. (Approach taken from this article on how to create perfect nested border radius)

.outer {
    --outer-border-radius: 8px;
    --border-width: 2px;

    --inner-border-radius: calc(
      var(--outer-border-radius) - var(--border-width)
    );
}

.inner {
    padding: 16px;
}

Set your gradient and apply the border and border-radius. Note we use a transparent border here, so that the gradient “background” can eventually show through.

.outer {
    --outer-border-radius: 8px;
    --border-width: 2px;

    --inner-border-radius: calc(
      var(--outer-border-radius) - var(--border-width)
    );

    background-image: linear-gradient(
      192deg,
      #ff4757 5.4%,
      #3742fa 33.68%,
      #2ed573 67.8%,
      #1e90ff 95.59%
    );

    border: var(--border-width) solid transparent;
    border-radius: var(--outer-border-radius);
}

.inner {
    padding: 16px;
    border-radius: var(--inner-border-radius);
}

This should result in something like the following:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Now essentially all we need to do is hide everything except the border. The key to this approach is to apply a box-shadow inside of the box that then fills the entire box leaving only the border showing the gradient.

For this you’ll need a inset shadow with an xOffset of 0 and a yOffset of 100vh. This is essentially a shadow starting at the top of the box and stretching all the way to the bottom (technically further than the bottom as we’re using vh units). Try changing it to something like 50px to see how the effect works.

We also include the background-clip and background-origin properties so that the shadow doesn’t escape our box.

.outer {
    --outer-border-radius: 8px;
    --border-width: 2px;

    --inner-border-radius: calc(
      var(--outer-border-radius) - var(--border-width)
    );

    background-image: linear-gradient(
      192deg,
      #ff4757 5.4%,
      #3742fa 33.68%,
      #2ed573 67.8%,
      #1e90ff 95.59%
    );

    border: var(--border-width) solid transparent;
    border-radius: var(--outer-border-radius);

    box-shadow: white 0 100vh inset;

    background-clip: border-box;
    background-origin: border-box;
}

.inner {
    padding: 16px;
    border-radius: var(--inner-border-radius);
}

and that’s it 🎉

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.