Reverse Knockout Text Mask Effect with SVG

A simple approach for knocking-out portions of an image with text.

A recent project here at Viget called for a hero image background, with a state outline and text overlay, producing a knockout effect (shown below). The solid shape of this graphic combined with text make it a prime candidate for SVG! Here’s how it works.

First, the basic shapes

Fortunately, this graphic only requires a text element and two path elements for the state outline (path data omitted for brevity).

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" width="1000" height="400" preserveAspectRatio="xMidYMid meet">
  <g>
    <path fill="#ee572f" d="..."></path>
    <path fill="#ee572f" d="..."></path>
  </g>
  <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#ffffff">
    Voices of Alabama
  </text>
</svg>

Second, the Mask

When SVG <mask> elements are applied to another shape, they hide by default, the objects that they are applied to. We need to essentially reverse this to show by default, then choose which areas to hide. We’ll use a simple <rect> shape with a white fill to show the entire area, and a <text> element with a black fill to hide, or knock-out that area.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" width="1000" height="400" preserveAspectRatio="xMidYMid meet">
  <defs>
    <mask id="text-mask">
      <rect width="100%" height="100%" fill="#ffffff"></rect>
      <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#000000">
        Voices of Alabama
      </text>
    </mask>
  </defs>
  <g style="mask: url('#text-mask')">
    <path fill="#ee572f" d="..."></path>
    <path fill="#ee572f" d="..."></path>
  </g>
  <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#ffffff">
    Voices of Alabama
  </text>
</svg>

In the image below, but you can see along the left and right sides, where the black text has hidden parts of the shape (with the original text element above hidden):

Finally, the Knockout

We need to extend the bounds of the text mask, so that when we add the visible text element back, we can see both the text and the knockout effect. There are two ways to achieve this.

SVG Stroke

Normally, a stroke would not work for a “glow” effect since the stroke draws on both the inside and outside of a shape. However, since the text element within the mask is not actually visible, it doesn’t matter where the stroke sits.

Text mask with stroke applied:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" width="1000" height="400" preserveAspectRatio="xMidYMid meet">
  <defs>
    <mask id="text-mask">
      <rect width="100%" height="100%" fill="#ffffff"></rect>
      <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#000000"
        stroke="#000000" stroke-width="40" stroke-linejoin="round">
        Voices of Alabama
      </text>
    </mask>
  </defs>
  <g style="mask: url('#text-mask')">
    <path fill="#ee572f" d="..."></path>
    <path fill="#ee572f" d="..."></path>
  </g>
  <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#ffffff">
    Voices of Alabama
  </text>
</svg>

With the visible text element back in:

feMorphology Filter

Another option is to use the feMorphology filter primitive instead of a stroke. The feMorphology filter works to apply a morphing effect to an object, modifying its shape. In the case of text strokes, feMorphology is a way that enables us to add an outline without affecting the size or thickness of the original text in the process. Codrops has a great article on the feMorphology filter that digs into this approach. Essentially, the feMorphology filter is set to use the dilate operator with a radius, and gets applied to the text within the mask.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" width="1000" height="400" preserveAspectRatio="xMidYMid meet">
  <defs>
    <mask id="text-mask">
      <rect width="100%" height="100%" fill="#ffffff"></rect>
      <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#000000"
        filter="url(#dilated-text)">
        Voices of Alabama
      </text>
    </mask>
  </defs>
  <filter id="dilated-text">
    <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="20"></feMorphology>
  </filter>
  <g style="mask: url('#text-mask')">
    <path fill="#ee572f" d="..."></path>
    <path fill="#ee572f" d="..."></path>
  </g>
  <text transform="translate(500, 200)" text-anchor="middle" font-size="100" font-family="Georgia" font-style="italic" fill="#ffffff">
    Voices of Alabama
  </text>
</svg>

It’s important to note that while this approach looks nearly identical, it does not work in Firefox at the time of this writing.

Accommodating Long Titles

For cases where the graphic needs to display multiple lines of text, we have to set text in a slightly different way, since text elements in SVG do not wrap the same way they do in HTML. With SVG, your text element must contain <tspan> elements for each row of text.

<text font-size="72" font-family="Georgia" font-style="italic" fill="#ffffff">
  <tspan x="0">
    <tspan x="0">Ben Moore Hotel &amp; Malden</tspan>
    <tspan x="0" dy="72">Brothers Barber Shop</tspan>
  </tspan>
</text>

The Finished Product

Summary

SVG masks are great for showing an image in a non-square format, but they are just as good, if not better, for knocking-out portions of an image with text. Reversing the colors in the mask and adding a simple stroke is all it takes! If you’ve seen this knock-out effect in the wild, let me know in the comments below.

Jeremy Frank

Jeremy focuses on responsive buildout, CMS integration, and compelling animation for clients including the University of Pennsylvania, POLITICO, and The Nature Conservancy. He maintains a healthy balance of innovation and responsibility from our Durham, NC, office.

More articles by Jeremy