Let’s say you need a gradient border around an element. My mind goes like this:
- There is no simple obvious CSS API for this.
- I’ll just make a wrapper element with a
linear-gradient
background, then an inner element will block out most of that background, except a thin line of padding around it.
Perhaps like this:
If you hate the idea of a wrapping element, you could use a pseudo-element, as long as a negative z-index value is OK (it wouldn’t be if there was much nesting going on with parent elements with their own backgrounds).
Here’s a Stephen Shaw example of that, tackling border-radius
in the process:
You could even place individual sides as skinny pseudo-element rectangles if you didn’t need all four sides.
But don’t totally forget about border-image
, perhaps the most obtuse CSS property of all time. You can use it to get gradient borders even on individual sides:
Using both border-image
and border-image-slice
is probably the easiest possible syntax for a gradient border, it’s just incompatible with border-radius
, unfortunately.
DigitialOcean documents the same approach in another tutorial.
These are all great solutions, but I really hope in the near future linear-gradient on borders will be a reality. Would really help when making CSS graphics.
Hey Chris,
Yes, it was intended that you could use linear-gradient and border-image together. If you need rounded borders too, you should be able to do that with SVG and border image. I’ve successfully done that before without distorting the corners or anything. Let me know if you want me to post an example.
I’m sure everyone would enjoy having a demo like that to reference, including me!
When we were designing how border-image should work (many years ago), the thought was that the image would contain whatever rounded or fancy corners you wanted, and border-radius would only be used as a fallback, not as something that further clipped the corners.
yep i agree I would of done the same thing as well.
Wasn’t working for me on Firefox 64 until I changed the
border-image
toborder-image-source
asborder-image-slice: 1
was being overridden in the cascade.Funnily enough, browser support seems to be inconsistent for the last example. When
border-image-slice
is declared in advance and there’s no value for slice set in theborder-image
shorthand, as in the example above, Firefox 64 and Safari on iOS 12 uses the default slice value (which is 100%, resulting inborder-image: <image> 100%;
) while Chrome 71 and Opera cascades the previously declared value into the shorthand (which givesborder-image: <image> 1;
).Shorthands do normally reset the longhands to their initial values when left out, so I don’t know why Chrome and Opera wouldn’t. The last example seems to be fixed in iOS12 by changing
border-image
toborder-image-source
in those two classes.Looks much like a Chromium bug in the cascade. Interestingly, Chrome DevTools in both “Styles” and “Computed” tabs displays the value
1
ofborder-image-slice
struck-through, and Computed styles tab shows the value100%
coming from the shorthand declaration as overriding it — as it should be — but the value1
appears to somehow mysteriously “survive” this overriding.As a side note, I disagree that
border-image
(as well as its longhand sub-properties) is “obtuse”. It’s arguably the most misunderstood CSS properties of all time, and maybe significantly underrated, especially in combination with SVG images. Some of its abilities like 9-slice scaling and — especially — painting things outside the element’s box (viaborder-image-outset
) are unique in CSS, and can provide much shorter and cleaner solutions to many cases that otherwise would require hacking around pseudo-elements or even extra markup.Not sure about the last two. I’m getting different results in iOS Safari. I’d give it a look.
Here is a variant with SVG: https://codepen.io/ccprog/pen/BvwbKX?editors=1100 It tries to expose as many specifics to CSS as possible. Note that SVG 2 would allow
rx/ry
to be defined as CSS properties, but implementation is as yet incomplete. Gradient positioning must also be done in the markup. Border positioning follows SVG rules, which means half the border overflows the box.I have a pen showing how you can use background-clip to create rounded gradient borders https://codepen.io/GeorgePark/pen/EEGJEj
Thanks for writing this! I used the first option recently when a student wanted to have a gradient border animated.
Worked out pretty well: https://codepen.io/jupago/ Did not know about border-image-slice. Might also be fun to try clip-path. That 24ways article on Clip Paths got me wondering if that will eventually be the the next best thing. https://24ways.org/2018/clip-paths-know-no-bounds/
Been playing around with the Stephen Shaw’s ::before border. I tried applying it to one of my working projects and it seems that if you apply any sort of transform to the base element, then the pseudo element overrides the background color/image of the base element. I don’t know any way to make this work together with transform, just something to keep in mind, and a challenge to find a workaround.
I was able to come up with a pretty simple, single element, solution to this using multiple backgrounds and the
background-origin
property.The nice things about this approach are:
1. It isn’t affected by z-index
2. It can scale easily by just changing the width of the transparent border
Check it out: https://codepen.io/AlexOverbeck/pen/axGQyv?editors=1100
From reader Fabian Michael:
Another one from reader Blaz Kemperle:
Aaaaaaand another from reader Rogério Chaves: