CSS Filter Primitives — SVG Filters on HTML Elements

Last week I walked through the css named filter functions which are relatively easy to understand and use. However, they’re only one way to add css filter effects and the least powerful way at that. Today I want to look at filter primitives, one of the other and more powerful ways to add filters to images and elements.

Filter primitives offer us a bit more than named functions. For one there are more of them. For another where primitives and named filters overlap the former tend to have more options in how they’re used.

photo-filters.jpg

Primitives are a bit more complicated to work with and there are far more details to working with all of them than I can cover here in a single post. I also need to offer an apology upfront. Where primitives are concerned, I found the spec more than a bit confusing and lacking examples to help clarify things.

I sought other resources, but most talked about filter primitives through the lens of SVG filters, which also led to some confusion on my part. On the bright side I was able to work an example easily enough, which I’ll share below. What wasn’t so easy was making it work in all the browsers I thought would support it.

What are CSS Filter Primitives?

Filter primitives aren’t that different from named functions. In many ways they’re the same thing just done a different way. Primitives were originally developed as SVG filters and are expanding to be applied to more than SVG images.

Like named functions, they’re simply a way to add some filters without the need for a graphic editor. That makes them non-destructive and more flexible. They’re written using SVG markup and are applied to an image or element through the css filter function, specifically the url value of the filter property.

1
.filtered {filter: url(path-to-filter-primitives);}

The general concept of filter primitives isn’t any more complicated than it was for named filters. The primitives are simply written a different way than the named functions. There are more of them with more options and they’re applied to the element you want to filter in a different way.

An example is probably the best way to explain what filter primitives are and how they work.

Working with SVG Filter Primitives

One of the filter functions I used in examples last week was hue-rotate. As a named function, you might use it like this.

1
filter: hue-rotate(60deg);

The equivalent filter primitive would look something like this

1
<feColorMatrix type="hueRotate" values="60"/>

It’s markup as opposed to css and it has a new name, but you can see the type attribute matches the function name from the css and takes a similar value. There’s a little more code needed to make the above primitive work, but you can see how the primitive and named function are related.

Let’s walk through a simple example. Once again I’m going to apply filters to my image of the Strawberry Fields Memorial in Central Park.

Strawberry Fields Memorial, Central Park, New York

The html is simple. Just an image inside a div.

1
2
3
<div class="filtered">
  <img src="images/strawberry-fields.jpg" />
</div>

The css is also simple.

1
filter: url(filters.svg#filter-primitives);

I’m using the url value for the filter property and having the url point to a document named filters.svg. Inside that document I’m calling the specific filter with the id filter-primitives.

So far easy enough, but we need to define the actual filter. Here’s the full filters.svg document.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg">
<defs>
   <filter id="filter-primitives">
     <feGaussianBlur stdDeviation="3" />
     <feColorMatrix type="hueRotate" values="270"/>
     <feColorMatrix type="saturate" values="0.75"/>
     <feBlend mode="multiply"/>
   </filter>
</defs>
</svg>

The document starts by defining itself as xml with an an svg doctype. Next is the SVG object. The filter is defined inside the defs element. Its id is the one we used to reference it from the css. Here I’ve applied 4 filter primitives. I hope each is mostly self explanatory.

A couple of things to note. is used for both hue-rotate and saturate, each of which were unique named filters. isn’t limited to hue and saturation. It also takes a luminanceToAlpha value and you can apply a matrix to it directly

Also note , which give us several blend modes as filters, something I’m sure you’re used to working with in Photoshop or whatever graphic editor you use. We don’t get every blend mode, but we do get normal, multiply, screen, darken, and lighten.

This all leads to the filtered image below.

Image with filter primitives applied

The above worked perfectly in Firefox. I had no luck getting it to work in either Chrome Canary or Safari, which I didn’t expect. I was, however, able to make the example work, by using similar code all in the original index.html document.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<svg>
  <image x="0" y="0" width="800" height="600" xlink:href="images/strawberry-fields.jpg" filter="url(#filter-primitives)"></image>
</svg>

<svg>
 <defs>
   <filter id="filter-primitives">
     <feGaussianBlur stdDeviation="0" />
     <feColorMatrix type="hueRotate" values="270"/>
     <feColorMatrix type="saturate" values="0.75"/>
     <feBlend mode="multiply"/>
   </filter>
 </defs>
</svg&gt

Instead of linking to an external document via css, everything here is located in the same document. The filter itself is the same. The difference is that the image is displayed as an SVG image instead of a regular html image.

Image with filters applied, but clipped in Firefox

Interestingly this works, kind of, in Firefox. The filter is applied, but for whatever reason Firefox would only display the image inside a clipped area as you can see to the left.

The image is rendered full size, and the containing div is the full size, but the image is clipped at 300px by 150px. I haven’t been able to figure out why.

If someone knows please share.

More Examples

Here are a few other examples I came across while trying to figure all this out.

You can see even from the titles of these examples they cross into SVG filters and while css and SVG are using the same filter primitives there’s a slightly different way of working with them as far as I could tell and different browser support for the different ways.

List of Filter Primitives

There are more filter primitives than the ones I used above. Many will look familiar to the named functions we saw last week or at least be familiar enough to understand what they do. Quite a few are more complex and I haven’t had a chance to explore them.

Blend and Merge Effects

  • feBlend
  • feComposite
  • feMerge

Color Effects

  • feColorMatrix
  • feComponentTransfer
  • feFlood (flood-color and flood-opacity)

Light and Light Source Effects

  • feDiffuseLighting
  • feSpecularLighting
  • feDistantLight
  • fePointLight
  • feSpotLight

Other Special Effects

  • feConvolveMatrix
  • feDisplacementMap
  • feTurbulence
  • feGaussianBlur
  • feImage
  • feMorphology
  • feTile
  • feOffset
  • feDropShadow

Custom Effects

  • feCustom

This is clearly a larger list than the list of named functions. Perhaps all or more of these primitives will eventually find their way into the named functions list as I think those are much easier to work with.

Summary

Again my apologies for any confusion I might be creating. The spec isn’t all that clear to me, mainly due to a lack of examples and most of the information I found outside of the spec seems to be more about svg filters. The two are clearly related, but they aren’t exactly the same.

If we stick to the filter effects spec, filter primitives are SVG markup stored in a separate file accessed through filter: url(); They can be applied to any image or other element in your html. SVG filters on the other hand are stored in the same file as the element being filtered and they only apply to SVG elements.

While I struggled to make them work, when they did, it was easy to see how powerful they can be. Hopefully in the not too distant future, I’ll gain a better understanding and be able to clear up any confusion I created today.

Download a free sample from my book, Design Fundamentals.

3 comments

Leave a Reply

Your email address will not be published.

css.php