Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint
Share this Page URL
Help

4. Hacking Your Graphics with Canvas and... > Hack 43. Style SVG Elements with CSS

Hack 43. Style SVG Elements with CSS

SVG has the same privileges as all other DOM elements, including the ability to be styled with CSS. This hack demonstrates how easy it is to create elements with SVG, and then turn them into illustrations with CSS.

The most powerful part of SVG is its standing in the DOM. SVG elements are first-class elements in HTML5, and they have every privilege that other DOM elements have. That being said, it’s simple to control the presentation of these elements with CSS.

For a refresher on how to implement SGV, see [Hack #42], which discusses how to create SVG elements.

SVG has the ability to control the presentation of its elements by setting attributes on the elements themselves. Here are some of the more popular presentation attributes in SVG:

  • fill

  • stroke

  • stroke-width

  • fill-opacity

  • height

  • width

  • x, y

  • cx, cy

  • orientation

  • color

  • cursor

  • clipPath

Many times it makes sense to embed these attributes within the SVG itself where it can be downloaded as one file. Other times it may be more flexible to create our base SVG elements within the SVG file, and style them with a language we are very familiar with: CSS.

Starting with SVG

To get started we will illustrate a simple smiley face with SVG elements. This is a basic illustration that consists of five elements, and nine lines of code:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
   stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
   stroke-width="5" clip-path="url(#MyClip)" />
</svg>

This simple code snippet gets stored in a file called smiley.svg and embedded into our page with an <object> tag as follows:

<object data="smiley.svg" type="image/svg+xml" />

Once the object is on the page, we see the SVG image as it should appear (see Figure 4-19).

SVG representation of a smiley face
Figure 4-19. SVG representation of a smiley face

Stripping Away the Noise

In order to move our visual aspects of the illustration to CSS, we need to strip all the visual aspects out of our SVG. We basically want to leave ourselves with some raw shapes that we can manipulate. We will do this by removing most of the attributes from the SVG file. The one attribute we will not remove is the circle radius, as there is no CSS equivalent to this. Here is what our plain Jane SVG will look like:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle r="160" class="head"  />
  <circle r="20" class="eye leftEye" />
  <circle r="20" class="eye rightEye" />
  <clipPath id="MyClip">
    <rect class="clipBox" width="100%" height="100%" />
  </clipPath>
  <circle r="120" class="mouth"  />

I want to point out a few things about the preceding SVG code. First, note the rect element with the class of clipBox. We have inserted a width and height of 100%. At the time of this writing, current implementations of clip boxes require some height and width attributes set in the element to take effect. Second, I have added a class attribute to each element and assigned at least one class name to each element. Although I could have assigned all the CSS via pseudotags based on DOM position, I prefer to use class names, as they’re more flexible if the DOM should change.

Since our elements have no look and feel to them, we end up with an SVG element that looks like Figure 4-20.

Our SVG components as they appear unstyled
Figure 4-20. Our SVG components as they appear unstyled

Our SVG elements have no visual characteristics or positioning, so we’re starting with a series of circles stacked on top of the SVG object. Think of your SVG object as being like an iframe (an HTML element that loads a new page inside your current page), having its own separate DOM. As an aside, if you don’t want to use an <object> tag to create your element on the page, you can use an iframe to create SVG elements on the page as well.

In the preceding example we have a few simple class names, such as head and eye. If we write CSS declarations based on these class names and put them in our master stylesheet, they won’t actually affect our SVG elements, as the CSS will not cascade down to the SVG elements. To resolve this issue we need to load our CSS in one of three ways. The first way is with inline CSS where we put a style attribute on the element itself and set our styles directly in the element:

<circle class="head"  r="160"  style= fill: yellow; stroke: black;
stroke-width: 2px; "/>

The second way is by attaching a class name to the element (as we have) and then referencing it in an embedded style block. It’s important to remember that the style block needs to be directly in the SVG file, not in the page DOM:

<circle class="head"  r="160" />
<style>
.head {fill: yellow; stroke: black; stroke-width: 2px;}
</style>

The third method, and our choice for this hack, is to attach a class name to the element and then reference an external stylesheet. Again, it’s important to reference the stylesheet from the SVG file and not from the HTML page file. There is also a bit of a twist on this stylesheet. SVG is XML-based but it isn’t HTML, so our traditional link reference will not work properly within the SVG file. For external CSS, the SVG specification references an ancient specification on referencing a stylesheet within an XML document. My only assumption was that this specification was part of the Dead Sea Scrolls discovery or something. You can find the old specification at w3.org.

According to this specification, the stylesheet is loaded with a tag at the top of the SVG file, like so:

<?xml-stylesheet type="text/css" href="/assets/css/svg.css"?>

This, of course, will have an href that will point to the location of your stylesheet on the server, so yours may look different from this example.

Building the CSS

Now that our structure is in place, let’s dig into the CSS that we will use to return our little smiley face to its full glory. We basically have two factors to deal with for each element. The first is the visual attributes, and the second is the position. The visual attributes are quite simple: you will see in our CSS that we have basically taken our old inline attributes of stroke, fill, stroke size, and the like and set them in CSS. Here is a sample from our CSS:

  .head{
        fill: yellow;
        stroke: black;
        stroke-width: 2px;
        }

    .mouth {
        stroke: black;
        fill-opacity: 0;
        stroke-width: 5px;
    }

That was simple enough. The second factor to address is positioning. In order to not have all our elements stacked up on top of one another, we need to tell them where to go. For this, we will pull out one of our new CSS3 attributes called transform, which we will use to move our elements into place. Here is a sample of our CSS3 transforms within our CSS:

    .eye {
       transform:translate(210px, 100px);
        }

    .rightEye {
        transform:translate(380px, 100px);
        }

The transform specifies the translate (or repositioning) of each element from its current position, which again is with the radius centered at (0,0) or the top-left corner of the SVG element.

Each element has CSS specified to provide the visual attributes and the positioning. When we put it all together, our CSS file contains the following declarations:

    .head{
        fill: yellow;
        stroke: black;
        stroke-width: 2px;
      transform:translate(300px, 164px);
        }
    .eye {
        fill: black;
       transform:translate(210px, 100px);
        }

    .rightEye {
        transform:translate(380px, 100px);
        }

    .mouth {
        stroke: black;
        fill-opacity: 0;
        stroke-width: 5px;
        clip-path: url(#MyClip);
       transform:translate(0px, 0px);


    }
    .clipBox {
        width: 600px;
        height: 100px;
      transform:translate(30px, 200px);

    }

For more information on how the clip path works, see [Hack #42] where we clarify how and why we use clip paths.

With this CSS, our finished product looks identical to the one we started with where all our attributes were directly within the SVG elements. Figure 4-21 shows our finished product.

The SVG element using CSS for styling
Figure 4-21. The SVG element using CSS for styling
  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint