168

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?

0

14 Answers 14

187

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

Sign up to request clarification or add additional context in comments.

8 Comments

This is the best answer. If you are using padding on your element, change the background-clip property to padding-box, border-box.
this works with a known solid background, but it's useless when you need a transparent space between the colored border and the content
why linear-gradien(white, white) is necessary ?
@allan.simon 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.
~~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 solution
|
134

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.

19 Comments

Works flawlessly in WebKit.
@jorisw Yeah, the point is that this works in all the browsers.
@bolshas added position:relative;z-index:-1 to the background and presto: jsfiddle.net/osw11t96/346
Works kinda, as long as you don't have transparency in your background color =/
Doesn't work in current Safari/Webkit (12.0.3) or Chrome/Blink (73.0.3683.86)
|
65

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); mask: conic-gradient(#000 0 0) content-box, conic-gradient(#000 0 0); 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

4 Comments

@KernelJames use xor instead of destination-out for safari
Best answer right here! Support for transparent backgrounds for the win.
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.
This works great with glass effect cards, thanks!
35

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.

4 Comments

Yeah, I assumed it wasn't possible, but just wanted to make sure. Image it is.
Gerben has a potential work-around in his answer, though it does add some extrenuous markup.
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 ?
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.
7

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>

Comments

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>

Comments

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

1 Comment

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.
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>

Comments

0

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.

2 Comments

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.
once you figure out what he is saying, this is a simple & obvious way to do it (adding extra markup however)
0

Simply adding a background-color: white (or any color) restored the border-radius effect for me. I'm not sure why it worked, but it was much simpler.

Comments

0

Simple solution, may be it will help to someone.

Increase or decrease padding for .wrapper-div to change border width.. (Adjust border-radius accordingly)

.wrapper-div { background-image: linear-gradient(to right, skyblue, orange); padding: 5px; border-radius: 20px; } .wrapper-inner-div { padding: 20px; background: #ccc; border-radius: 15px; }
<div class="wrapper-div"> <div class="wrapper-inner-div">Hi, I have gradient border with border-radius</div> </div>

Comments

-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!

Comments

-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>

1 Comment

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).
-2

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>

3 Comments

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.
How is that different from the top voted answer?
this is a simple copy of my answer

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.