DEV Community

Cover image for Gradient Border is Unexpectedly Hard
Theodorus Clarence
Theodorus Clarence

Posted on • Originally published at theodorusclarence.com

4 1 1 1 1

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!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay