Search code examples
csssvg

SVG with 100% width and height causes overflow if wrapped inside DIV element


Codepen example

The HTML shown below works perfectly fine. It shows some SVG element which always matches its parents size without any overflow. This is what I want. But when the <div class="svg-inner-container"> element is commented back in, then the SVG causes unwanted overflow. Why?

Related topics/answers suggest to set display: block on the SVG. But this does not change anything.

I am using Brave Browser Version 1.70.123 Chromium: 129.0.6668.89. Edge Version 129.0.2792.79 has same issue.

Here the SVG works correctly cause no wrapper div is used:

// Step 0. Look at the result
// Obersvation 0: The SVG perfectly matches its parents size and causes no overflow at any viewport. This is what the result is supposed to look like. But the final result also needs to use the commented out <div class="svg-inner-container"> element.

// Step 1. Comment in the <div class="svg-inner-container">. 
// Obersavation 1: For some reason this is causing an unwanted overflow. Why?

// Step 2. Perhaps This could help to understand the problem. Change the class "svg-inner-container" to "svg-inner-container-90-50". Now play with the viewport width. 
// Oservation 2: If the viewport width is very small, then the svg matches the 90-50 size. But if the viewport grows in width, at some point the svg height scales beyond its 50% limit. This is very confusing to me.
.root{
  width: 100vw;
  height: 100vh;    
}

.svg-outer-container{
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: auto;
  width: 100%;
  height: 100%;
  border: 2px solid red;
}

.svg-inner-container {
    width: 100%;
    height: 100%;
}

.svg-inner-container-90-50 {
    width: 90%;
    height: 50%;
}

.svg{
  width: 100%;
  height: 100%;
  background: lightgreen;
}

polygon {
  fill: skyblue;
  stroke: purple;
  stroke-width: 1;
}

body {
  margin: 0;
}

* {
   box-sizing: border-box;
}
<div class="root">
  <div class="svg-outer-container">  
<!--     <div class="svg-inner-container">  -->
      <svg class="svg" viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
        <polygon points="50,0 100,50 50,100 0,50"/>
      </svg>
<!--     </div>  -->
  </div>
</div>

Here the SVG is wrapped in an additional DIV and is causing unwanted overflow:

.root{
  width: 100vw;
  height: 100vh;    
}

.svg-outer-container{
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: auto;
  width: 100%;
  height: 100%;
  border: 2px solid red;
}

.svg-inner-container {
    width: 100%;
    height: 100%;
}

.svg{
  width: 100%;
  height: 100%;
  background: lightgreen;
}

polygon {
  fill: skyblue;
  stroke: purple;
  stroke-width: 1;
}

body {
  margin: 0;
}

* {
   box-sizing: border-box;
}
<div class="root">
  <div class="svg-outer-container">  
    <div class="svg-inner-container"> 
      <svg class="svg" viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
        <polygon points="50,0 100,50 50,100 0,50"/>
      </svg>
    </div> 
  </div>
</div>


Solution

  • Add position: relative to svg-inner-container class and position: absolute to svg class. Also you can remove additional height and width from inner container. The reason why svg overflows when you uncomment inner div is because inner container adds an additional layer of sizing. Then inner container will try to take the size of outer container which is set to 100% width and height that's why it looks like svg overflowed but actually it just takes its parents width and height.

    .root{
      width: 100vw;
      height: 100vh;    
    }
    
    .svg-outer-container{
      display: grid;
      grid-template-rows: auto;
      grid-template-columns: auto;
      width: 100%;
      height: 100%;
      border: 2px solid red;
    }
    
    .svg-inner-container {
        position: relative;
    }
    
    .svg{
      width: 100%;
      height: 100%;
      background: lightgreen;
      position: absolute
    }
    
    polygon {
      fill: skyblue;
      stroke: purple;
      stroke-width: 1;
    }
    
    body {
      margin: 0;
    }
    
    * {
       box-sizing: border-box;
    }
    <div class="root">
      <div class="svg-outer-container">  
        <div class="svg-inner-container"> 
          <svg class="svg" viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
            <polygon points="50,0 100,50 50,100 0,50"/>
          </svg>
        </div> 
      </div>
    </div>