Styling Text with SVG Filters

Published

Note: This article was originally published on the Code School Blog.

If you look at a US dollar bill up close, there are a lot of typographic niceties going on: the huge numbers in the corners, the beefy capitals across the top and bottom, and even the vibrant serial numbers on each half of the bill. But my absolute favorite part is the layered style used for “The United States of America.”

Thanks to SVG, we can build a reusable filter to apply the same style to any text we want.

Recreating the effect

Let’s get things started by adding a new SVG element to the page and centering the word STATES inside.

SVG
<svg width='600' height='200' >
<text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle'>
STATES
</text>
</svg>

Here the combined x, y, dominant-baseline, and text-anchor attributes allow us to center the text horizontally and vertically within the SVG. That means no more guessing x and y values — 50% will always work.

Using CSS, we’ll specify a background, fill, and font-size. We want a typeface similar to the one on the dollar bill, so we’ll pick Playfair Display from Google Fonts.

The syntax

An SVG’s filters are defined in its markup like any other SVG elements. You can create an SVG filter with the filter tag and reference its id in CSS.

SVG
<filter id='money'>
<!-- Filter effects go here. -->
</filter>
CSS
text {
filter: url('#money');
}

If there’s nothing in a filter (like in the example above), whatever you apply it to will disappear entirely. That’s the normal behavior, so no worries. Let’s fix that by adding in a few filter effects.

Expanding the text

SVG
The first thing we want to do is expand the text a small amount in all directions. The filter effect to do that is feMorphology, and here’s what that markup looks like:

There’s a lot going on in that short snippet, so let’s break things down piece by piece.

So once we update the filter’s markup, this is what we end up with:

Looks bolder, right? That’s exactly what we want.

Adding a shadow

The next thing we want to add is a solid shadow — the kind that’s a single color and stretches out in a direction. I thought SVG filters would give us an easy way to make a solid shadow, but it’s surprisingly difficult to do. Here’s the approach I came up with:

Here’s what the markup for that looks like:

SVG
<feOffset in='expand' dx='1' dy='1' result='shadow_1'/>
<!-- Repeat for 2 through 11... -->
<feMerge result='shadow'>
<feMergeNode in='expand'/>
<feMergeNode in='shadow_1'/>
<!-- Repeat for 2 through 11... -->
</feMerge>

A quick detour

Is anyone else tired of staring at the same colors? Let’s try something a little different and learn about feFlood and feComposite in the process.

Here’s what each one does:

To change the solid shadow’s color, we’ll write markup like this:

SVG
<feFlood flood-color='#0e483b'/>
<feComposite in2='shadow' operator='in' result='shadow'/>
<feMerge>
<feMergeNode in='shadow'/>
<feMergeNode in='SourceGraphic'/>
</feMerge>

feComposite uses the in2 attribute for its second input, but we skipped writing in for the first. The same goes for feFlood — we skipped writing result. That’s because if you have a filter effect right after another, the result from one becomes the implied input for the next.

Okay, the detour’s over — let’s get back to work.

Adding a border

Like in the example above, we’ll use feFlood and feComposite to give a color to the SVG text’s shadow. Here, we’re using the same color as the background for a see-through effect.

SVG
<feFlood flood-color='#ebe7e0'/>
<feComposite in2='shadow' operator='in' result='shadow'/>

We can use feMorphology again here to create a border around the shadow.

SVG
<feMorphology operator='dilate' radius='1' result='border'/>
<feFlood flood-color='#35322a'/>
<feComposite in2='border' operator='in' result='border'/>

Adding a second shadow

Using the same feOffset technique we used earlier, we’ll take the new border layer and create a second shadow with it.

SVG
<feOffset dx='1' dy='1' in='border' result='secondShadow_1'/>
<!-- Repeat for 2 through 11... -->
<feMerge result='secondShadow'>
<feMergeNode in='border'/>
<feMergeNode in='secondShadow_1'/>
<!-- Repeat for 2 through 11... -->
</feMerge>

Browser support for tiled images in SVG can be unreliable, so I saved out a pre-tiled stripes.svg at 600 by 200 pixels. We’ll use feImage to load it into the SVG filter, then use feComposite to mask it beneath the second shadow.

SVG
<feImage x='0' y='0' width='600' height='200' xlink:href='stripes.svg'/>
<feComposite in2='secondShadow' operator='in' result='secondShadow'/>

Browser Support

It’s hard to predict how browsers will support certain SVG filters, especially when combining them to create complex effects. The approach in this article works in Chrome, Firefox, Safari, and Opera, but the second shadow ends up partially clipped in Internet Explorer and Edge. For the most part, SVG filters have great support across browsers, but I’d still recommend testing things out early on in development. You might have to compromise here and there to get things working.