SVG: Conclusions (for now)

This series has barely begun to scratch the surface of what you can do with SVG. As much SVG as we’ve covered, We’ve barely scratched the surface.

There are things like the GSAP library that I deliberately did not cover because I wrote about it before and because I don’t want to push yet another third party library into my codebase.

The list below shows links to tutorials and examples of things that I’ve either used as inspiration or would like to play with at a later time.

If you have comments, criticisms or ideas for further projects, feel free to contact me on twitter at elrond25.

I look forward to hearing from you 🙂

Links and resources

SVG Animation

Perhaps the most interesting and complex topic in SVG, for me, is animations. I have a basic understanding on how to create keyframe animations for CSS content and a very basic understanding of how to use GSAP to animate HTML elements. I want to explore how to create smaller targeted animations using both CSS and SVG specific animations techniques.

SMIL and the animate element

SVG comes with its own animation tools in the form of the animate element and SMIL.

The most basic animation element is animate. It takes the following attributes:

  • attributeName: The name of the attribute we want to animate
  • attributeType: One of CSS, XML or auto
    • CSS specifies that the value of attributeName is the name of a CSS property defined as animatable
    • XML represents an XML attribute defined as animatable in the SVG namespace
    • auto tells the browser (in this case) to search through the list of CSS properties first for a matching property name, and if it doesn’t find one, then search the default XML namespace for the element
  • from is the initial value of the property we want to animate
  • to is the final value of the property we want to animate
  • dur tells the browser how long we want the animation to last
  • repeatCount tells the user agent how many times we want to run the animation. It can be a positive integer or the keyword indefinite

The example below will animate the movement of the rectangle on the x axis from left to right, lasting 10 seconds and repeating the animation from the beginning 10 times.

<svg width="100" height="100" viewBox="0 0 100 100" version="1.1"
     xmlns="http://www.w3.org/2000/svg">

  <rect x="10" y="10"
        width="90" height="90"
        fill="purple" >
    <animate attributeType="XML"
             attributeName="x"
             from="100" to="-100"
             dur="10s"
             repeatCount="10"/>
  </rect>
</svg>

The second example builds on the first one. It adds additional animate elements to animate different properties of the same target element. The second animate element will move the rectangle on the y axis, and the third animate element will animate the color from purple to blue.

If we wanted to we could give these properties their own duration or repeatCount values but, for this example,

<svg width="100" height="100" viewBox="0 0 100 100" version="1.1"
     xmlns="http://www.w3.org/2000/svg">

  <rect x="10" y="10"
        width="90" height="90"
        fill="purple" >
    <animate attributeType="XML"
             attributeName="x" from="100" to="-100"
             dur="10s"
             repeatCount="10"/>
    <animate attributeType="XML"
             attributeName="y" from="100" to="-100"
             dur="10s"
             repeatCount="10"/>
    <animate attributeType="XML"
             attributeName="fill" from="purple" to="blue"
             dur="10s"
             repeatCount="10" />
  </rect>
</svg>

animateMotion

The second element is animateMotion and it animates the target element along a pre-defined path.

  • calcMode indicates what interpolation mode to use for animation. It takes one of the following values:
    • discrete: The animation function will jump from one value to the next without any interpolation
    • linear: Simple linear interpolation between values is used to calculate the animation function
    • paced: Defines interpolation to produce an even pace of change across the animation. This is only supported for values that define a linear numeric range, and for which some notion of “distance” between points can be calculated (e.g. position, width, height, etc.). For animateMotion, this is the default value
    • spline: Interpolates from one value in the values list to the next according to a time function defined by a cubic Bézier spline. The points of the spline are defined in the keyTimes attribute, and the control points for each interval are defined in the keySplines attribute
  • path represents the path the animation will follow
  • keyPoints takes a semicolon-separated list of floating point values between 0 and 1 and indicates how far along the motion path the object shall move at the moment in time specified by corresponding ‘keyTimes’ value. Each value in the list corresponds to a value in the keyTimes attribute list. The number of keyPoint values must exactly match the values in the ‘keyPoints’ list
  • rotate controls the positioning of the animated object. It can take one of the following values:
    • auto: The object is rotated over time by the angle of the direction (i.e., directional tangent vector) of the motion path
    • auto-reverse The object is rotated over time by the angle of the direction (i.e., directional tangent vector) of the motion path plus 180 degrees.
    • number The target element has a constant rotation transformation applied to it, where the rotation angle is the specified number of degrees.
    • The default value is 0.
  • origin is defined in the SMIL Animation Specification and has no effect on SVG

The mpath sub-element provides the ability to reference an external path element as the definition of a motion path.

  1. The example creates an ellipse to be used as the path our object will navigate.
  2. Then we create three circles and place them on the path, one at the beginning, one on the middle and one on the end.
  3. Finally, we create a triangle and animate it along the path we defined in step 1
<svg  width="5cm" height="3cm"
      viewBox="0 0 500 300"
      xmlns="http://www.w3.org/2000/svg">

  <path id="path1"
        d="M100,250 C 100,50 400,50 400,250"
        fill="none"
        stroke="blue"
        stroke-width="7.06"  />
  <circle cx="100" cy="250" r="12.64" fill="blue"  />
  <circle cx="250" cy="100" r="12.64" fill="blue"  />
  <circle cx="400" cy="250" r="12.64" fill="blue"  />

  <path id="triangle"
        d="M-25,-12.5 L25,-12.5 L 0,-87.5 z"
        fill="yellow"
        stroke="red"
        stroke-width="7.06"  >
    <!-- Define the motion path animation -->
    <animateMotion  dur="6s"
                    repeatCount="10"
                    rotate="auto" >
       <mpath href="#path1"/>
    </animateMotion>
  </path>
</svg>

animateTransform

The animateTransform element control transform-based animations and does a similar job to the CSS transform. Because it handles different types of transformations there is no single set of parameters to discuss here. I’ll refer you to the element’s description in the SVG Animations specification.

<svg width="400" height="600" viewbox="0 0 100 100">
  <rect id="Rectangle1" fill="#3C81C1" x="0" y="0" width="100" height="125">
    <animateTransform   attributeName="transform"
                        type="translate"
                        from="0 0"
                        to="150 20"
                        begin="0s"
                        dur="2s"
                        repeatCount="indefinite"
    />
  </rect>
</svg>

Multiple animations

One important detail is how to run multiple animateTransform elements for the same target.

We need to wrap all the SVG children in a g element and then, like in the example, define one inside the target element and one as the last child of the g grouping element.

<svg width="600" height="400" viewbox="0 0 600 400">
  <g>
    <rect id="Rectangle1"
          fill="#3C81C1"
          x="0" y="0"
          width="100" height="125">
        <animateTransform   attributeName="transform"
                            type="scale"
                            from="1 1"
                            to="3 1.25"
                            begin="0s"
                            dur="2s"
                            repeatCount="5"
        />
    </rect>
    <animateTransform   attributeName="transform"
                       type="translate"
                       from="0 0"
                       to="150 20"
                       begin="0s"
                       dur="2s"
                       repeatCount="5" />
  </g>
</svg>

Set

The ‘set’ element provides a simple means of just setting the value of an attribute for a specified duration. It supports all attribute types, including those that cannot reasonably be interpolated, such as string and boolean values.

In the example, we set the stroke-width attribute, which takes a string to be 5px starting 5 seconds into the animation.

<svg width="100" height="100" viewBox="0 0 100 100" version="1.1"
     xmlns="http://www.w3.org/2000/svg">

  <rect x="10" y="10"
        width="90" height="90"
        fill="blue"
        stroke="red">
    <animate attributeType="XML"
             attributeName="x"
             from="100" to="-100"
             dur="10s"
             repeatCount="10"/>
    <set  attributeName="stroke-width" to="10px"
          begin="5s"
          dur="10s"/>
  </rect>
</svg>

Discard

The ‘discard’ element allows authors to specify the time at which particular elements are to be discarded, thereby reducing the resources required by an SVG user agent. This is particularly useful to help SVG viewers conserve memory while displaying long-running documents. This element will not be processed by static SVG viewers.

The ‘discard’ element may occur wherever the ‘animate’ element may.

<svg xmlns="http://www.w3.org/2000/svg" width="352" height="240" playbackorder="forwardonly">

  <ellipse cx="98.5" cy="17.5"
           rx="20.5" ry="17.5"
           fill="blue" stroke="black"
           transform="translate(9 252) translate(3 -296)">
    <animateTransform attributeName="transform"
                      begin="0s" dur="2s"
                      fill="remove"
                      calcMode="linear"
                      type="translate"
                      additive="sum"
                      from="0 0" to="-18 305"/>
    <discard begin="2s"/>
  </ellipse>

  <rect x="182" y="-39" width="39" height="30"
        fill="red" stroke="black"
        transform="translate(30 301)">
    <animateTransform   attributeName="transform"
                        begin="1s" dur="2s"
                        fill="remove"
                        calcMode="linear"
                        type="translate"
                        additive="sum"
                        from="0 0" to="-26 -304"/>
    <discard begin="3s"/>
  </rect>

  <polygon points="-66,83.5814 -43,123.419 -89,123.419"
           fill="green" stroke="black"
           transform="matrix(1 0 0 1.1798 0 -18.6096)">
    <animateTransform attributeName="transform"
                      begin="2s" dur="2s"
                      fill="remove" calcMode="linear" type="translate" additive="sum"
                      from="0 0" to="460 63.5699"/>
    <discard begin="4s"/>
  </polygon>
</svg>

The downside is that SMIL, the technology behind SVG native animations is not in IE/Edge and never will be; it has been deprecated by Blink (but the deprecation was withdrawn and will not happen… for now). Check caniuse.com for SMIL support and how it evolves over time.

There are other options to animate SVG. We’ll explore them

CSS Animations

Interestingly, most of what you can do natively in SVG you can do with CSS transitions and transformations. We’ll work with CSS inside the SVG element but it could easily be done with an external style sheet that integrates all the styles for the document.

The example shows how using the style element inside an inline SVG we can control SVG animations the same way we control them in CSS.

In this example, we define the styles for the .rotator element and the keyframes we need for the animation to work.

The rectangle in the SVG matches the class in the CSS so it gets animated and rotates.

<svg  xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="https://www.w3.org/1999/xlink" 
      width="600" height="450" 
      viewbox="0 0 100 100">

  <style>
    .rotator {
      fill: deeppink;
      animation: hideshow 5s ease-in 10;
      transform-origin: center;
    }

    @keyframes hideshow {
       50% { fill: purple;}
        to { 
          transform: rotate(360deg);
          fill: blue;
      }
    } 
  </style>


  <rect class="rotator" 
        x="50" y="50" 
        width="25" height="25"></rect>
</svg>

Web Animations API: One API to rule them all

The last animation technique I want to discuss is the Web Animations API; an attempt to unify all the animations APIs available in CSS and SVG. Yes, it means adding a script to the page and, potentially blocking render if we’re not careful but I think it’s the best way to use animations short of going the GSAP route.

I wrote about Web Animations API in 2016 but it wasn’t until I started looking at SVG that I came back to the API as a one-stop way to animate content.

This example is broken into two sections. The first one is the SVG rectangle where we assign an ID to the element we want to rotate so we can grab it later from the script.

<svg  xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="https://www.w3.org/1999/xlink" 
      width="600" height="450" 
      viewbox="0 0 100 100">
  <rect id="rotator" 
        x="50" y="50" 
        fill="deeppink"
        width="50"
        height="50"></rect>
</svg>

This is the first, and only, example that will use Javascript. Web Animation APIs main method is animate; it takes two parameters, an array of objects containing the different animations to run and another object containing the settings for the animation.

const player = document.getElementById('toAnimate').animate([
    { transform: 'scale(1)', opacity: 1, offset: 0 },
    { transform: 'scale(.5)', opacity: .5, offset: .3 },
    { transform: 'scale(.667)', opacity: .667 },
    { transform: 'scale(.6)', opacity: .6 }
  ], {
    // duration in milliseconds
    duration: 10000, 
    //'linear', a bezier curve, etc.
    easing: 'ease-in-out', 
    // delay in milliseconds
    delay: 10, 
    // infinity or a positive integer number
    iterations: Infinity, 
    //'normal', 'reverse', etc.
    direction: 'alternate', 
    //'backwards', 'both', 'none', 'auto'
    fill: 'forwards' 
  });

Yeah, much more verbose, but it’ll work with both CSS and SVG and it doesn’t require a third party library.

SVG Filters

SVG filters allow for non-destructive image manipulation. They are a superset of the filters available directly in CSS

The basic container for the filters is the filter element. To use them you add the filter attribute to the image element containing the image you want to filter.

A barebones svg for filtering an image looks like this:

<svg width="800" height="600" viewBox="0 0 800 600">
    <filter id="myFilter">
        <!-- filter effects go in here -->
    </filter>
    <image xlink:href="..."
           width="100%" height="100%" x="0" y="0"
           filter="url(#myFilter)"></image>
</svg>

In the following sections, we’ll talk about filter components and other things we need to understand before making them work.

Filter Primitives

In SVG filters, each filter element contains one or more filter primitives as its children. Each primitive performs a graphical operation on one or more inputs, producing a result that can be used as an input for subsequent elements on the chain. They all share the same prefix: fe, which is short for “filter effect” and use a name that references its purpose (for example feGausianBlur for the primitive that creates a Gaussian blur).

Filter primitives take a source graphic as input and outputting another one as the result. You chain the output of one effect as the input of another one; this flexibility gives you an almost endless combination of filter primitives producing an endless number of effects

Each primitive can take one or two inputs and output only one result. The inputs of a filter primitive are defined in attributes called in and in2. The result of an operation is defined in the result attribute. If you don’t specify the result of a primitive, its result will automatically be used as input to the primitive that follows.

A filter primitive also accepts other types of inputs, the most important of which are:

  • SourceGraphic: the actual element that we’re applying the filter to, for example, an image or a piece of text
  • SourceAlpha: the alpha channel for the element
<svg width="600" height="400" viewBox="0 0 600 400">
  <filter id="myFilter">

    <feFlood flood-color="navy" flood-opacity=".15" result="flood"></feFlood>

    <feMerge>
      <feMergeNode in="flood" />
      <feMergeNode in="SourceGraphic" />
    </feMerge>

  </filter>
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/32795/octotron.jpg"
         width="100%" height="100%" x="0" y="0"
         filter="url(#myFilter)"></image>
</svg>

There are 17 filter primitives available in the Filter Effects Module Level 1.

The Filter Region

In SVG, elements have “regions” whose boundaries are defined by the borders of the element’s Bounding Box. The Bounding Box (also abbreviated “bbox“) is the smallest fitting rectangle around an element.

If you apply a filter effect to an image or a piece of text, the effect will be restricted to its bounding box, and any filter result that lies beyond the boundaries of it will be clipped off. This may not be very useful because many filters will impact pixels outside the boundaries of the bounding box and, by default, those pixels will end up being cut off.

So how do we prevent that from happening? By extending the filter region. We can extend the region the filter is applied to by modifying the x, y, width and height attributes on the filter element.

By default, filters have regions extending 10% the width and height of the bounding box in all four directions. When working with filters we might want to create it using the following syntax:

<filter x="-10%" y="-10%" width="120%" height="120%"
        filterUnits="objectBoundingBox">
    <!-- filter operations here -->
</filter>

If you omit these attributes on the filter element, these values will be used by default. You can also override them to extend or shrink the region as you need.

