I need to reuse multiple times some SVG icons, to avoid repetition I put them in a <defs>
tag and reuse them later with <use>
. However, I want to resize the icons by only defining the height, I expect the width to adapt automatically, but it doesn't work and I can't manage to find any solution. Is there anything possible to make the width dynamic?
svg {
display: block;
margin: 1rem;
padding: 1rem;
background: rebeccapurple;
color: white;
}
<svg style="display: none" >
<defs>
<symbol id="icon" viewBox="0 0 384 512">
<path fill="currentColor" stroke="none" d="m384 192c0 87.4-117 243-168.3 307.2-12.3 15.3-35.1 15.3-47.4 0-52.2-64.2-168.3-219.8-168.3-307.2 0-106.04 85.96-192 192-192 106 0 192 85.96 192 192z"/>
</symbol>
</defs>
</svg>
<!-- Doesn't work, the width is arbitrarily set to 300px. -->
<svg height="1rem">
<use href="#icon" />
</svg>
CodePudding user response:
CSS aspect-ratio
allows you to achieve a consistent ratio when specifying only height or width. In your case, the aspect-ratio
can be set (in the stylesheet) to the svg viewbox
ratio: 384/512;
See https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio
Working snippet:
svg {
display: block;
aspect-ratio: 384/512;
margin: 1rem;
padding: 1rem;
background: rebeccapurple;
color: white;
}
<svg style="display: none" >
<defs>
<symbol id="icon" viewBox="0 0 384 512">
<path fill="currentColor" stroke="none" d="m384 192c0 87.4-117 243-168.3 307.2-12.3 15.3-35.1 15.3-47.4 0-52.2-64.2-168.3-219.8-168.3-307.2 0-106.04 85.96-192 192-192 106 0 192 85.96 192 192z"/>
</symbol>
</defs>
</svg>
<!-- Doesn't work, the width is arbitrarily set to 300px. -->
<svg height="1rem">
<use href="#icon" />
</svg>
CodePudding user response:
Unfortunately, a parent svg can't automatically get a <symbol>
viewBox values.
So you either need to:
- copy the symbol's viewBox attribute to your
<use>
parent svg - calculate viewBox via javaScript (querying for referenced symbol instance)
- create aspect-ratio css classes (as suggested by @Dave Pritlove)
Example 1: copied viewBox attribute (exact, scaled and rounded)
svg {
display: block;
margin: 1rem;
padding: 1rem;
background: rebeccapurple;
color: white;
}
<svg style="display: none" >
<symbol id="icon" viewBox="0 0 384 512">
<path fill="currentColor" stroke="none" d="m384 192c0 87.4-117 243-168.3 307.2-12.3 15.3-35.1 15.3-47.4 0-52.2-64.2-168.3-219.8-168.3-307.2 0-106.04 85.96-192 192-192 106 0 192 85.96 192 192z"/>
</symbol>
</svg>
<p>Original viewBox values</p>
<svg height="1rem" viewBox="0 0 384 512">
<use href="#icon" />
</svg>
<p>Upscaled viewBox values</p>
<svg height="1rem" viewBox="0 0 768 1024">
<use href="#icon" />
</svg>
<p>Scaled down viewBox values</p>
<svg height="1rem" viewBox="0 0 3.84 5.12">
<use href="#icon" />
</svg>
<p>Rounded</p>
<svg height="1rem" viewBox="0 0 4 5">
<use href="#icon" />
</svg>
Worth mentioning: you don't necessarily need the exact same values. Scaled values will work at well. Rounded values might also be OK for your needs - even though they actually change the precise aspect ratio.
Example 2: viewBox attribute retrieved from referenced <symbol>
(Will only work with svgs inlined in your HTML body)
let use = document.querySelectorAll('use')
use.forEach(function(el){
let id = el.getAttribute('href');
let symbol = document.querySelector(id);
let viewBox = symbol.getAttribute('viewBox');
el.closest('svg').setAttribute('viewBox', viewBox );
})
svg {
display: inline-block;
margin: 1rem;
padding: 1rem;
background: rebeccapurple;
color: white;
}
<svg style="display: none" >
<symbol id="icon" viewBox="0 0 384 512">
<path fill="currentColor" stroke="none" d="m384 192c0 87.4-117 243-168.3 307.2-12.3 15.3-35.1 15.3-47.4 0-52.2-64.2-168.3-219.8-168.3-307.2 0-106.04 85.96-192 192-192 106 0 192 85.96 192 192z"/>
</symbol>
<symbol id="address-card" viewBox="0 0 576 512">
<path fill="currentColor" d="M208 256c35.35 0 64-28.65 64-64c0-35.35-28.65-64-64-64s-64 28.65-64 64C144 227.3 172.7 256 208 256zM464 232h-96c-13.25 0-24 10.75-24 24s10.75 24 24 24h96c13.25 0 24-10.75 24-24S477.3 232 464 232zM240 288h-64C131.8 288 96 323.8 96 368C96 376.8 103.2 384 112 384h192c8.836 0 16-7.164 16-16C320 323.8 284.2 288 240 288zM464 152h-96c-13.25 0-24 10.75-24 24s10.75 24 24 24h96c13.25 0 24-10.75 24-24S477.3 152 464 152zM512 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h448c35.35 0 64-28.65 64-64V96C576 60.65 547.3 32 512 32zM528 416c0 8.822-7.178 16-16 16H64c-8.822 0-16-7.178-16-16V96c0-8.822 7.178-16 16-16h448c8.822 0 16 7.178 16 16V416z"></path>
</symbol>
</svg>
<p>Original viewBox values</p>
<svg height="1rem" viewBox="0 0 384 512">
<use href="#icon" />
</svg>
<svg height="1rem" viewBox="0 0 576 512">
<use href="#address-card" />
</svg>
<p>Js generated viewBox values</p>
<svg height="1rem">
<use href="#icon" />
</svg>
<svg height="1rem">
<use href="#address-card" />
</svg>
BTW: <symbol>
elements don't need to be nested in a <defs>
since they are invisible by default.