Responsive Logo Composition With SVG
At 4/19/2024
Logos are an ideal use-case for SVG. Because they’re typically designed for reproduction and versatility, they’re almost always vector to begin with. SVG’s support of CSS and media queries means we can adapt brand imagery depending on the display size, a technique artfully documented by the likes of Andreas Bovens, Ilya Pukhalski, Sara Souedian and Ana Sampaio.
But what if we want to re-arrange our logo elements depending on the aspect ratio of the asset, perhaps to account for the amount of vertical real estate available to us?
We could use two separate image assets, recomposing them using the float
, display
or position
properties, but that makes it difficult to scale both assets together. We could also use the <picture>
element, but that requires two different image files and awareness of how large the image will be relative to the viewport.
Is it possible for a single SVG (embedded as an <img>
) to adapt to the aspect ratio of its dimensions in-page?
Turns out, yes! Here’s a demo:
The <img>
element is sized using percentages. Notice how the image contents scale to meet the nearest edge of their container, re-composing when the aspect ratio has changed significantly.
The demo source itself is unremarkable. The magic’s in the SVG file:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style>
#emblem {
fill: #39cccc;
}
#word {
fill: #225b6d;
}
#wide {
visibility: hidden;
}
@media all and (min-aspect-ratio: 3/1) {
#wide {
visibility: visible;
}
#tall {
visibility: hidden;
}
}
</style>
</defs>
<symbol id="emblem" viewBox="0 0 51 60">
<polygon points="31 38.43 46 29.52 46 35.23 31 44.13 31 51.02 51 39.5 51 20.7 31 32.21 31 38.43"/>
<polygon points="50.88 15 25.44 0 0 15 0 45 25 60 25 30 50.88 15"/>
</symbol>
<symbol id="word" viewBox="0 0 200 26">
<path d="M16.72,0.14V5H5.11v5.25h9.41v4.83H5.11v5.82H16.72V25.7H0V0.14H16.72Z"/>
<path d="M24.21,25.7H18.64l7.67-13.1L18.85,0.14h6L29.57,8.2l4.72-8.06h5.57L32.45,12.92,40.08,25.7h-6l-5-8.45Z"/>
<path d="M46.43,25.7H41.14L49.38,0.14h6L63.58,25.7h-5.5L56.41,20H48.14Zm3.12-10.58H55l-2.7-9.37Z"/>
<path d="M91.38,25.7H86.55v-17L81,21.62H77.32l-5.54-13v17H66.95V0.14h5.61l6.6,15,6.6-15h5.61V25.7Z"/>
<path d="M110,0.14a4.81,4.81,0,0,1,5,5v6.46a4.81,4.81,0,0,1-5,5h-8v9H97V0.14H110Zm-1.28,11.72A1.09,1.09,0,0,0,110,10.65V6.18A1.09,1.09,0,0,0,108.77,5h-6.67v6.89h6.67Z"/>
<path d="M124.32,0.14V20.88H135V25.7H119.2V0.14h5.11Z"/>
<path d="M155.63,0.14V5H144v5.25h9.41v4.83H144v5.82h11.61V25.7H138.91V0.14h16.72Z"/>
<path d="M172.31,6a1.09,1.09,0,0,0-1.21-1.21h-5.54A1.09,1.09,0,0,0,164.36,6V19.81A1.09,1.09,0,0,0,165.57,21h5.54a1.09,1.09,0,0,0,1.21-1.21V16.69h5.11V20.8a4.81,4.81,0,0,1-5,5h-8.09a4.81,4.81,0,0,1-5-5V5a4.81,4.81,0,0,1,5-5h8.09a4.81,4.81,0,0,1,5,5V9.16h-5.11V6Z"/>
<path d="M195,0a4.81,4.81,0,0,1,5,5V20.8a4.81,4.81,0,0,1-5,5H186.4a4.81,4.81,0,0,1-5-5V5a4.81,4.81,0,0,1,5-5H195Zm-0.07,6a1.09,1.09,0,0,0-1.21-1.21h-6A1.09,1.09,0,0,0,186.47,6V19.81A1.09,1.09,0,0,0,187.68,21h6a1.09,1.09,0,0,0,1.21-1.21V6Z"/>
</symbol>
<svg id="wide" viewBox="0 0 260 60">
<use xlink:href="#emblem" width="51"/>
<use xlink:href="#word" width="200" x="60"/>
</svg>
<svg id="tall" viewBox="0 0 200 130">
<use xlink:href="#emblem" x="60" width="80" height="92"/>
<use xlink:href="#word" y="104" width="200" height="26"/>
</svg>
</svg>
Code language: HTML, XML (xml)
Let’s break down how this is working.
1. Making Our Container Responsive
Because we’re going to change the dimensions of our logo depending on the container size, we can’t have any attributes that suggest a size or aspect ratio up-front. This means no viewBox
, width
or height
.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
Code language: Handlebars (handlebars)
2. Adding Reusable Elements
Both logo variations use the same two assets: The hexagonal mark, and the “ExampleCo” type. Because our container has no inherent dimensions, we’ll store these assets as <symbol>
elements with their own viewBox
. We use CSS to style both symbols.
<defs>
<style>
#emblem {
fill: #39cccc;
}
#word {
fill: #225b6d;
}
</style>
</defs>
<symbol id="emblem" viewBox="0 0 51 60">
<!-- (paths, etc.) -->
</symbol>
<symbol id="word" viewBox="0 0 200 26">
<!-- (paths, etc.) -->
</symbol>
Code language: HTML, XML (xml)
3. Creating “Wide” and “Tall” Compositions
Now that we have all our ingredients, we can arrange them using <svg>
elements, each with its own viewBox
attribute:
<svg id="wide" viewBox="0 0 260 60">
<use xlink:href="#emblem" width="51"/>
<use xlink:href="#word" width="200" x="60"/>
</svg>
<svg id="tall" viewBox="0 0 200 130">
<use xlink:href="#emblem" x="60" width="80" height="92"/>
<use xlink:href="#word" y="104" width="200" height="26"/>
</svg>
Code language: HTML, XML (xml)
At this point, both compositions will be visible all of the time.
4. Hiding or Showing Based on Aspect Ratio
We return to the <style>
element, using the visibility
property and min-aspect-ratio
to determine when to hide or show each variation:
#wide {
visibility: hidden;
}
@media all and (min-aspect-ratio: 3/1) {
#wide {
visibility: visible;
}
#tall {
visibility: hidden;
}
}
Code language: CSS (css)
By default, the “tall” variation will be visible. If the asset is at least three times wider than it is tall, the “wide” variation will be visible instead.
5. Displaying in Our Page
With our asset complete, we can include it in our page. We should also provide a default width
and height
in case styles fail to load (since we haven’t specified any in the SVG asset itself):
<img id="logo" src="logo.svg" alt="ExampleCo" width="200" height="130">
Code language: Handlebars (handlebars)
We’re now free to style the width
and height
of the image however we like. The demo uses percentages:
#logo {
display: block;
height: 33.3%;
width: 100%;
}
Code language: CSS (css)
Is This a Good Idea?
Sometimes! It’s super flexible, only takes one request, caches beautifully and works in IE 9 and above.
That said, media queries in external SVG elements without a viewBox
can be rather finicky. If I wanted more than two compositions, I would definitely consider using <picture>
instead.
Further Reading
- How to Scale SVG by Amelia Bellamy-Royds
- Establishing New Viewports by Sara Soueidan