The following is a short extract from Tiffany’s upcoming book, CSS Master, 2nd Edition, which will be available shortly.

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 and documents. Consider a logo, such as that of the fictitious Hexagon Web Design & Development below.

A very real logo for a fictitious company

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 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:[^4]

<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: 20em) {
    [id=word-mark] {
        display: none;
    }
}

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

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

Showing/hiding elements based on the SVG viewport size

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

<object data="hexlogo.svg" type="image/svg+xml" style="width: 20em;"></object>

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.

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/javascript">
var svg, originalViewBox, max20em, mq, updateViewBox;

svg = document.querySelector('svg');

/* Store the original value in a variable */
originalViewBox = svg.getAttribute('viewBox');

/* Define our media query and media query object */
mq  = matchMedia("(max-width: 20em)");

/* Define the handler */
updateViewBox = function(){
    if (mq.matches) {
        /* Change the viewBox dimensions to show the hexagon */
        svg.setAttribute('viewBox', "0 0 200 174");
    } else {
        svg.setAttribute('viewBox', originalViewBox);
    }
}

/* Fire on document load */
// WebKit/Blink browsers
svg.onload = updateViewBox;

// Firefox & IE
svg.addEventListener('SVGLoad', updateViewBox, true);

/* Fire if the media condition changes */
mq.addListener(updateViewBox);            

</script>

Note: Browsers are a little bit of a mess when it comes to handling the SVGLoad event. In my tests, addEventListener worked most consistently with Firefox. For best results in Chrome and Safari, use the onload event attribute. Microsoft Edge also works best with onload, but only when used as an attribute of the <svg> tag. In other words, <svg onload="updateViewBox">.

Now, whenever the SVG container is 20em or less, the value of viewBox will be "0 0 200 174". When it exceeds 20em, viewBox will be restored to its initial value as represented below.

Adjusting the viewBox attribute based on the viewport’s width

Note: For a fuller primer on creating interactive SVG documents, read the “Dynamic SVG and JavaScript” chapter of An SVG Primer for Today’s Browsers from the W3C.

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.

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. All of the latest major browsers support this technique, but older browser versions don’t. Be careful when using this technique in production.

We’ll start with this SVG document:

<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_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 will get a new fill color at specific viewport widths. When the viewport is 20 pixels wide, the fill will be teal. When it’s 300 pixels wide, it will be 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. The image below shows the results:

body, li {
    background: url(../images/circles.svg);
}
body {
    background-color: #9c27b0;
    background-size: 300px auto;
}
li {
      background-size: 20px auto;
      background-repeat: no-repeat;
      background-position: left 3px;
      padding-left: 25px;
}

Manipulating the SVG viewport with the CSS background-size property





Source link https://www.sitepoint.com/using-svg-with-media-queries/

LEAVE A REPLY

Please enter your comment!
Please enter your name here