Using SVG with Media Queries

Using SVG with Media Queries

In HTML documents, we can show, hide, or rearrange parts of the page based on the conditions of the viewport. If the browser window is 480 pixels wide, for example, we might shift our navigation from a horizontal one to a vertical, collapsible list. We can do something similar when using SVG with media queries.

This article is an extract from Tiffany’s book CSS Master, 3rd Edition. Check it out if you’d like to master other advanced CSS3 features like Flexbox and Grid. You can read it online, download the ebook, or get the hard copy!

*      *      *

As well as using CSS with HTML, we can also use CSS with SVG, or Scalable Vector Graphics. SVG is a markup format for describing flat, two-dimensional images. Because it’s a markup language, it has a Document Object Model, and can be used with CSS.

By using CSS with SVG, we can change the appearance of SVG based on user interaction. Or we can use the same SVG document in multiple places, and show or hide portions of it based on the width of the viewport.

All major browser engines support the SVG 1.1 specification, and they have done for years. Support for features of SVG 2, on the other hand, is still a work in progress. Some of what we’ll discuss here has limited browser support at the time of writing. That may have changed by the time you’re reading this. Keep an eye on the Chromium meta issue — Implement SVG2 features — to track development progress in Chromium-based browsers. Watch the Support SVG 2 features meta issue to follow Firefox’s implementation work, and WebKit’s Implement SVG 2 meta issue for Safari. Issue trackers can be unpleasant to navigate, but for now they’re the best way to track SVG 2 support.

Before we go any further, however, let’s talk about what SVG is and why you should use it.

Vector Images versus Raster Images

Most of the images currently used on the Web are raster images, also known as bitmap images. Raster images are made up of pixels on a fixed grid, with a set number of pixels per inch. JPEG, WebP, GIF, and PNG are all examples of raster image formats.

Raster images are resolution dependent. A 144 PPI (pixels-per-inch) PNG image looks great on a device with a 144 PPI display resolution. When viewed on a higher resolution, 400 PPI display, however, that same image can look fuzzy. Raster images also have fixed dimensions and look best at their original size. Scaling a 150 by 150 pixel image up to 300 by 300 pixels distorts it.

Instead of using pixels on a grid, vector image formats describe the primitive shapes — circles, rectangles, lines, or paths — that make up an image, and their placement within the document’s coordinate system. As a result, vector images are resolution independent, and retain their quality regardless of display resolution or display dimensions.

Resolution independence is the biggest advantage of SVG. We can scale images up or down with no loss of quality. The same image looks great on both high and low PPI devices. That said, SVG is poorly suited to the amount of color data required for photographs. It’s best for drawings and shapes. Use it in place of PNG and GIF images, and as a more flexible replacement for icon fonts.

Another advantage of SVG is that it was designed to be used with other web languages. We can create, modify, and manipulate SVG images with JavaScript. Or, as we’ll see below, we can style and animate SVG using CSS.

Associating CSS with SVG Documents

Using CSS with SVG is a lot like using it with HTML. We can apply CSS using the style attribute of an SVG element, group CSS within a document using the <style> element, or link to an external stylesheet. The pros and cons of each method are the same as when using CSS with HTML.

Using the style Attribute

Here’s a simple SVG document where the code creates a black circle:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" ➥enable-background="new 0 0 200 200"> <circle cx="101.3" cy="96.8" r="79.6" /> </svg> 

The image below shows how that code renders in a browser.

A circle in SVG

Let’s give our circle a pink fill using CSS and the style attribute:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" ➥enable-background="new 0 0 200 200"> <circle cx="101.3" cy="96.8" r="79.6" style="fill: #f9f" /> </svg> 

The effect of this is shown below.

Using the style attribute to add a fill color

Here’s one difference between using CSS with HTML and using it with SVG: property names. Many of the CSS properties that we use with HTML documents aren’t compatible with SVG, and vice versa. We’ll come back to this point later in the chapter.

Using the style attribute isn’t the best way to use CSS, of course. Doing so limits the ability to reuse those styles across multiple elements or documents. Instead, we should use inline or linked CSS.

Embedding CSS in SVG Documents

Instead of using the style attribute, we can use the <style> element:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ➥ 200 200" enable-background="new 0 0 200 200"> <style type="text/css"> circle { fill: #0c0; } </style> <circle cx="101.3" cy="96.8" r="79.6" /> </svg> 

Embedding CSS in an SVG document lets us reuse those styles for multiple elements within the same document, but it prevents that CSS from being shared across multiple documents. That’s fine for logos and icons. But if you’re creating something like a library of chart styles, an external CSS file is a better bet.

Using a standard text editor, you can also add CSS to SVG images created with software such as Sketch, Inkscape, or Illustrator. Doing so won’t affect your ability to edit the image with the drawing application, but if you edit the file using image software, the application may rewrite or remove your CSS.

Linking from SVG to an External CSS File

As with HTML, linking to an external CSS file makes it possible to share styles across several SVG documents. To link an external CSS file, add <? xml-stylesheet ?> to the beginning of your SVG file:

<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet href="style.css" type="text/css"?> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ➥ 200 200" enable-background="new 0 0 200 200"> <circle cx="101.3" cy="96.8" r="79.6" /> </svg> 

Using the <link> Element

Alternatively, use the HTML <link> element. If you do use this method, you’ll need to include the xmlns namespace attribute, as shown below:

<link href="style.css" type="text/css" rel="stylesheet" ➥ xmlns="http://www.w3.org/1999/xhtml" /> 

Note: some older browsers need the <link> element to be enclosed by <defs> or <g> tags.

The <link> element isn’t an SVG element. It belongs to HTML and XHTML. XHTML is a variant of HTML that’s parsed according to the rules of XML markup. According to the rules of XML, we can borrow elements and their behavior from other XML dialects, such as XHTML. To do so, however, we need to tell the browser which namespace the element belongs to using the xmlns attribute.

Using @import

We can also link to an external stylesheet by using @import inside <style> and </style> tags:

<style type="text/css"> @import('style.css'); </style> 

This method functions similarly to the <link> method.

SVG and the <img> Element: Limitations

Linking from SVG files to external assets, including CSS files, doesn’t work with the <img> element. This is a security limitation of the <img> element that’s baked into browsers.

If you’d like to use linked CSS with your SVG images, you’ll need to do either of these two things:

  • use the <style> element in your SVG document to place your CSS inline
  • use an <iframe> or <object> element (see note below)

Note: Craig Buckler’s tutorial “How to Add Scalable Vector Graphics to Your Web Page” discusses using <iframe> and <object> in detail.

In general, you should use <iframe> over <object>. However, the <object> element can be the child of an <a> element, while <iframe> can’t. Using <iframe> or <object> also makes the SVG document tree available to the parent document’s document tree. This means that we can use JavaScript to interact with it (for example, with document.querySelector('iframe').contentDocument).

Inline SVG and External Assets

When adding SVG to HTML, the browser won’t load external assets referenced by the SVG document. We can, however, link to CSS for our SVG document from the <head> of our HTML document:

<head> ⋮ <link href="svg.css" type="text/css" rel="stylesheet" /> </head> 

SVG elements within HTML documents also become part of the HTML document tree. If you’re using inline SVG, it’s perfectly fine to combine your HTML-related and SVG-related CSS in the same stylesheet.

Differences between SVG and HTML

While SVG and HTML are both markup languages, there are two significant differences between them that affect how they work with CSS:

  • SVG doesn’t adhere to the CSS box model
  • SVG lacks a positioning scheme

SVG Doesn’t Adhere to the CSS Box Model

When used with HTML, CSS layout follows the rules of the CSS box model. SVG, on the other hand, uses coordinates for layout. It adheres to what may be best understood as a “shape model”.

SVG shapes aren’t limited to rectangular boxes. As a result, most box-model–related properties don’t apply to SVG elements. You can’t, for instance, change the padding or margin of an SVG element. Nor can you use the box-sizing, box-shadow, outline, or border-* properties. Grid layout, floats, and Flexbox also don’t work.

You can, however, use CSS to set or change a range of SVG properties and attribute values. The full list is outlined in the SVG 2 specification, although support in most browsers is incomplete. Some CSS properties, such as filter, can be used with SVG or HTML. We’ll discuss a few of them in this chapter, within the context of specific techniques.

SVG Lacks a Positioning Scheme

When CSS is used with HTML, element boxes can:

  • exist within a normal flow
  • be removed from normal flow with the float property
  • be removed from normal flow with the position property

The CSS specification refers to these as positioning schemes. Positioning schemes don’t exist in SVG. The position property has no effect on SVG elements. Neither do properties such as top, left and bottom, which depend on elements being positioned. You also can’t float elements within an SVG document.

Instead, SVG uses a coordinate system for element placement. To create a <circle>, for example, you need to set its center point coordinates using the cx and cy attributes, and set a radius length using the r attribute. A polygon consists of a series of point coordinates and line segments drawn between them. In other words, you can define where an element will be drawn to the SVG canvas, but you can’t “position” them in the CSS sense of the word.

Related to positioning schemes, SVG also lacks the idea of z-index and stacking contexts.

Note: the SVG 2 specification does define behavior for z-index and stacking contexts in SVG documents, but most browsers don’t yet support it.

SVG elements are instead stacked according to their source order. Those that appear later in the document sit towards the top of the stack. If you want to change the stacking order of SVG elements, you’ll need to move them around in the source or use JavaScript to reorder them in the DOM tree.

In fact, most CSS 2.1 properties don’t apply to SVG documents. Exceptions include animations and transforms, display, overflow, visibility, filter, and a few font and text-related properties. Instead, you’ll have to use SVG-specific styling properties with SVG documents. Most of these properties can also be expressed as SVG element attributes.

Styling SVG Elements

Here’s a simple example of how to style SVG elements using CSS. First our SVG document, which is a stand-alone file:

<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet href="styles.css" type="text/css" ?> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= ➥"http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 497 ➥ 184" enable-background="new 0 0 497 184" xml:space="preserve"> <polygon id="star" points="77,23.7 98.2,66.6 145.5,66.5 111.2, ➥106.9,119.3,154 77,131.8 34.7,154 42.8,106.9 8.5,67.5 55.8, ➥66.6 "/> <circle id="circle" cx="245" cy="88.9" r="67.5"/> </svg> 

This markup creates the image shown below.

A simple circle and star SVG image

Although we can’t use most CSS properties with SVG documents, we can use CSS to change an element’s color. Let’s make our star yellow:

#star { fill: hsl( 44, 100%, 50% ); } 

You’ll often see the fill attribute used with SVG tags — for example, <circle fill="rgb( 255, 185, 0 )" cx="3" cy="10" r="100"> — but it’s also a property that can be used with CSS.

We can also use CSS to adjust an element’s stroke, which is the outline of an SVG shape. A shape’s stroke exists, even if no stroke properties are set. Let’s give our circle a dark blue, dashed border that’s ten pixels wide. We’ll also set its fill property to cornflowerblue:

circle { fill: cornflowerblue; stroke: darkblue; stroke-width: 10; stroke-dasharray: 10, 15; stroke-linecap: round; } 

Together this gives us the result below.

A simple circle and star SVG image

Continue reading Using SVG with Media Queries on SitePoint.