This post is the first in a series about the power of CSS.
Article Series:
- Colorizing SVG Backgrounds (this post)
- Dropdown Menus
- Logical Styling Based On the Number of Given Elements
CSS is getting increasingly powerful, and with features like CSS grid and custom properties (also known as CSS variables), we’re seeing some really creative solutions emerging. The possibilities are still being explored on what CSS can do to make writing UI’s simpler, and that’s exciting!
One of those is now one of my favorite CSS features: filters. Let’s look at how we can use filters to solve a problem you may have encountered when working with SVG as a background image on an element.
CSS Filters
First, let’s start by with an overview of filters. They include the following functions:
blur()
brightness()
contrast()
drop-shadow()
grayscale()
hue-rotate()
invert()
opacity()
saturate()
sepia()
These effects can also be achieved with SVG filters or WebGL shaders, but CSS filters are the easiest way to implement basic transformations in the most browser-efficient manner. Because they are shortcuts of SVG filter effects, you can also use filter: url()
and specify a filter effect ID onto any element. If you want to play around with custom filters, I recommend checking out cssfilters.co.
The Problem: Editing SVG Backgrounds
I love using the SVG (scalable vector graphics) format for web design. SVG is a great image format for the web, and since it’s based on code, it allows for high-quality responsive and interactive content. When you inject SVG onto the page, you have access to each of its internal elements and their properties, allowing you to animate them, update values (such as color), and dynamically inject additional information. SVG is also a great icon format, especially instead of icon fonts, and in smaller UI elements due to its high quality (think: retina screens) and small image size (think: performance).
I find that often, when SVG is used for these smaller elements, or as a large area of illustration, it’s included as a background image for simplicity. The drawback to this is that the SVG is no longer under your control as a developer. You can’t adjust individual properties, like fill color, of an SVG background because it is treated just like any image. This color conundrum can be solved with CSS! Filters to the rescue!
Adjusting Brightness
The first time I discovered the SVG background challenge was when I was working on a website that had white SVG icons for social share icons that lived on a background determined to match that application. When these icons were moved onto a white background, they were no longer visible. Instead of creating a new icon, or changing the markup to inject inline SVG, you can use filter: brightness()
.
With the brightness
filter, any value greater than 1
makes the element brighter, and any value less than 1
makes it darker. So, we can make those light SVG’s dark, and vice versa!
What I did above was create a dark
class with filter: brightness(0.1)
. You can also do the opposite for darker icons. You can lighten icons by creating a light
class with something like filter: brightness(100)
or whatever is suitable to your needs.
Icons with a fill color of #000
, or rgb(0,0,0)
will not brighten. You need to have a value greater than 0
in any of the rgb
channels. fill: rgb(1,1,1)
works great with a high brightness value such as brightness(1000)
, but even brightness(1000)
will not work on pure black. This is not an issue with light colors and white.
Adjusting Color
We’ve now seen how to adjust light and dark values with a brightness()
filter, but that doesn’t always get us the desired effect. What if we want to inject some color into those icons? With CSS filters, we can do that. One little hack is to use the sepia
filter along with hue-rotate
, brightness
, and saturation
to create any color we want.
From white, you can use the following mixtures to get the navy, blue, and pink colors above:
.colorize-pink {
filter: brightness(0.5) sepia(1) hue-rotate(-70deg) saturate(5);
}
.colorize-navy {
filter: brightness(0.2) sepia(1) hue-rotate(180deg) saturate(5);
}
.colorize-blue {
filter: brightness(0.5) sepia(1) hue-rotate(140deg) saturate(6);
}
The world is your oyster here. SVG is just one use case for multiple filters. You can apply this to any media type—images, gifs, video, iframes, etc., and support is pretty good, too:
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
18* | 35 | No | 79 | 6* |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
119 | 119 | 4.4* | 6.0-6.1* |
One final note here is to remember your user! Filters will not work in Internet Explorer, so please send a visible image to all of your users (i.e. don’t use a white SVG with an applied filter on a white background, because your IE users will not see anything). Also, remember to use alternative text for icon accessibility, and you’ll be golden to use this technique in your own applications!
This seems like it could be used for some really cool stuff and perhaps I’m missing something, but in the examples provided, wouldn’t it be easier to just set the fill/stroke of the SVG icons via CSS or by leveraging the
currentColor
value? This would also broaden the support to any browser that supports SVG icons and opens up the possibility of two-tone icons.Ahh yes, I did miss something! These are used as background images not as SVG sprites. Apologies!
I’m curious to know why the Sepia is used in the last example, can this be explained a bit more please?
The
sepia
filter applies an actual “color” to the SVG, which can then be manipulated viahue-rotate
. The original SVG has a color of white (#fff
) which essentially doesn’t have a “hue” and so can’t be changed to a different color viahue-rotate
. (A pure black [#000
] SVG would have the same issue.)I was aware that Sepia was used to generate colour from a black and white image although I didn’t even realise sepia was included in the CSS filters!
Thanks John, makes perfect sense now :)
Using
use
is almost always the better option here, but this is still a cool idea that certainly has some valid use cases.how about
mask
?That depends on browser requirements: https://caniuse.com/#search=filter vs https://caniuse.com/#search=mask.
Black images could be turned into white images with
filter: invert(100%)
, than the colorize feature will work here too:Ooh good call with the
invert