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
::beforepseudo-element creates a layer with the gradient border. padding: 3pxensures 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!
