Possible to use border-radius together with a border-image which has a gradient?
Asked Answered
G

13

141

I'm styling an input field which has a rounded border (border-radius), and attempting to add a gradient to said border. I can successfully make the gradient and the rounded border, however neither work together. It's either rounded with no gradient, or a border with a gradient, but no rounded corners.

-webkit-border-radius: 5px;
-webkit-border-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b0bbc4), to(#ced9de)) 1 100%;

Is there anyway to have both CSS properties work together, or is this not possible?

Grunter answered 18/4, 2011 at 18:4 Comment(0)
A
33

Probably not possible, as per the W3C spec:

A box's backgrounds, but not its border-image, are clipped to the appropriate curve (as determined by ‘background-clip’). Other effects that clip to the border or padding edge (such as ‘overflow’ other than ‘visible’) also must clip to the curve. The content of replaced elements is always trimmed to the content edge curve. Also, the area outside the curve of the border edge does not accept mouse events on behalf of the element.

This is likely because border-image can take some potentially complicated patterns. If you want a rounded, image border, you'll need to create one yourself.

Alarise answered 18/4, 2011 at 18:30 Comment(4)
Yeah, I assumed it wasn't possible, but just wanted to make sure. Image it is.Grunter
Gerben has a potential work-around in his answer, though it does add some extrenuous markup.Alarise
It sounds like nonsense to me. Browsers can compute (the mask and colors for) a border-radius:10px; border-top:10px dotted blue; border-left:4px groove green; but they won't be able to apply the same mask on a border-image ?Fatwitted
This question is 6 years old at this point. While it's still not part of the spec AFAIK (so even if/though browsers could, they probably don't), things do evolve and it may be more feasible now and the spec may change. That said, keep in mind that things that look simple aren't always so easy to actually do, especially when technical debt is involved.Alarise
S
148

Working on this same problem. Came across a non-svg solution which is more succinct than others here:

div{
  width: 300px;
  height: 80px;
  border: double 1em transparent;
  border-radius: 30px;
  background-image: linear-gradient(white, white), 
                    linear-gradient(to right, green, gold);
  background-origin: border-box;
  background-clip: content-box, border-box;
}
<div></div>

This is not my own solution and has been taken from here: https://gist.github.com/stereokai/36dc0095b9d24ce93b045e2ddc60d7a0

Stumer answered 29/10, 2018 at 1:26 Comment(7)
This is the best answer. If you are using padding on your element, change the background-clip property to padding-box, border-box.Clack
this works with a known solid background, but it's useless when you need a transparent space between the colored border and the contentTorsk
why linear-gradien(white, white) is necessary ?Arango
@Arango That linear-gradient(white, white) is to add background to the box itself while the linear-gradient(green, gold) is for the border. Change the values in linear-gradient(white, white) to some other color and see the results yourself.Caye
~~For me this has the opposite effect, the inside is filled the border is transparent, removing~~ Adding the linear-gradient(white, white), was the solution, but then the inside is not transparent which might not be a viable solutionBotulin
imagine, if button has transparent background? it's not working if button has no background except need only border.Niphablepsia
thank you this is flawless for meCongelation
E
131

This is possible, and it does not require extra markup, but uses an ::after pseudo-element.

                                   screenshot

It involves putting a pseudo-element with a gradient background below and clipping that. This works in all current browsers without vendor prefixes or hacks (even IE), but if you want to support vintage versions of IE, you should either consider solid color fallbacks, javascript, and/or custom MSIE CSS extensions (i.e., filter, CSSPie-like vector trickery, etc).

Here's a live example (jsfiddle version):

@import url('//raw.githubusercontent.com/necolas/normalize.css/master/normalize.css');

