Cross-browser filters with CSS and SVG

Warning This article was written over six months ago, and may contain outdated information.

For some time now, since Safari 6 and Chrome 18 were released, we‚Äôve been able to use the -webkit-filter CSS prop¬≠er¬≠ty to apply graph¬≠i¬≠cal fil¬≠ters to HTML con¬≠tent. But it seems that many peo¬≠ple aren‚Äôt aware that you can also do this in Fire¬≠fox and ‚ÄĒ with¬≠in a very lim¬≠it¬≠ed set of para¬≠me¬≠ters ‚ÄĒ IE9 and above.

The rea­son for this is that all of these browsers sup­port SVG fil­ter effects, and CSS fil­ter effects are basi­cal­ly short­hand func­tions that apply pre­de­fined con­fig­u­ra­tions of SVG filters.

CSS and SVG filters

Apply­ing fil­ters in CSS is very easy; here’s an exam­ple of how you’d apply Gauss­ian blur to an element:

E { -webkit-filter: blur(3px); }

The blur() func¬≠tion is a short¬≠hand for SVG‚Äôs feGaussianBlur fil¬≠ter, which is defined in SVG markup like this:

<filter id="blur">
  <feGaussianBlur stdDeviation="3"/>
</filter>

The val­ue of the stdDeviation attribute is in pix­els, match­ing the val­ue sup­plied to the blur() func­tion. To apply this fil­ter to an ele­ment in Fire­fox you pass its id val­ue into the url() func­tion of the unpre­fixed filter property:

E { filter: url('filters.svg#blur'); }

The result looks like this:

Broken Links logo
This image has a blur applied with a CSS/SVG filter.

In my exam­ple I’m link­ing to an exter­nal SVG file (filters.svg) where I’ve pre­de­fined a hand­ful of fil­ters, but you can also embed the code in the same page as your tar­get ele­ment and use only the id value.

Some CSS fil­ter func­tions mask much more com­plex­i­ty; the drop-shadow func­tion, for exam­ple, is pret­ty simple:

E { -webkit-filter: drop-shadow(5px 5px 2px black); }

But the same func­tion in SVG com­bines off­set posi­tions, fill colours, and Gauss­ian blur, then merges them all togeth­er before applying:

<filter id="drop-shadow">
  <feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
  <feOffset dx="5" dy="5" result="offsetblur"/>
  <feFlood flood-color="#000000"/>
  <feComposite in2="offsetblur" operator="in"/>
  <feMerge>
    <feMergeNode/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
</filter>

The out­put is the same, however:

Broken Links logo
This image has a drop shad­ow applied with a CSS/SVG filter.

If you want to use this tech­nique to apply fil­ters in Fire­fox there’s an excel­lent page on MDN which shows each CSS fil­ter func­tion and its equiv­a­lent SVG fil­ter markup.

While defin¬≠ing fil¬≠ter effects in this way lets us use them in Fire¬≠fox, there are a few draw¬≠backs to this approach; as all the val¬≠ues are in the SVG markup, you lose the abil¬≠i¬≠ty to change them using CSS alone, and it becomes more com¬≠pli¬≠cat¬≠ed to ani¬≠mate them.

Internet Explorer

I men­tioned at the start of this arti­cle that you can apply SVG fil­ters in IE9+, but there are some major draw­backs: for a start, they can’t be applied with CSS, not even in the way Fire­fox cur­rent­ly does. The only way to apply fil­ter effects to an ele­ment in IE is to make the ele­ment part of the SVG markup; so if you want to apply a fil­ter to an image you must use the image ele­ment inside the svg ele­ment, with the fil­ter ref­er­ence as the val­ue of the filter attribute:

<svg>
  <defs>…</defs>
  <image xlink:href="/wp-content/uploads/2013/11/logo-no-bg.png" width="200" height="158" filter="url(#desaturate)" />
</svg>

In this exam­ple I’m using the feColorMatrix fil­ter to com­plete­ly desat­u­rate the image, as shown below (inspect the ele­ment to see the full markup required):

This image is desat­u­rat­ed with an SVG filter.

To be clear, all browsers can use this method, but the big lim­i­ta­tion in IE’s imple­men­ta­tion is that you can only apply fil­ters to images (as above) or text, and not chunks of HTML. Oth­er browsers sup­port the foreignObject ele­ment, which allows you to include HTML inside your SVG:

<foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" filter="url(#blur)">
  <div xmlns="http://www.w3.org/1999/xhtml">
    <h1>Elephants</h1>
  </div>
</foreignObject>

It’s some­what dis­ap­point­ing that IE has yet to imple­ment foreignObject.

These draw­backs severe­ly lim­it the use­ful­ness of SVG fil­ters in IE, cer­tain­ly com­pared to the cur­rent imple­men­ta­tions by the oth­er browsers. If you want to play around with fil­ters in IE a lit­tle more, there’s a use­ful SVG fil­ters test page.

Browser Consensus

For fil­ters to reach their full poten­tial, a few things need to hap­pen: IE should imple­ment foreignObject; Fire­fox should imple­ment CSS fil­ter func­tions (work is under­way); and all oth­er browsers should imple­ment the url() func­tion that Fire­fox has, allow­ing for new and more fine­ly tuned filters.

For now, there is an excel­lent CSS fil­ters poly­fill avail­able which pro­vides fil­ter sup­port to Fire­fox and old­er ver­sions of IE, using the non-stan­dard Direc­tX fil­ters which were removed from IE10+.

4 comments on
“Cross-browser filters with CSS and SVG”

  1. [‚Ķ] Cross-brows¬≠er fil¬≠ters with CSS and SVG | Bro¬≠ken Links [‚Ķ]

  2. [‚Ķ] Cross-brows¬≠er fil¬≠ters with CSS and SVG ‚Äď For some time now, since Safari 6 and Chrome 18 were released, we‚Äôve been able to use the ‚Äďwebkit-fil¬≠ter CSS prop¬≠er¬≠ty to apply graph¬≠i¬≠cal fil¬≠ters to HTML content. [‚Ķ]

  3. Units in an SVG Fil­ter aren’t nec­es­sar­i­ly pix­els though, they’re what­ev­er units are defined in the view­Box. This defaults to CSS pix­els, but if you’re using em’s or pts or what­ev­er, this is what your fil­ter units are going to be. It’s also pos­si­ble to use rel­a­tive siz­ing inside SVG filters.

    (Also, I think that IE10 was the first brows¬≠er to sup¬≠port SVG fil¬≠ters, not IE9 ‚ÄĒ you might want to check that.)

  4. Image with CSS Shadow

    http://www.corelangs.com/css/box/image-shadow.html

    css shad­ow