Search code examples
csscss-grid

CSS Grid changes track size when nested grid changes size


In the snippet below, I have 5 cells named a through e. a and b should be side by side and as tall as possible with a fixed width. c should be below them, shrinking its height as much as possible and matching the width of a and b combined. d and e should be on the right, stacked with d on top, taking all available width with e having a fixed height and letting d take up as much height as possible.

I do not want the heights of c and e to be at all related. Thus, I have placed d and e in a nested grid (brilliantly named de). I expect that when I set the height of e to some value, it should become that height, and d should shrink or grow as needed, and everything else should stay the same. However, when I change the height of e, c also changes its height.

Why in the world is my nested grid effecting its parent, and how to I prevent it?

Here is the snippet, and a fiddle just in case. You can change the height of e by adjusting the value if the input.

let e = document.getElementById('e')
let heightInput = document.querySelector('#controls input')
heightInput.addEventListener('input', function() {
	e.style.height = heightInput.value + 'px'
})
html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

#controls {
  height: 25px;
  padding: 5px;
}

#grid {
  background: orange;
  
  width: 100%;
  height: calc(100% - 35px);
  
  display: grid;
  grid-template-columns: min-content min-content auto;
  grid-template-rows: auto min-content;
  grid-template-areas:
    'a b de'
    'c c de';
}

#a {
  background: red;
  
  grid-area: a;
  width: 30px;
}

#b {
  background: blue;
  
  grid-area: b;
  width: 30px;
}

#c {
  background: green;
  
  grid-area: c;
}

#de {
  background: purple;
  
  grid-area: de;

  display: grid;
  grid-template-rows: auto min-content;
  grid-template-areas:
    'd'
    'e';
}

#d {
  background: grey;
  
  grid-area: d;
}

#e {
  background: yellow;
  
  grid-area: e;
  height: 30px;
}
<body>
  <div id="controls">
    Set height of e in px <input type="number" value="30" />
  </div>
  <div id="grid">
    <div id="a">a</div>
    <div id="b">b</div>
    <div id="c">c</div>
    <div id="de">
      <div id="d">d</div>
      <div id="e">e</div>
    </div>
  </div>
</body>


Solution

  • Actually I don't have an accurate explanation but this is due to the computation of min-content. Instead of it you can use the combination of 1fr and auto

    let e = document.getElementById('e')
    let heightInput = document.querySelector('#controls input')
    heightInput.addEventListener('input', function() {
    	e.style.height = heightInput.value + 'px'
    })
    html, body {
      width: 100%;
      height: 100%;
      padding: 0;
      margin: 0;
    }
    
    #controls {
      height: 25px;
      padding: 5px;
    }
    
    #grid {
      background: orange;
      
      width: 100%;
      height: calc(100% - 35px);
      
      display: grid;
      grid-template-columns: min-content min-content auto;
      grid-template-rows: 1fr auto; /* updated this */
      grid-template-areas:
        'a b de'
        'c c de';
    }
    
    #a {
      background: red;
      
      grid-area: a;
      width: 30px;
    }
    
    #b {
      background: blue;
      
      grid-area: b;
      width: 30px;
    }
    
    #c {
      background: green;
      
      grid-area: c;
    }
    
    #de {
      background: purple;
      
      grid-area: de;
    
      display: grid;
      grid-template-rows: 1fr auto;  /* updated this too but not mandatory */
      grid-template-areas:
        'd'
        'e';
    }
    
    #d {
      background: grey;
      
      grid-area: d;
    }
    
    #e {
      background: yellow;
      
      grid-area: e;
      height: 30px;
    }
    <body>
      <div id="controls">
        Set height of e in px <input type="number" value="30" />
      </div>
      <div id="grid">
        <div id="a">a</div>
        <div id="b">b</div>
        <div id="c">c</div>
        <div id="de">
          <div id="d">d</div>
          <div id="e">e</div>
        </div>
      </div>
    </body>