40

I have the following CSS:

a.btn.white-grad {
    background: $lgrey;
    color: #313149 !important;
    border: 1px solid #000;
    border-image-source: linear-gradient(to right, #9c20aa, #fb3570);
    border-image-slice: 20;
    float: left;
    @include font-size(26);
    margin: 75px 0;
}

Adding border-radius: 5px doesn't seem to do anything. I figured it's because I'm using a border gradient... is there a way for me to achieve the desired 5px border radius at all?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
nsilva
  • 5,184
  • 16
  • 66
  • 108
  • @TemaniAfif the title needs to reflect the question being asked--the old title was vague, non-descriptive. This new title is much better because it's a question and it also clarifies exactly what OP is asking about/having trouble with. It also makes it clear it isn't a duplicate of, e.g. https://stackoverflow.com/questions/2717127/how-to-do-gradient-borders-in-css – TylerH Aug 25 '23 at 13:06
  • @TylerH the title also serve for the SEO purpose. The old one was a good one and attract all the queries related to gradient and border-radius. I also made my answer a canonical one that covers more that the initial question. The question is popular for those reasons and changing the title may affect it and it can become less popular – Temani Afif Aug 25 '23 at 13:38
  • @TemaniAfif The question will remain popular--it's a canonical with many many questions linked to it, so SEO is not going to be harmed much. On top of that, the old title was misleading/vague--that's not the kind of traffic we want to achieve, and besides, SEO should always be secondary to an accurate description of the question. – TylerH Aug 25 '23 at 13:53
  • @TylerH the old title is not misleading. People want to create "border gradient with border-radius" and I shared all the possible ways including different techniques and none of them are using border-image. So people don't need to know "why border-image doesn't work with border-radius" which is a very specific thing and won't play well in search engine. The question is attracting a lot of traffic and I am getting new upvotes which means it works well and that's what we want. We attract people to a generic solution not to a specific problem. The use of border-image was not the right thing to do – Temani Afif Aug 25 '23 at 14:02

3 Answers3

121

2021: If you want transparency, I recommend using the CSS mask method since the support is pretty good now


You cannot use border-radius with gradient. Here is another idea where you can rely on multiple background and adjust the background-clip:

.white-grad {
  background: 
    linear-gradient(#ccc 0 0) padding-box, /*this is your grey background*/
    linear-gradient(to right, #9c20aa, #fb3570) border-box;
  color: #313149;
  padding: 10px;
  border: 5px solid transparent;
  border-radius: 15px;
  display: inline-block;
  margin: 75px 0;
}
<div class="white-grad"> Some text here</div>

<div class="white-grad"> Some long long long text here</div>

<div class="white-grad"> Some long long <br>long text here</div>

CSS border gradient with radius

SVG method

If you want transparency, you can consider SVG like below:

svg {
  width:200px;
  height:100px;
  margin:10px;
}
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
      <linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse">
         <stop stop-color="#9c20aa" offset="0"/>
         <stop stop-color="#fb3570" offset="1"/>
      </linearGradient>
   </defs>
  <rect x="5" y="5" height="100%" width="100%" style="width:calc(100% - 10px);height:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(#Gradient)"/>
</svg>

That you can apply as background:

.white-grad {
    background:url('data:image/svg+xml;utf8,<svg   xmlns="http://www.w3.org/2000/svg" ><defs><linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop stop-color="%239c20aa" offset="0"/><stop stop-color="%23fb3570" offset="1"/></linearGradient></defs><rect x="5" y="5" width="100%" height="100%" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(%23Gradient)"/></svg>');
    color: #313149;
    padding:25px;
    border-radius:15px;
    display:inline-block;
    margin: 75px 0;
}

body {
  background:yellow;
}
<div class="white-grad"> Some text here</div>

<div class="white-grad"> text very loooooooooooong here</div>

And the same way as mask where you can get the gradient outside of the SVG:

.white-grad {
  color: #313149;
  padding: 25px;
  border-radius: 15px;
  display: inline-block;
  margin: 75px 0;
  background-size: 0 0;
  position: relative;
  z-index: 0;
}

.white-grad::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: inherit;
  background-size: auto;
  --mask: url('data:image/svg+xml;utf8,<svg  xmlns="http://www.w3.org/2000/svg" ><rect x="5" y="5" width="100%" height="100%" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="white"/></svg>');
  -webkit-mask: var(--mask);
          mask: var(--mask);
}

body {
  background: yellow;
}
<div class="white-grad" style="background-image:linear-gradient(to right,blue,red)"> Some text here</div>

<div class="white-grad" style="background-image:linear-gradient(black,lightblue,green)"> text very loooooooooooong here</div>

<div class="white-grad" style="background-image:radial-gradient(blue,pink)"> text very<br> loooooooooooong here</div>

CSS border gradient with SVG mask


You can also use it as common element and consider position:absolute to place it around the text:

.white-grad {
  color: #313149;
  padding: 25px;
  border-radius: 15px;
  display: inline-block;
  margin: 75px 0;
  position:relative;
  z-index:0;
}
.white-grad > svg {
  position:absolute;
  top:0;
  left:0;
  height:100%;
  width:100%;
  z-index:-1;
}

body {
  background: yellow;
}

.hide {
 height:0;
 width:0;
}
<svg class="hide" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop stop-color="#9c20aa" offset="0"/><stop stop-color="#fb3570" offset="1"/></linearGradient></defs><rect x="5" y="5" width="100%" height="100%" id="border" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(#Gradient)"/></svg>


<div class="white-grad"> 
<svg xmlns="http://www.w3.org/2000/svg">
  <use href="#border" />
</svg>
Some text here</div>

<div class="white-grad"> 
<svg xmlns="http://www.w3.org/2000/svg">
  <use href="#border" />
</svg>
text very loooooooooooong here</div>

CSS Mask method

Here is a different idea with CSS using mask where you will have transparency and it will also be responsive:

.white-grad {
  color: #313149;
  padding: 10px;
  display: inline-block;
  margin: 75px 0;
  position: relative;
  z-index: 0;
}
.white-grad:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  padding: 5px;
  border-radius: 15px;
  background: linear-gradient(to right, #9c20aa, #fb3570);
  -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;

}
<div class="white-grad"> Some text here</div>

<div class="white-grad"> Some long long long text here</div>

<div class="white-grad"> Some long long <br>long text here</div>

CSS border radius with linear gradient

With CSS variables, we can make it easy to adjust:

.white-grad {
  --b:5px;  /* border width*/
  --r:15px; /* the radius */

  color: #313149;
  padding: calc(var(--b) + 5px);
  display: inline-block;
  margin: 75px 0;
  position:relative;
  z-index:0;
}
.white-grad:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  padding: var(--b);
  border-radius: var(--r);
  background: var(--c,linear-gradient(to right, #9c20aa, #fb3570)); 
  -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;
}

body {
  background:#f2f2f2;
}
<div class="white-grad"> Some text here</div>

<div class="white-grad" style="--r:20px;--b:10px;--c:linear-gradient(140deg,red,yellow,green)"> Some long long long text here</div>

<div class="white-grad"  style="--r:30px;--b:8px;--c:linear-gradient(-40deg,black 50%,blue 0)"> Some long long <br>long text here</div>

<div class="white-grad"  style="--r:40px;--b:20px;--c:conic-gradient(black,orange,purple)"> Some long long <br>long text here<br> more and more more and more</div>

CSS gradient mask with border radius

Related question to get a different effect: How do you apply a gradient from outer to inner, only to borders, in CSS?


The above examples cover also the circle shape:

.white-grad {
  --b:5px;  /* border width*/

  color: #313149;
  display: inline-block;
  margin: 10px;
  width: 150px;
  aspect-ratio: 1;
  position: relative;
  z-index: 0;
}

.white-grad:before {
  content:"";
  position:absolute;
  z-index:-1;
  inset: 0;
  background: var(--c,linear-gradient(to right, #9c20aa, #fb3570));
  padding: var(--b);
  border-radius: 50%;
  -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;
}

body {
  background:#f2f2f2;
}
<div class="white-grad"></div>

<div class="white-grad" style="--b:10px;--c:linear-gradient(140deg,red,yellow,green)"></div>

<div class="white-grad"  style="--b:8px;--c:linear-gradient(-40deg,black 50%,blue 0)"></div>

<div class="white-grad"  style="--b:20px;--c:conic-gradient(black,orange,purple)"></div>

CSS circular border with gradient

Related question in case you want to apply an animation to the border: Button with transparent background and rotating gradient border


Also different radius shapes:

.white-grad {
  --b:5px;  /* border width*/

  color: #313149;
  display: inline-block;
  margin: 10px;
  width: 150px;
  aspect-ratio: 1;
  position: relative;
  z-index: 0;
}

.white-grad:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--c,linear-gradient(to right, #9c20aa, #fb3570));
  padding: var(--b);
  border-radius: var(--r,50%);
  -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;
}

body {
  background:#f2f2f2;
}
<div class="white-grad" style="--r:50% 0 50% 50%;"></div>

<div class="white-grad" style="--b:10px;--r:50% 0;--c:linear-gradient(140deg,red,yellow,green)"></div>

<div class="white-grad"  style="--b:8px;--r:50% 0 0;--c:linear-gradient(-40deg,black 50%,blue 0)"></div>

<div class="white-grad"  style="--b:20px;--r:50% 50% 0 0;--c:conic-gradient(black,orange,purple)"></div>

CSS curved shape with gradient border

and different border thickness:

.white-grad {
  --b:5px;  /* border width*/

  color: #313149;
  display: inline-block;
  margin: 10px;
  width: 150px;
  aspect-ratio: 1;
  position: relative;
  z-index: 0;
}
.white-grad:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--c,linear-gradient(#9c20aa, #fb3570));
  padding: var(--b);
  border-radius:var(--r,50%);
  -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;
}

body {
  background:#f2f2f2;
}
<div class="white-grad" style="--b:0 0 20px 20px;--r:50% 0 50% 50%;"></div>

<div class="white-grad" style="--b:10px 0 10px 0;--r:50% 0;--c:linear-gradient(140deg,red,yellow,green)"></div>

<div class="white-grad"  style="--b:8px 0px 0px 8px;--r:50% 0 0;--c:linear-gradient(40deg,black 50%,blue 0)"></div>

<div class="white-grad"  style="--b:20px 20px 0 20px;--r:50% 50% 0 0;--c:conic-gradient(pink,orange,red,pink)"></div>

Linear gradient curved shape

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • How to get a transparent background with your solution ? Thanks ! – Noé VIRICEL Aug 15 '18 at 23:20
  • I love you. @NoéVIRICEL if you change the #ccc value in the first linear-gradient to transparent, that should work – mikespitz_8 Aug 04 '20 at 13:15
  • 2
    @mikespitz_8 the first one won't work with transparency, you need to consider the other solutions for transparency (the SVG or the mask) – Temani Afif Aug 04 '20 at 13:23
  • 1
    This is really cool, but it seems that if the box background and page background are different colors, the page background square corner will stick out from behind the border. – Stonecraft Oct 01 '21 at 07:31
  • What are the `--b`, `--r`, and `--c` attributes are called? They look like a shorthand but I can't find reference to it anywhere. Thank you for this wonderful answer. – Willow Dec 11 '21 at 04:57
  • @Willow https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties – Temani Afif Dec 11 '21 at 08:30
  • Got the mask version working on Safari by setting `-wekit-mask-composite` to `source-out` or `xor` – Kasper Jan 03 '22 at 01:38
  • best answer here. also found a similar reference [article here](https://nikitahl.com/gradient-border-css) – Gangula Jan 11 '23 at 09:19
  • In the case of "CCS mask method", why do we use "#fff" in the later "mask:" when the gradient is of other colours? – Mr J Wolf Feb 23 '23 at 06:52
  • @MrJWolf the color inside mask doesn't matter, it needs to be a solid color without transparency (you can use #000, red, blue, #f00, etc). The color your see comes from the background definition – Temani Afif Feb 23 '23 at 08:30
2

2023 - single element, no pseudo elements, no SVG, no masks.

I can't take credit for this, I saw this on a website (I forgot the site and can't find it again).

  • It's just a normal button with rounded edges and a gradient background
  • It uses a box-shadow that is inset to fill the inside with white
  • It has a 2px border which is actually transparent, so the very edge of the button shows through

body {
  background: aliceblue;
}

.gradient-border {  
  border-radius: 24px;
  padding: 6px 12px;
  background-image: linear-gradient(90deg, red 0%, blue 100%);
  /* Fill the inside with white */
  background-origin: border-box;
  box-shadow: inset 0 100vw white;
  /* A transparent border, so the very edge of the button shows through */
  border: 2px solid transparent;
}
<button class="gradient-border">Hello</button>
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
-1

You need to wrap the button in a div and set the border-radius on that parent div. In order to work, you will have to set overflow:hidden to that parent div as well. Like so:

.btn-wrap {
    border-radius: 5px;
    overflow: hidden;
    margin: 20px;
    width: 60px;
}
a.btn.white-grad {
    background: #eee;
    color: #313149 !important;
    border: 20px solid #000;
    border-image-source: linear-gradient(to right, #9c20aa, #fb3570);
    border-image-slice: 20;
    line-height: 2;
}
 <div class="btn-wrap">
  <a href="#" class="btn white-grad">link</a>
 </div>