Gradient Border is Unexpectedly Hard

theodorusclarence

Theodorus Clarence

Posted on July 15, 2024

Gradient Border is Unexpectedly Hard

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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.

Caniuse mask composite

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.

Diagram Exclude

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

showing image and rainbow

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;
}
Enter fullscreen mode Exit fullscreen mode

Final view

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:

Star Button

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


Originally posted on my personal site, find more blog posts and code snippets library I put up for easy access on my site 🚀

Like this post? Subscribe to my newsletter to get notified every time a new post is out!

💖 💪 🙅 🚩
theodorusclarence
Theodorus Clarence

Posted on July 15, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related