DEV Community

Cover image for Responsive SVG Image Overlays
Jess Damerst
Jess Damerst

Posted on • Updated on

Responsive SVG Image Overlays

For every Subnautica: Below Zero update, we create a micro-site to detail, in visual clarity, the additions to the game. The objective of each update is to show what we've added or improved upon with the latest Early Access update to the game. Information needs to be visual, clear, and expressive. So, in advance of building each micro-site, I sit down with the information and imagine the best possible way to express each addition. Throughout the years, I've grown the number of designs in my toolkit and with each site, I try to push my skills as web developer.

For this update, I wanted to find a way to point out additions in a particular area of the game using one image. I didn't want to crop the same image over and over, but I also didn't want to simply describe what I needed the viewer to look at. What's a girl to do? Enter responsive SVGs.

Image of Marguerit in her greenhouse with her pet snow stalker

I can feel it, you're asking "Why bother with SVGs when we can use PNGs to a similar effect?" Great question. Most importantly, I didn't want to affect the image. If a user wanted to save the image off the page, I wanted them to have a clean version. Second, PNGs are expensive resources on web-pages, especially on this scale (we would need an image that is 2x the intended size to get a clear picture on Retina displays). Third, SVGs are vector-based and thus scale perfectly. They are the most loss-less format for graphics in websites. Finally, if I wanted to, I could move the path end-points to match up with my textboxes in a responsive fashion.

OK, What about the code?

First things first, I knew I needed the image to be responsive inside of a parent element constrained only by a max-width property. When working with SVGs, you have to think of the containing element as a canvas within which to work. I needed a canvas that was always going to be the size of a responsive image:



<style>
    #greenhouse {
        position: relative;
        width: 100%;
        vertical-align: middle;
        margin: 0;
        overflow: hidden;
    }

    #greenhouse svg { 
        display: inline-block;
        position: absolute;
        top: 0; 
        left: 0;
    }
</style>
<div id="greenhouse">
    <svg viewBox="0 0 1280 720" 
    preserveAspectRatio="xMinYMin meet">
        <image width="1280" 
        height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
        </image>
    </svg>
</div>


Enter fullscreen mode Exit fullscreen mode

The important part here is only that we are containing an image inside of an SVG viewBox which describes the contained element space (our canvas). By making the parent div position: relative; and the child SVG position: absolute; we're asking the DOM to overlay our SVG canvas over the parent element #greenhouse with width instructions to fill 100% of its parent.

The other important part is the preserveAspectRatio attribute, which asks the DOM to calculate a specific ratio for the SVG element at any size. In this case, we've asked for uniform scaling with xMinYMin and we've asked to keep the view box smaller than the viewport with meet.

You can also see that we've explicitly set the height and width of the <image> to match the view box. This is important as it ensures they are responsive together.

The next step is to draw the SVG elements we want within the same view box.



<div id="greenhouse">
    <svg viewBox="0 0 1280 720" 
    preserveAspectRatio="xMinYMin meet">
        <image width="1280" 
        height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
        </image>
        <circle cx="220" cy="320" r="40" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
        <circle cx="320" cy="520" r="70" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
        <circle cx="750" cy="380" r="60" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
    </svg>
</div>


Enter fullscreen mode Exit fullscreen mode

I've chosen to use circles with no fill and a 3px stroke-width. Explaining how to draw SVGs is outside the scope of this post and there is a plethora of information available on the internet describing the various shapes. I used Chris Coyer's The SVG Path Syntax: An Illustrated Guide to design the paths described later.

Using the <circle> element, we describe the coordinates inside our view box using cx for the x-axis and cy for the y-axis. Because this circle element is inside the same parent SVG, we are using the same view box for both the image and the circles. Voila, the circles scale perfectly with the image. If I wanted to, I could also place an anchor tag in exactly the same fashion to match my circles.

But how do we draw the SVG outside the view box?

I'm glad you asked. I wanted to include descriptive textboxes below the image that explained what was in each circle. So I needed a way to extend the SVG graphic outside this view box. Solution? Place another view box on top of this view box and give it a higher z-index. View boxes for everyone!



<style>
    #lines {
        z-index: 3;
    }
</style>
<svg id="lines" viewBox="0 0 1280 1280" 
preserveAspectRatio="xMinYMin meet">
    <path stroke="#d4ffde" stroke-width="3" fill="none"  d="
        M 130,820
        L 130,320
        l 50,0
    "/>
    <path stroke="#d4ffde" stroke-width="3" fill="none" d="
        M 620,820
        L 620,520
        l -230,0
    "/>
    <path stroke="#d4ffde" stroke-width="3" fill="none" d="
        M 1100,820
        L 1100,380                         
        l -290,0
    "/>
</svg>


Enter fullscreen mode Exit fullscreen mode

