Using SVG with Media Queries in CSS

With HTML documents, we might 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 with media queries and SVG documents. Consider a logo, such as that of the fictitious Hexagon Web Design & Development pictured below.

Without media queries, this SVG logo would simply stretch or shrink to fit the viewport or its container. But with media queries, we can do more clever things.

Let’s distinguish between the HTML document viewport and the SVG document viewport. When SVG is inline, the HTML viewport and the SVG viewport are one and the same. The SVG document behaves like any other HTML element. On the other hand, when an SVG document is linked—as with the <iframe> , <object> or <img> elements—we’re dealing with the SVG document viewport.

Media queries work in both cases, but when the SVG document is linked, its viewport is independent of its HTML document. In that case, the size of the browser window doesn’t determine the size of the SVG viewport. Instead, the viewport size is determined by the dimensions of the <object> , <iframe> , or <img> element. Take the (abridged) SVG document that follows as an example:

<svg version=”1.1″ id=”HexagonLogo” xmlns=”http://www.w3.org/2000/ ^svg” xmlns:xlink=”http://www.w3.org/1999/xlink” x=”0px” y=”0px” ^ viewBox=”0 0 555 174″ xml:space=”preserve”>

<defs>

<style type=”text/css”>

/* CSS goes here */

</style>

</defs>

<g id=”hex”>

<polygon id=”hexagonbg” points=”55.2,162 10,86.5 55.2,11 145.5,11 190.7,86.5 145.5,162″/>

<path id=”letterH” fill=”#FFFFFF” d=”M58,35.5h33v35.2h18.

4V35.5 h33.2v103.4h-33.2v-38.3H91v38.3H58V35.5z M77.5,126.5V87.

3h45.6v39.2h4V47.9h-4v35.6H77.5V47.9h-4v78.6H77.5z”/>

</g>

<g id=”word-mark”>

<g id=”hexagon-word”>

</g>

<g id=”web-design-and-dev”>

</g>

</g>

</svg>

In smaller viewports, let’s show just the H in a hexagon symbol:

@media (max-width: 320px) {

[id=word-mark] {

display: none;

}

}

Now, whenever our SVG’s container is less than or equal to 20em , only the symbol portion of our logo will be visible.

To trigger this view from the HTML document, set the width of the SVG container:

<iframe src^’hexlogo.svg” style=”width: 320px; border:0″></iframe>

As you may have noticed from looking at the image above, our SVG image retains its intrinsic dimensions even though part of it has been hidden. This, unfortunately, is a limitation of SVG. To fix it, we need to change the viewBox attribute of the SVG document, but only when the viewport is below a certain size. This is a great use case for matchMedia (which is discussed in Chapter 10, “Applying CSS Conditionally”).

The viewBox attribute, as its name suggests, determines the viewable area of an SVG element. By adjusting it, we can determine which part of an SVG image fills the viewport. What follows is an example using matchMedia and a media query to update the viewBox attribute:

<script type=”text/]avascript”>

const svg = document.querySelector( ‘svg’ );

/* Store the original value in a variable */

const originalViewBox = svg.getAttribute( ‘viewBox’ );

/* Define our media query and media query object */

const mq = matchMedia( ‘( max-width: 320px )’ );

/* Define the handler */ const updateViewBox = () => {

if (mq.matches) {

/* Change the viewBox dimensions to show the hexagon */

svg.setAttribute( ‘viewBox’, ‘0 0 200 174’ );

} else {

svg.setAttribute( ‘viewBox’, originalViewBox );

}

}

svg.addEventListener( ‘SVGLoad’, updateViewBox );

/* Fire if the media condition changes */

mq.addEventListener( ‘change’, updateViewBox );

</script>

Now, whenever the SVG container is 320 pixels or less, the value of viewBox will be “0 0 200 174” . When it exceeds 320 pixels, viewBox gets restored to its initial value.

Since this technique uses either the onload event attribute or the SVGLoad event, it’s a good idea to embed our CSS and JavaScript within the SVG file. When CSS is external, the SVGLoad event may fire before its associated CSS finishes loading.

1. Using Media Queries with background-size

SVG documents and media queries aren’t limited to foreground images. We can also resize the SVG viewport using the CSS background-size property.

We’ll start with this SVG document:

<?xml version=”1.0″ encoding=”utf-8″?>

<svg version=”1.1″ xmlns=”http://www.w3.org/2000/svg”

xmlns:xlink=”http://www.w3.org/1999/xlink” x=”0px” y=”0px”

viewBox=”-20 -20 250 250″ xml:space=”preserve”>

<style type=”text/css”> circle {

stroke: #000;

stroke-width: 30;

fill: #009688;

}

@media ( width: 100px ) {

circle {

fill: #673ab7;

}

}

@media ( width: 300px ) {

circle {

fill: #ffc107;

}

}

</style>

</defs>

<circle cx=”100″ cy=”100″ r=”100″ />

<circle cx=”100″ cy=”100″ r=”50″ />

</svg>

This is a simple case. Our <circLe> elements get a new fiLL color at specific viewport widths. When the viewport is 20 pixels wide, the fiLL value is teal. When it’s 300 pixels wide, it’s yellow.

To make this work, we have to use our SVG image as a background image and set the selector’s background-size property. In this case, we’ll use our image as a background for the <body> element and for <Li> elements:

body, li {

background: url(circles.svg);

}

body {

background-color: #9c27b0;

background-size: 300px auto;

}

li {

background-position: left center;

background-repeat: no-repeat;

background-size: 1em auto;

padding-left: 40px;

font-size: 24px;

margin: 1rem 0;

}

The result is pictured below.

Source: Brown Tiffany B (2021), CSS , SitePoint; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *