Search code examples
htmlcsssvgaspect-ratio

WebKit SVG height and max-height differences


If you run the following two examples in Chrome or Safari (Firefox is not affected), you will see the differences on the width of the SVG image.

The first example seems like the correct behavior IMO. But it looks like the svg is being stretched in the second example. Is that a bug? Is there any fix?

Example 1 - height:100%, it works as expected.

html, body {
  height: 100%;
  margin: 0;
}
img {
  background: silver;
  vertical-align: top;
  height: 100%;
}
<img src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">

Example 2 - max-height:100%, the main part of the image is shifted to the center, and leaving a large amount of space on the left and right.

html, body {
  height: 100%;
  margin: 0;
}
img {
  background: silver;
  vertical-align: top;
  max-height: 100%;
}
<img src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">


Solution

  • This seems certainly a bug. CSS 2.1 says

    for replaced elements with both width and height computed as auto, use the algorithm under Minimum and maximum widths above to find the used width and height. Then apply the rules under "Computing heights and margins" above, using the resulting width and height as if they were the computed values.

    In the Minimum and maximum widths algorithm, we have this case:

    • Constraint violation: h > max-height
    • Resolved Width: max(max-height * w/h, min-width)
    • Resolved Height: max-height

    Since min-width is 0, the resolved width should be max-height * w/h.

    Webkit seems to use w instead, so it's a bug.

    As a workaround, you can use the common technique to force an aspect ratio.

    html, body {
      height: 100%;
      margin: 0;
    }
    #wrapper {
      position: relative;
      display: inline-block; /* Shrink-to-fit width */
      vertical-align: top;
      height: 100%;
      max-width: 100%;
      overflow: hidden;
    }
    .ratio {
      height: 100%;
      vertical-align: top;
      visibility: hidden;
    }
    .filler {
      position: absolute;
      max-height: 100%;
      max-width: 100%;
      background: silver;
      max-height: 100%;
      left: 0;
      top: 0;
    }
    <div id="wrapper">
      <img class="ratio" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">
      <img class="filler" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">
    </div>

    However, note that the effect will only work when you load the page. If then you resize the window, it will break. To fix this, force a rerender with JS:

    window.addEventListener('resize', function() {
      var el = document.getElementById('wrapper'),
          parent = el.parentNode,
          next = el.nextSibling;
      parent.removeChild(el);
      parent.insertBefore(el, next);
    });
    html, body {
      height: 100%;
      margin: 0;
    }
    #wrapper {
      position: relative;
      display: inline-block; /* Shrink-to-fit width */
      vertical-align: top;
      height: 100%;
      max-width: 100%;
      overflow: hidden;
    }
    .ratio {
      height: 100%;
      vertical-align: top;
      visibility: hidden;
    }
    .filler {
      position: absolute;
      max-height: 100%;
      max-width: 100%;
      background: silver;
      max-height: 100%;
      left: 0;
      top: 0;
    }
    <div id="wrapper">
      <img class="ratio" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">
      <img class="filler" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/mozilla.svg">
    </div>