So I've added a new SVG element and defined its view box as being double the height of the image view box. I won't utilize all this space but it's an easy aspect ratio to understand.

Using the previously mentioned guide, I carefully drew paths from each circle and anchored them below the image and its SVG view box.

And of course, we include the CSS z-index to ask this element to be on top.

Finally, I added some descriptive boxes below. The whole thing looks like:



<style>
    #greenhouse {
        position: relative;
        width: 100%;
        vertical-align: middle;
        margin: 0;
        overflow: hidden;
    }
    #greenhouse svg { 
        display: inline-block;
        position: absolute;
        top: 0; 
        left: 0;
    }
    #lines {
        z-index: 3;
    }
    #greenhouse-details {
        display: flex;
        margin-top: 60%;
        flex-wrap: wrap;
        justify-content: center;
    }
    .narrow-text {
        background-color: #d4ffde;
        padding: 3% 5%;
        margin: 3%;
        flex-basis: 25%;
    }
    @media only screen and (max-width : 480px) {
            .narrow-text {
                flex-basis: 100%;
            }
    }
</style>
<div id="greenhouse">
    <svg id="lines" viewBox="0 0 1280 1280" 
preserveAspectRatio="xMinYMin meet">
        <path stroke="#d4ffde" stroke-width="3" fill="none"  d="
            M 130,820
            L 130,320
            l 50,0
        "/>
        <path stroke="#d4ffde" stroke-width="3" fill="none" d="
            M 620,820
            L 620,520
            l -230,0
        "/>
        <path stroke="#d4ffde" stroke-width="3" fill="none" d="
            M 1100,820
            L 1100,380                         
            l -290,0
        "/>
    </svg>
    <svg viewBox="0 0 1280 720" 
    preserveAspectRatio="xMinYMin meet">
        <image width="1280" 
        height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
        </image>
        <circle cx="220" cy="320" r="40" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
        <circle cx="320" cy="520" r="70" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
        <circle cx="750" cy="380" r="60" stroke="#d4ffde" 
        stroke-width="3" fill="none" />
    </svg>
    <div id="greenhouse-details">
        <div class="narrow-text">
            <p>Whip up a delicious salad</p>
        </div>
        <div class="narrow-text">
            <p>Cuddle up to Marguerit’s Snow Stalker companion</p>
        </div>
        <div class="narrow-text">
            <p>Learn how to farm new, unusual plants</p>
        </div>
    </div>
</div>


Enter fullscreen mode Exit fullscreen mode

And there you have it! Responsive SVG circles overlaid on an image, with some SVG paths extending beyond the image.

Parting thoughts:

  • The color of the textboxes matched the path color perfectly. So, you couldn't see that the paths were actually on top of the boxes. With some due-diligence, it would be best to calculate where the box meets the path and end the path exactly there. Since we're working with fixed ratio, this is simpler then it sounds.
  • I always recommend that the parent element (not pictured here) has a max-width property to prevent the wild things one might see on extra wide screens.
  • The circles could have been inside the same svg view box as the lines. It was simply a result of process that they are not in this example.
  • On mobile, you would also want to take this idea a step further and calculate the end points of the path to meet each textbox as it changes to a 100% width. For the sake of brevity, I'll leave that particular challenge to you, dear reader.

Thanks for reading my first dev.to post!

Top comments (11)

Collapse
 
hangindev profile image
Jason Leung 🧗‍♂️👨‍💻

Interesting use case of SVG. Thanks!

Collapse
 
damjess profile image
Jess Damerst

Thanks Jason!!

Collapse
 
hugh_jeremy profile image
Hugh Jeremy

Great example of a succinct, creative solution to a problem, without resorting to some bloated library. Good stuff! 👍

Collapse
 
damjess profile image
Jess Damerst

Thank you so much Hugh!

Collapse
 
roka profile image
Robert Katzki

Congrats on your first article! 🥳 Written nicely!
I would’ve loved to see a screenshot of how it looks like though.

Collapse
 
damjess profile image
Jess Damerst

Good idea, I'll add one :D

Collapse
 
simbiosis profile image
SIMBIOSIS

The screenshot is at the article's heading.

Collapse
 
simbiosis profile image
SIMBIOSIS • Edited

Amazing! I'm just a huge fan of svg and try to use it everywhwre I allowed to. This is a great teaching and use case to add to my personal knowledge. Thank you very much. If someday you may want to interchange this kid of stuff just contact me.
Well, I was really exited about it and could not avoid palying with it a little bit.
I added an overlay and a clip path to highlight the important areas within the circles. It looks like this:
My file

Collapse
 
bernardbaker profile image
Bernard Baker

I love SVG. And I love this article too 🤸.

Collapse
 
reedids profile image
청설모

SVG 🥰

Collapse
 
tomhermans profile image
tom hermans

Nice idea ! Well explained. Probably will use this technique in some form or another !