I am encountering an issue where an inline <svg>
element does not stretch to its native size, as declared in its viewBox
attribute, when it is wrapped in an absolutely positioned parent:
width: 100%
seems to only force the SVG to stretch to the browser defined default size of 300px
. This also causes <img>
to not size to its native dimensions.width: auto
causes the SVG to collapse entirely into a dimension of 0px by 0px, but <img>
is now sized to its native dimensionsInterestingly, this behavior can be replicated by using the SVG as a data:image/svg+xml
for the src
attribute of an <img>
element, so it appears that SVG does not "pass on" its native dimensions to its containing parent (be it an <svg>
or an <img>
element).
So, my question is that if there is any reliable way in CSS that can force an SVG to size to its native size based on its viewbox attributes. I can probably use JS to brute force my way, by reading the viewBox
attributes and use the fixed aspect ratio hack to show my SVG as a background image in a fixed aspect ratio <div>
element, but I try to refrain from that. I am perhaps misunderstanding the browser's implementation of SVG specs, but I can't seem to find a workaround for this.
My issue can be reproduced in the code snippet below. You can:
width
declarations (auto or 100%) on the <img>
or <svg>
elements// The JS logic below is only used to dynamically set styles based on checkbox/select changes, has nothing to do with SVG layout
// Change positioning strategy
document.getElementById('absPosToggle').addEventListener('change', function() {
var parents = document.querySelectorAll('.parent');
if (this.checked) {
for (var i = 0; i < parents.length; i++) {
parents[i].classList.remove('no-absolute-positioning');
}
} else {
for (var i = 0; i < parents.length; i++) {
parents[i].classList.add('no-absolute-positioning');
}
}
});
// Change width declaration of <svg>/<img> elements
document.getElementById('widthSetting').addEventListener('change', function() {
var images = document.querySelectorAll('img, svg');
var value = this.options[this.selectedIndex].value;
if (value === '100%') {
for (var i = 0; i < images.length; i++) {
images[i].classList.add('width--100');
}
} else {
for (var i = 0; i < images.length; i++) {
images[i].classList.remove('width--100');
}
}
});
body {
margin: 0;
padding: 50px 0 0 0;
}
form {
position: fixed;
top: 0;
left: 0;
right: 0;
display: block;
background-color: #fff;
z-index: 1;
padding: 5px;
}
.wrapper {
width: 100%;
height: 250px;
background-color: #eee;
margin-bottom: 10px;
position: relative;
text-align: center;
}
.parent {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.parent.no-absolute-positioning {
position: relative;
top: 0;
left: 0;
transform: none;
}
img,
svg {
display: block;
width: auto;
}
img.width--100,
svg.width--100 {
width: 100%;
}
<form>
<label><input type="checkbox" id="absPosToggle" checked />Toggle absolute positioning</label><br />
<label for="widthSetting">Set svg/img widths to:</label><select id="widthSetting">
<option value="auto">Auto</option>
<option value="100%">100%</option>
</select>
</form>
<!-- <img> -->
<div class="wrapper">
<div class="parent">
<img src="https://via.placeholder.com/500x150/173755/ffffff" />
</div>
<span>This is an <code><img></code> element</span>
</div>
<!-- Inline <svg> -->
<div class="wrapper">
<div class="parent">
<svg viewBox="0 0 500 150" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="500" height="150" fill="#b13131" /><g transform="translate(250, 75)"><text fill="#ffffff" style="text-anchor: middle; font-size: 50; font-family: Arial;" dy="0.35em">500 x 150</text></g></svg>
</div>
<span>This is an inline <code><svg></code> element</span>
</div>
<!-- <img> with SVG as data:image -->
<div class="wrapper">
<div class="parent">
<img src="data:image/svg+xml;charset=utf8,%3Csvg%20viewBox=%220%200%20500%20150%22%20xmlns=%22http://www.w3.org/2000/svg%22%3E%3Crect%20x=%220%22%20y=%220%22%20width=%22500%22%20height=%22150%22%20fill=%22#b13131%22%20/%3E%3Cg%20transform=%22translate(250,%2075)%22%3E%3Ctext%20fill=%22#ffffff%22%20style=%22text-anchor:%20middle;%20font-size:%2050;%20font-family:%20Arial;%22%20dy=%220.35em%22%3E500%20x%20150%3C/text%3E%3C/g%3E%3C/svg%3E"
/>
</div>
<span>This is an <code><img></code> element with SVG as data:image</span>
</div>
Any height or width you set for the SVG with CSS will override the height and width attributes on the <svg> . So a rule like svg {width: 100%; height: auto;} will cancel out the dimensions and aspect ratio you set in the code, and give you the default height for inline SVG.
As you know, SVG files are vector images, so you can simply zoom in when you view it. That is, you define the viewBox (to 0, 0, <oldWidth>, <oldHeight> ), and then you can set the width and height as you wish. The above example thus doubles the width and height.
I think root cause of the issue is that with absolute positioning you make the width calculation method for the parent become shrink-to-fit
, and with that and content that has no proper intrinsic width either, that's kind of a catch 22, which might explain the fall back to the "standard" 300px width.
As we figured out, you have been through https://css-tricks.com/scale-svg/ already, and it seems like adding width
and height
attributes to the SVG itself might be the only working way (in this particular situation) to give the SVG a proper intrinsic height, so that it can in turn "span up" its absolute positioned parent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With