html {
    /* just for showing that background doesn't need to be solid */
    background: linear-gradient(to right, #DDD 0%, #FFF 50%, #DDD 100%);
    padding: 10px;
}

.grounded-radiants {
    position: relative;
    border: 4px solid transparent;
    border-radius: 16px;
    background: linear-gradient(orange, violet);
    background-clip: padding-box;
    padding: 10px;
    /* just to show box-shadow still works fine */
    box-shadow: 0 3px 9px black, inset 0 0 9px white;
}

.grounded-radiants::after {
    position: absolute;
    top: -4px; bottom: -4px;
    left: -4px; right: -4px;
    background: linear-gradient(red, blue);
    content: '';
    z-index: -1;
    border-radius: 16px;
}
<p class="grounded-radiants">
    Some text is here.<br/>
    There's even a line break!<br/>
    so cool.
</p>

The extra styling above is to show:

  • This works with any background
  • It works just fine with box-shadow, inset or not
  • Does not require you to add the shadow to the pseudo-element

Again, this works with IE, Firefox and Webkit/Blink browsers.

Extemporary answered 24/3, 2015 at 2:8 Comment(18)
Works flawlessly in WebKit.Sienese
@Sienese Yeah, the point is that this works in all the browsers.Extemporary
nice update to this old problem, i came back and was pleased to find a new solution :)Ivyiwis
while it still technically isn't using border-image, it does achieve the intended effect. Nicely done!Ramburt
If you put it in a <div> with background, it doesn't work. see fiddle: jsfiddle.net/osw11t96/344Acro
@Acro added position:relative;z-index:-1 to the background and presto: jsfiddle.net/osw11t96/346Extemporary
This is old, but no it does not work well. I would like to do it without an additional background colorJenijenica
@CalebPrenger what do you mean, an additional background color? You mean you want the div to be transparent in the center and have only a gradient on the border?Extemporary
Works kinda, as long as you don't have transparency in your background color =/Coagulase
@CamiloMartin You can remove background-clip: padding-box; and border: 4px solid transparent; from .grounded-radiants. These styles are redundant.Pinero
Nice try. But having to add position:relative;z-index:-1 to the parent make it practically unusable.Fatwitted
@Fatwitted Not really. You can have Parent with position relative, child div with the content(position relative) and antother child element with position absolute. Then it should work with background set on either of them. Z-index would still be used tho.Soiree
Doesn't work on self-closing tags such as input elements, unfortunately.Citrin
Note; using with Bootstrap 4 requires display: inline; and z-index: 1; on the main element (.grounded-radiants).Indispose
what happens when you have a transparent background and really needing a BORDER ? you don't get a border, you get a backgroundChasechaser
Doesn't work in current Safari/Webkit (12.0.3) or Chrome/Blink (73.0.3683.86)Flexible
I found an example of this approach working here: css-tricks.com/gradient-borders-in-cssBackward
Warning ! Since 2019, this solution does not work anymore ! Be aware, best answer available on css-tricks.com for now...Rueful
C
50

Now we can use mask to easily achieve this while having transparency and responsiveness

.box {
  position: relative;
  padding: 20px 30px;
  margin: 5px;
  display: inline-block;
  font-size: 30px;
}

.box::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50px;
  padding: 10px; /* control the border thickness */
  background: linear-gradient(45deg, red, blue);
  -webkit-mask: 
    linear-gradient(#fff 0 0) content-box, 
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
<div class="box">
  Hello World
</div>

<div class="box">
  Hello World again
</div>
<div class="box">
  Hello World <br> two lines
</div>

More details: https://dev.to/afif/border-with-gradient-and-radius-387f

Constitutional answered 3/4, 2021 at 23:9 Comment(6)
Works in Chrome and Firefox but not in Safari (desktop).Blackman
@KernelJames use xor instead of destination-out for safariConstitutional
Best answer right here! Support for transparent backgrounds for the win.Promotion
Only solution that supports transparencyCarthy
As of writing, this now seems to work on Safari desktop.Harleigh
I think this could be better by moving border-radius to .box and set border-radius: inherit on .box::before that way utility classes can affect it.Trapp
A
33

Probably not possible, as per the W3C spec:

A box's backgrounds, but not its border-image, are clipped to the appropriate curve (as determined by ‘background-clip’). Other effects that clip to the border or padding edge (such as ‘overflow’ other than ‘visible’) also must clip to the curve. The content of replaced elements is always trimmed to the content edge curve. Also, the area outside the curve of the border edge does not accept mouse events on behalf of the element.

This is likely because border-image can take some potentially complicated patterns. If you want a rounded, image border, you'll need to create one yourself.

Alarise answered 18/4, 2011 at 18:30 Comment(4)
Yeah, I assumed it wasn't possible, but just wanted to make sure. Image it is.Grunter
Gerben has a potential work-around in his answer, though it does add some extrenuous markup.Alarise
It sounds like nonsense to me. Browsers can compute (the mask and colors for) a border-radius:10px; border-top:10px dotted blue; border-left:4px groove green; but they won't be able to apply the same mask on a border-image ?Fatwitted
This question is 6 years old at this point. While it's still not part of the spec AFAIK (so even if/though browsers could, they probably don't), things do evolve and it may be more feasible now and the spec may change. That said, keep in mind that things that look simple aren't always so easy to actually do, especially when technical debt is involved.Alarise
P
6

I would use SVG for this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220" width="100%" height="100%" preserveAspectRatio="none">
  <defs>
    <linearGradient id="gradient">
      <stop offset="0" style="stop-color:#0070d8" />
      <stop offset="0.5" style="stop-color:#2cdbf1" />
      <stop offset="1" style="stop-color:#83eb8a" />
    </linearGradient>
  </defs>
  <ellipse ry="100" rx="100" cy="110" cx="110" style="fill:none;stroke:url(#gradient);stroke-width:6;" />
</svg>

SVG can be used as separate file (preferred way) or like part of value of background (code below will work only in webkit-browsers):

div {
  width: 250px;
  height: 250px;
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220" width="100%" height="100%" preserveAspectRatio="none"><defs><linearGradient id="gradient"><stop offset="0" style="stop-color:#0070d8" /><stop offset="0.5" style="stop-color:#2cdbf1" /><stop offset="1" style="stop-color:#83eb8a" /></linearGradient></defs><ellipse ry="100" rx="100" cy="110" cx="110" style="fill:none;stroke:url(#gradient);stroke-width:6;" /></svg>');
}
<div></div>

For this to work in MS Edge and Firefox we should escape our markup after utf8, so we will be replacing double quotes " with single quotes ', # with %23 and % with %25:

div {
  width: 250px;
  height: 250px;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 220' width='100%25' height='100%25' preserveAspectRatio='none'><defs><linearGradient id='gradient'><stop offset='0' style='stop-color:%230070d8' /><stop offset='0.5' style='stop-color:%232cdbf1' /><stop offset='1' style='stop-color:%2383eb8a' /></linearGradient></defs><ellipse ry='100' rx='100' cy='110' cx='110' style='fill:none;stroke:url(%23gradient);stroke-width:6;' /></svg>");
  background-size: 100% 100%; /* Fix for Fifefox image scaling */
}
<div></div>
Pinero answered 22/6, 2017 at 8:52 Comment(0)
R
1

What if you apply the gradient to the background. Than and add an extra div inside, with margin set to the old border-width and with a white background, and of course a borderradius. That way you have the effect of a border, but are actually using background, which is clipped correctly.

Revest answered 18/4, 2011 at 18:46 Comment(2)
I'd add that instead of adding such markup on the HTML document, one could do it using JavaScript. That way you get the best of both worlds.Tip
once you figure out what he is saying, this is a simple & obvious way to do it (adding extra markup however)Ivyiwis
C
1

This always works for me in WebKit, although its a bit tricky!

Basically you just make the border bigger then mask it out with bigger and smaller pseudo-element's borders : ).