Keep in mind that the units used in the x, y, width and height attributes are dependent on which filterUnits value is in use.

  • objectBoundingBox (default units): When the filterUnits is objectBoundingBox, the values of the x, y, width and height attributes are percentages or fractions of the size of the element’s bounding box
  • userSpaceOnUse: when filterUnits is set to userSpaceOnUse the coordinates of the x, y, width and height attributes are set relative to the current coordinate system used in the SVG.

See Sara Soueidan’s Understanding SVG Coordinate Systems and Transformations (Part 1) for a good introduction and objectBoundingBox versus userSpaceOnUse for an in-depth discussion on ObjectBoundingBox and userSpaceOnUse that is applicable to gradients, patterns, filters, masks, and clipping paths.

SVG Text

In this post, we’ll look at basic text and elements that will make interacting with the text easier. We’ll also look at how to use Web Fonts with SVG. This is not a replacement for HTML, but an alternative for doing things that are not possible, or not easily possible with HTML and CSS alone.

Text

The text element allows you to write text as part of your SVG content. It interacts well with CSS, either written as a style inside the SVG element or in an external stylesheet.

The example below uses an internal stylesheet to define three classes to control a different aspect of the text.

Next, we define a group of text elements and give it an ID of catMessage. Each text block has a class matching the ones we defined in CSS.

Lastly, we use the catMessage group. Because we associated each text block to a CSS class we don’t need to do any further styling.

<svg viewBox="0 0 240 80"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink"https://www.w3.org/1999/xlink">
  <style>
    .small {
      font: italic 0.75em sans-serif; fill: blue
    }
    .heavy {
      font: bold 2em sans-serif; fill: rebeccapurple
    }
    .Rrrrr {
      font: italic 3em serif; fill: red;
    }
  </style>

  <defs>
    <g id="catMessage">
      <text x="20" y="35" class="small">My</text>
      <text x="40" y="35" class="heavy">cat</text>
      <text x="55" y="55" class="small">is</text>
      <text x="65" y="60" class="Rrrrr">Grumpy!</text>
    </g>
  </defs>

  <use xlink:href="#catMessage"/>
</svg>

The example adds class to tell the parser what CSS class we want to use for that particular element.

text

The text element renders text in the SVG document. This is the only way to render text in SVG. If there is any text in the SVG document that is outside a text element, it won’t render.

x and y are the coordinates where, in the viewport, we want to place the text block.

tspan

tspan allows for changes in only some parts of the text element without changing the complete line of text. It is similar to the HTML span element

<svg viewBox="0 0 240 80"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink"https://www.w3.org/1999/xlink">
  <style>
    .small {font: italic 0.75em sans-serif; fill: blue}
    .heavy {font: bold 2em sans-serif; fill: rebeccapurple}
    .Rrrrr {font: italic 3em serif; fill: red;}
  </style>

  <defs>
    <g id="catMessage">
      <text x="20" y="35" class="small">My</text>
      <text x="40" y="35" class="heavy">cat</text>
      <text x="55" y="55" class="small">is</text>
      <text x="65" y="60" class="Rrrrr">Gru<tspan style="font-size: 150%;fill:navy;">m</tspan>py!</text>
    </g>
  </defs>

  <use xlink:href="#catMessage"/>
</svg>

Loading fonts via CSS

Rather than depending on the system fonts, we can load fonts via CSS’ font-face attribute just like we do for our regular web pages.

The first version of this example uses Marvin Visions as the font, loaded with @font-face.

This example uses a single text element for the content of the text.

The CSS loads the font and sets formatting attributes for the text. The attributes set are:

  • Font family
  • Font size
  • The color of the inside of the font (the fill)
  • Text shadow
<svg viewBox="0 0 240 360"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink"https://www.w3.org/1999/xlink">
  <style>
    @font-face {
      font-family: "Marvin";
      src: url("MarvinVisionsBig-Bold.woff") format("woff");
    }

    text {
      font-family: Marvin;
      font-size: 3em;
      fill: #e65f1d;
      text-shadow: 12px 12px 6px rgba(0, 0, 0, 1);
    }
  </style>

  <defs>
    <g id="title">
      <text x="10" y="40">Nightfall</text>
    </g>
  </defs>

  <use xlink:href="#title"/>
</svg>

A logical, and cumbersome, next step would be to add tspan elements surrounding each letter so we can style them individually, similar to an exercise in the CSS: Advanced Typographic Techniques course from LinkedIn Learning.

The idea is that by assigning a tspan with an ID attribute to each letter we can style them differently from each other and it works, sort of.

Because we use a single text element as the container we must make changes to all the tspan children that need to move. In the example below, I moved the word to fall into its own line by changing the coordinates of the first letter; all the following elements moved along.

If we want to play with x and y coordinates we need to keep in mind that if we make changes to one we must make changes to all the subsequent ones. In the example below, we use this trick to move the second word to its own line by changing the coordinates for the first letter in that word.

Because we’ve assigned id attributes to each letter we can style them individually outside the coordinates that we must set in the individual SVG spans.

<svg viewBox="0 0 240 360"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink"https://www.w3.org/1999/xlink">
  <style>
    /* Load the font */
    @font-face {
      font-family: "Marvin";
      src: url("path/to/MarvinVisionsBig-Bold.woff") format("woff");
    }

    text {
      font-family: Marvin;
      font-size: 3em;
      fill: #e65f1d;
      text-shadow: 12px 12px 6px rgba(0, 0, 0, 1);
    }

    /* Individual letters styles */
    #l6 {fill: rebeccapurple;}
    #l7 {fill: navy;}
    #l8 {fill: red;}
    #l9 {fill: limegreen;}
  </style>

  <defs>
  <!--
    tspan elements broken for readability.

    They should all be in a single line or browsers will
    insert weird spaces between the characters
  -->
    <g id="title">
      <text x="10" y="40"><tspan id="l1">N</tspan>
      <tspan id="l2">i</tspan>
      <tspan id="l3">g</tspan>
      <tspan id="l4">h<tspan>
      <tspan id="l5">t</tspan>
      <tspan id="l6" x="10" y="80">f</tspan>
      <tspan id="l7">a</tspan>
      <tspan id="l8">l</tspan>
      <tspan id="l9">l</tspan></text>
    </g>
  </defs>

  <use xlink:href="#title"/>
</svg>

Rotating text

CSS allows you to rotate text in different writing modes but it’s not supported in all browsers. SVG provides an alternative way to rotate and display the text that will work in browsers that support SVG.

We apply the same attribute/value combination that we use in CSS: writing-mode: vertical-lr. There is an SVG specific attribute writing-mode="tb" that we can use, but support is not uniform across browser.

<svg viewBox="0 0 240 720"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink"https://www.w3.org/1999/xlink">
  <style>
    @font-face {
      font-family: "Marvin";
      src: url("MarvinVisionsBig-Bold.woff") format("woff");
    }

    text {
      font-family: Marvin;
      font-size: 3em;
      fill: #e65f1d;
      text-shadow: 12px 12px 6px rgba(0, 0, 0, 1);
      writing-mode: vertical-lr;
    }
  </style>

  <defs>
    <g id="title">
      <text x="40" y="10">Nightfall</text>
    </g>
  </defs>

  <use xlink:href="#title"/>
</svg>

Text Path

textPath places text the shape of a path element defined alongside it.

For textPath to work, the element we use to wrap our content around must be a path. In earlier versions of this example I tried to use a circle but it doesn’t work. Manually creating a circle with SVG is not easy, so I cheated. I created the circle in Adobe Illustrator and then exported it as SVG and copied it to Codepen.

textLength (essentially the circumference of the circle) is used as an alternative to letter-spacing for Firefox, which currently doesn’t support letter-spacing for SVG

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
      viewBox="0 0 600 600">

  <defs>
    <path d="M276.14,178C395,178,491.29,259.54,491.29,360.12S395,542.24,276.14,542.24,61,460.7,61,360.12,157.32,178,276.14,178Z" id="textcircle"/>
  </defs>

  <text textLength="1200">
    <textPath xlink:href="#textcircle">
      The fellowship of the ring
    </textPath>
  </text>
</svg>

The CSS portion of this example is simple. It defines the dimensions of the svg element and font attributes.

In most production cases I would never import the font directly into the CSS file but since this is a demo I’m making an exception. For production, I would likely host the font on the same server and cache it using a service worker.

@import url('https://fonts.googleapis.com/css?family=Fredoka+One');

text {
  font-size: 38px;
    font-family: 'Fredoka One', cursive;
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 24px;
    fill: rebeccapurple;
  opacity: 0.8;
}

SVG Masks and Clip Paths

One of the fun things to explore is how we can mask and clip SVG elements and how we can do “text on a path” effects. As we explore these new elements we’ll visit additional elements that make part of the mask and path functionality and how CSS interacts with SVG (TL, DR: It doesn’t use the same elements).

Masks

mask Defines an alpha mask for compositing the current object into the background. It will ‘bleed’ the background into the foreground element

The pattern element defines a graphics object which you can tile (repeat at x and y coordinate intervals) to cover an area. This is similar to what we can do with blend modes in CSS

The patternUnits attribute defines the coordinate system in use for x, y, width and height properties of the element.

The image SVG element includes images inside SVG documents. It can display raster image files (JPG or PNG) or other SVG files. The behavior for animated GIFs is undefined.

The SVG text element defines a graphics element consisting of text. It’s possible to apply a gradient, pattern, clipping path, mask, or filter to text, just like any other SVG graphics element.

If you include text within SVG outside a text element, it is not rendered. This is different from being hidden by default, as setting the display property will not show the text.

The SVG portion of the example includes the image pattern and the text that we want to use as the mask.

<svg>
  <defs>
    <pattern  id="wood"
              patternUnits="userSpaceOnUse"
              width="400" height="400">
    <image
            xlink:href="http://subtlepatterns.com/patterns/purty_wood.png"
            width="400" height="400" />
    </pattern>
  </defs>
  <!-- 
    The text below will have the background image of the pattern
  -->
  <text y="1.2em">SVG rocks!</text>
</svg>

The CSS sets the dimensions of the SVG element and the text fill, the color inside the element, to use the wood pattern we’ve defined in the SVG.

When we use CSS to style SVG elements we need to be aware that SVG doesn’t use the same selectors, attributes, and values than regular CSS.

svg {
  width: 8em;
  height: 2em;
  font-weight: 900;
  font-size: 5em;
  line-height: 1.2;
  font-family: 'Arial Black', sans-serif;
}

text {
  fill: url(#wood);
}

Clip Path

clipPath defines a clipping path.

The idea is that whatever changes we make to the image will not go larger than the clipping path. In this example, we create to elements inside the SVG a circle and a path with the shape of a heart.

We use the clipping path with the clip-path property.

<svg viewBox="0 0 100 100">
  <clipPath id="myClip">
    <circle cx="40" cy="35" r="35" />
  </clipPath>
 
  <path id="heart" d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" />
 
  <use clip-path="url(#myClip)" xlink:href="#heart" fill="red" />
</svg>

The CSS handles the animation using keyframes. We’re animating the r attribute from CSS. We want the circle to grow into the full size of the clipping element and ignore anything falling outside.

Unfortunately, this example doesn’t work in Firefox (tested with 67 nightly in a Mac) because it doesn’t support geometry properties in CSS. Bug 1383650 is tracking the issue but it doesn’t appear to be a high priority.

The workaround is to remove the clip-path="url(#myClip)" from the use element . We lose the animation but at least we get the color we want to display the clipped element in.

I’m also researching if it’s possible to do this with JavaScript or with CSS @support techniques.

html,body,svg {
  height:100%;
}

@keyframes animateHeart {
  from {
    r: 0
  }

  to {
    r: 60px
  }
}

#myClip circle {
  animation: animateHeart 15s infinite;
}

Conclusion

SVG can be as simple or as complicated as you need it to be. There are other areas that I want to further explore as I move deeper into the things you can and cannot do with SVG as a vector graphic format.