Reputation: 66113
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>
Upvotes: 10
Views: 3518
Reputation: 96273
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.
Upvotes: 3