The Search for the Perfect Gradient Outline Button (With a Transparent Background)

When I first set out to create a button with a gradient outline and a transparent background, I assumed it would be easy. CSS has so many tricks for borders and backgrounds—surely, one of them would do the job. Turns out, it wasn’t that simple.

Attempt 1: border-image

The first thing that came to mind was border-image, which allows you to apply a gradient to the border. It seemed like the obvious solution:

.button {
    background: transparent;
    color: white;
    padding: 10px 20px;
    font-size: 16px;
    font-weight: bold;
    border: 3px solid;
    border-image-source: linear-gradient(45deg, #ff416c, #ff4b2b);
    border-image-slice: 1;
    border-radius: 8px;
    cursor: pointer;
}

At first glance, it looked fine. But as soon as I tested it, I noticed a problem: the gradient border didn’t look smooth, and it didn’t play well with border radius. On some browsers, it even looked pixelated.

So, onto the next attempt.

Attempt 2: background-clip

I had seen a clever trick using background-clip to apply a gradient border while keeping the inner part transparent. The idea is to layer two backgrounds—one for the padding area (transparent) and one for the border area (gradient):

.button {
    position: relative;
    display: inline-block;
    padding: 10px 20px;
    font-size: 16px;
    font-weight: bold;
    color: white;
    border: 3px solid transparent;
    border-radius: 8px;
    background: linear-gradient(white, white) padding-box,
                linear-gradient(45deg, #ff416c, #ff4b2b) border-box;
    background-clip: padding-box, border-box;
    cursor: pointer;
}

This method worked perfectly… except for one big issue: The button wasn’t actually transparent. And I want this button on my navbar on top of a video. The inner background was being filled with white. No matter how I adjusted it, this approach required a solid fill color. Not what I needed.

Attempt 3: Masking (The Winner!)

At this point, I was getting frustrated. The usual suspects weren’t cutting it, so I turned to masking. By using mask-composite (or -webkit-mask-composite for WebKit browsers), I could cut out the middle part of the button while keeping the gradient border intact.

Here’s the final, fully functional solution:

.button {
    position: relative;
    display: inline-block;
    padding: 10px 20px;
    font-size: 16px;
    font-weight: bold;
    color: white;
    border: none;
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
}

.button::before {
    content: "";
    position: absolute;
    inset: 0;
    padding: 3px; /* Border thickness */
    border-radius: inherit;
    background: linear-gradient(45deg, #ff416c, #ff4b2b);
    
    -webkit-mask:
        linear-gradient(#fff 0 0) content-box,
        linear-gradient(#fff 0 0);
    -webkit-mask-composite: destination-out;
    mask-composite: exclude;
}

Why this works

  • The ::before pseudo-element creates a layer with the gradient border.
  • padding: 3px ensures the inside remains empty, acting as the transparent area.
  • mask-composite: exclude; cuts out the middle, making the button’s background fully transparent.

Finally, a fully transparent button with a smooth gradient outline that works across modern browsers!


Final Thoughts

This was a great reminder that not every CSS trick works in every scenario. Sometimes, the first (or even second) attempt won’t cut it, but there’s always a way to achieve the result you want. Masking might not be the most well-known technique, but it ended up being the perfect solution for this case.

If you’ve run into a similar issue before, or if you’ve found another creative approach, I’d love to hear about it!

Leave a Reply

Your email address will not be published. Required fields are marked *