Skip to main content
Photo from unsplash: milad-fakurian-PGdW_bHDbpI-unsplash

Gradient Border is Unexpectedly Hard

Written on July 14, 2024 by Theodorus Clarence.

4 min read
––– views

Introduction

I was doing a design revamp for this blog, and my designer came up with a button that looks like this.

Button with a gray gradient border and a translucent background

Looks great, right?

I thought to myself, that would be simple enough to make: a button, with a transparent and blur background, and add in a gray-ish gradient border around it. Probably something like border: 1px solid linear-gradient() would definitely work.

Spoiler: I was wrong

Border Gradient Attempt

So I set my goal, to build a proof of concept.

I whipped out a simple codepen, and created a button with a linear gradient border.

button {
  all: unset;
  font-family: sans-serif;
  font-size: 2rem;
 
  padding: 1rem 2rem;
  background: #172554;
  color: white;
 
  border: 1px solid linear-gradient(to bottom right, red, orange, yellow, green, blue, indigo, violet);
}

That must be the most colorful gradient known to man. I was confident that it would work.

Button without a rainbow gradient border

It must be a dry season because I can’t see any rainbow!

Yeah, turns out linear-gradient does not work with border.

Stacked Element Method

As a seasoned problem solver, I went to google, searched “How to make a gradient border in CSS”, and found an interesting method:

You basically create two divs, first is for the rainbow background, and second one is the button text itself. It sounds like a great idea.

Diagram of how stacked element method works

I updated my code using a pseudo element as a background.

button {
  all: unset;
  position: relative;
 
  font-family: sans-serif;
  font-size: 2rem;
 
  padding: 1rem 2rem;
  background: #000;
  color: white;
  /* formula for 2 stacked radius is: (radius - (border-width / 2)) */
  border-radius: calc(12px - (2px / 2));
}
 
button::before {
  content: '';
  position: absolute;
  /* this is the border width */
  inset: -2px;
  z-index: -1;
  background: linear-gradient(
    to bottom right,
    red,
    orange,
    yellow,
    green,
    blue,
    indigo,
    violet
  );
  border-radius: 12px;
}
Button with a rainbow gradient border

IT WORKS!

The code looks a bit complicated, but it does the job. I was really happy about it.

Adding a transparent background

I continued my work by adding the transparent and blurred background.

button {
  background: rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(1px);
}

And it looks like this:

Button with a rainbow gradient background, but the border is gone

Remember that we have a rainbow div behind? Introducing a translucent background means we will see the rainbow.

If you think that maybe we can try making a rainbow background with a hollow transparent background in the middle, that would be called a gradient border 🙃. So we're back to square one.

Ultimate Solution with Mask Exclude

So I did some more digging, and found the perfect solution that can cater all cases.

There’s a new property that’s recently gained a major support across browsers which is mask-composite.

Button with a rainbow gradient background

According to CSS Tricks, we can remove a certain part of element by creating mask with 2 elements on top of each other, and use mask-composite exclude.

css-tricks-exclude

That means, if we create two boxes with one smaller than the other, we can have a mask that can show anything we like.

Mask Composite caniuse

With mask, the element's background will be reflected. This even includes background-image.

CSS Tricks exclude

Final Code

This is how the final code looks like!

button {
  all: unset;
  position: relative;
 
  font-family: sans-serif;
  font-size: 1rem;
 
  padding: 1rem 2rem;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(2px);
  color: white;
  border-radius: 12px;
}
 
button::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0px;
 
  border-radius: inherit;
  /* this is the border width */
  padding: 1px;
  background: linear-gradient(
    to bottom right,
    #171717 0%,
    #525252 62%,
    #171717 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}
Untitled

Reference: https://stackoverflow.com/a/51496341

Final Words

There you have it! A working set of gradient border with translucent background. With this new widely-supported property, we can create anything that comes to mind.

Including something like this:

ps: I can’t open-source this because it’s from work ✌️. But it's using the same method!

Tweet this article

Enjoying this post?

Don't miss out 😉. Get an email whenever I post, no spam.

Subscribe Now