.thing {
  display: block;
  position: absolute;
  left: 50px;
  top: 50px;
  margin-top: 18pt;
  padding-left: 50pt;
  padding-right: 50pt;
  padding-top: 25pt;
  padding-bottom: 25pt;
  border-radius: 6px;
  font-size: 18pt;
  background-color: transparent;
  border-width: 3pt;
  border-image: linear-gradient(#D9421C, #E8A22F) 14% stretch;
}
.thing::after {
  content: '';
  border-radius: 8px;
  border: 3pt solid #fff;
  width: calc(100% + 6pt);
  height: calc(100% + 6pt);
  position: absolute;
  top: -6pt;
  left: -6pt;
  z-index: 900;
}
.thing::before {
  content: '';
  border-radius: 2px;
  border: 1.5pt solid #fff;
  width: calc(100%);
  height: calc(100% + 0.25pt);
  position: absolute;
  top: -1.5pt;
  left: -1.5pt;
  z-index: 900;
}

http://plnkr.co/edit/luO6G95GtxdywZF0Qxf7?p=preview

Cangue answered 12/11, 2013 at 4:57 Comment(1)
this works really nicely if you have a solid-color background to go against, but in the interest of having rounded border-image against a changing background (such as a scrolling page on a static bg) this will not work.Ivyiwis
B
1

Solutions for transparent elements: working at least in Firefox.

There is actually one way I found without pseudo classes - but it only works for radial gradients:

body {
  background: linear-gradient(white, black), -moz-linear-gradient(white, black), -webkit-linear-gradient(white, black);
  height: 300px;
  
  }

div{
text-align: center;
  width: 100px;
  height: 100px;
  font-size:30px;
  color: lightgrey;
  border-radius: 80px;
  color: transparent;
  background-clip: border-box, text;
  -moz-background-clip: border-box, text;
  -webkit-background-clip: border-box, text;
  background-image: radial-gradient(circle,
      transparent, transparent 57%, yellow 58%, red 100%), repeating-linear-gradient(-40deg, yellow,
  yellow 10%, orange 21%, orange 30%, yellow 41%);
  line-height: 100px;
}
<body>
<div class="radial-gradient"> OK </div>
</body>

Getting a transparent element with pseudo classes I only found this way - ok it is not a gradient, but it is at least a multicolored striped border (looking like life-rings):

body {
  background: linear-gradient(white, black, white);
  height: 600px;
  }

div{
  position: absolute;
  width: 100px;
  height: 100px;
  font-size:30px;
  background-color:transparent;
  border-radius:80px;
  border: 10px dashed orange;
  color: transparent;
  background-clip: text;
  -moz-background-clip: text;
  -webkit-background-clip: text;
  background-image: repeating-linear-gradient(-40deg, yellow,
  yellow 10%, orange 11%, orange 20%, yellow 21%);
  text-align:center;
  line-height:100px;
}


div::after {
    position: absolute;
    top: -10px; bottom: -10px;
    left: -10px; right: -10px;
    border: 10px solid yellow;
    content: '';
    z-index: -1;
    border-radius: 80px;
    }
<body>
<div class="gradient"> OK </div>
</body>

with a svg (most satisfying in terms of variability but needs most codelines too):

body{
  margin: 0;
  padding: 0;
}

div {
  position: absolute;
  display: flex;
  align-items: center;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
}

span {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 100px;
  height: 100px;
  line-height: 105px;
  font-size:40px;
  background-clip: text;
  -moz-background-clip: text;
  -webkit-background-clip: text;
  background-image: repeating-linear-gradient(-40deg, yellow,
  yellow 10%, orange 11%, orange 20%, yellow 21%);
  color: transparent;
}

svg {
  fill: transparent;
  stroke-width: 10px; 
  stroke:url(#gradient);
  
}
<head>

</head>
<body>

<div>
<span>OK</span>
  <svg>
    <circle class="stroke-1" cx="50%" cy="50%" r="50"/>
    <defs>
      <linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="15%" gradientTransform="rotate(-40)" spreadMethod="reflect">
       
        <stop offset="0%" stop-color="orange" />
        <stop offset="49%" stop-color="orange" />
        <stop offset="50%" stop-color="yellow" />
        <stop offset="99%" stop-color="yellow" />

      </linearGradient>
  </defs>
  </svg>
  

</div>

</body>
Boatyard answered 8/3, 2018 at 17:26 Comment(0)
C
-1

Solution for Gradient Border.

This code work fine for me!

div#id123::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 31px;
    padding: 3px;
    width: 100%;
    height: 100px;
    background: linear-gradient(90deg, rgba(235,163,225,1) 0%, rgba(228,161,228,1) 13%, rgba(163,99,233,1) 47%, rgba(212,129,166,1) 62%, rgba(237,172,70,1) 89%, rgba(255,57,250,0.8733377659574468) 100%);
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
}
<div id = "id123">
<div class="" id="" data-de-type="input" data-title="input" data-delay="500" type="name" style="margin-top: 10px; outline: none; cursor: pointer; font-family: Nunito, Helvetica, sans-serif !important;" aria-disabled="false" data-google-font="Nunito">
<input type="name" placeholder="Enter Full Name" name="name" class="" data-type="extra">
</div>
<div class="" id="" data-de-type="input" data-title="input" data-delay="500" type="name" style="margin-top: 10px; outline: none; cursor: pointer; font-family: Nunito, Helvetica, sans-serif !important;" aria-disabled="false" data-google-font="Nunito">
<input type="name" placeholder="Enter Full Name" name="name" class="" data-type="extra">
</div>
<div class="" id="" data-de-type="input" data-title="input" data-delay="500" type="name" style="margin-top: 10px; outline: none; cursor: pointer; font-family: Nunito, Helvetica, sans-serif !important;" aria-disabled="false" data-google-font="Nunito">
<input type="name" placeholder="Enter Full Name" name="name" class="" data-type="extra">
</div>
</div>
Croze answered 1/10, 2022 at 2:6 Comment(3)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Impediment
How is that different from the top voted answer?Troubadour
this is a simple copy of my answerConstitutional
C
-1

You could use the CSS clip-path property.

.rounded-border-image {
    --border-image: linear-gradient(to bottom, orange, skyblue, magenta);
    --border-radius: 4px;
    background-image: var(--border-image);
    background-origin: border-box;
    border-color: transparent;
    border-radius: var(--border-radius);
    border-style: solid;
    border-width: 4px;
    clip-path: inset(0% 0% 0% 0% round var(--border-radius));
}

Which looks something like this when used around an img element of a plain, red, 40x40 image.

.rounded-border-image {
    --border-image: linear-gradient(to bottom, orange, skyblue, magenta);
    --border-radius: 4px;
    background-image: var(--border-image);
    background-origin: border-box;
    border-color: transparent;
    border-radius: var(--border-radius);
    border-style: solid;
    border-width: 4px;
    clip-path: inset(0% 0% 0% 0% round var(--border-radius));
}
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAN0lEQVR42u3OQQ0AAAjEMM6/aMAECY8uE9B01f63AAICAgICAgICAgICAgICAgICAgICAgIC3jTyeE/ZxiLJ7wAAAABJRU5ErkJggg==" class="rounded-border-image">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAN0lEQVR42u3OQQ0AAAjEMM6/aMAECY8uE9B01f63AAICAgICAgICAgICAgICAgICAgICAgIC3jTyeE/ZxiLJ7wAAAABJRU5ErkJggg==" class="rounded-border-image" style="--border-radius: 16px;">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAN0lEQVR42u3OQQ0AAAjEMM6/aMAECY8uE9B01f63AAICAgICAgICAgICAgICAgICAgICAgIC3jTyeE/ZxiLJ7wAAAABJRU5ErkJggg==" class="rounded-border-image" style="--border-radius: 40px;">

Hope this helps!

Chitchat answered 25/11, 2022 at 19:9 Comment(4)
the clip-path is doing nothing, you can remove itConstitutional
Removed it, thanks for the callout :)Pauli
Never mind, this person rejected the edits. They really want those pointless declarations there.Pauli
It serves a purpose. Unfortunately, I forgot what that purpose was. I absolutely hate redundant lines. I wouldn't have left it in there if it hadn't served a purpose.Chitchat
B
-1

According to this page, you can use 「clip-path」

#example-card{
  width: 200px;
  height: 200px;
  border: 20px solid hsl(80 100% 50%);
  border-image-slice: 1;

  border-image-source: conic-gradient(
    from 0deg,
    hsl(80 100% 50%), 
    hsl(200 100% 60%),
    hsl(80 100% 50%)
  );

  clip-path: inset(0 round 24px);
}
<div id="example-card">EXAMPLE</div>
Barger answered 3/11, 2023 at 8:51 Comment(1)
While clip-path is pretty cool, it doesn't round the inside of the border image like a solid color does. And BTW you can get the same effect with simply setting border-radius like normal and then clip-path: border-box; (which provides fallback to the solid color w/out repeating the radius).Andras
N
-1

for me this one help me quickly,

.border_linear_btn {
    width: 300px;
    height: 100px;
    margin: 2rem;
    border: solid 5px transparent;
    border-radius: 50px;
    background:
        linear-gradient( #050C35, #050C35) padding-box,
        linear-gradient(92.7deg, #0064fb, red) border-box;
}
<div class="border_linear_btn"> </div>
Niphablepsia answered 18/3 at 3:40 Comment(0)
U
-5

we need the background trans not white ..

div{
  width: 300px;
  height: 80px;
  border: double 1em transparent;
  border-radius: 30px;
  background-image: linear-gradient(transparent, transparent), 
                    linear-gradient(to right, green, gold);
  background-origin: border-box;
  background-clip: content-box, border-box;
}
<div></div>
Ultraviolet answered 21/6, 2021 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.