Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG fails to size properly in absolutely positioned parent

Tags:

css

svg

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:

  • Using 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.
  • Using width: auto causes the SVG to collapse entirely into a dimension of 0px by 0px, but <img> is now sized to its native dimensions

Interestingly, 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:

  • turn on/off absolute positioning of the parent using a checkbox on the top of the page
  • select 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>&lt;img&gt;</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>&lt;svg&gt;</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>&lt;img&gt;</code> element with SVG as data:image</span>
</div>
like image 288
Terry Avatar asked Oct 18 '17 06:10

Terry


People also ask

How to control the size of SVG?

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.

How to increase height of 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.


1 Answers

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.

like image 82
CBroe Avatar answered Sep 24 '22 00:09

CBroe