Search code examples
csscyclic-dependency

Dealing with cyclic dependencies of percentage sized boxes css (specifically how to get a max-height)


First shortly, this is a follow up question to Problem with max-height not working in css grid element where I misunderstood my problem and I would have had to completely rewrite it, so I'm starting a new one, I hope that is fine.

My goal in one sentence is, to have an overflow: auto in a box, which has its height governed by its content, but its max-height governed by its parent. A static representation would be:

#ctlsmall {
  height: 50px;
}

#ctlbig {
  height: 210px;
}

#contentsmall {
  height: 50px;
}

#contentbig {
  height: 350px;
}

.content {
  background-image: linear-gradient(30deg, #005500 0%, #9922ee 100%);
  width: 120px;
}

.overflowctl {
  overflow-y: auto;
  overflow-x: hidden;
}

.frame {
  height: auto;
  max-height: calc( 100% - 40px );
  width: 120px;
  border-radius: 15px;
  padding: 20px;
  background: #221100;
}

.canvas {
  display: inline-block;
  height: 250px;
  width: 200px;
  margin-right: 50px;
  background: #ddccaa;
  vertical-align: top;
}
<div class='canvas'>
  <div class='frame'>
    <div id='ctlbig' class='overflowctl'>
      <div id='contentbig' class='content'>
      </div>
    </div>
  </div>
</div>

<div class='canvas'>
  <div class='frame'>
    <div id='ctlsmall' class='overflowctl'>
      <div id='contentsmall' class='content'>
      </div>
    </div>
  </div>
</div>

The problems begin, when I don't want to use fix heights for my .overflowctl, because both, .content and .canvas have unknown sizes. (.content in reality is an iframe, and .canvas depends on the window size). My naive approach was to just use height: auto for .overflowctl and .frame, as usually the height should be governed by their content, and at the same time a percentage based max-height, as their max-height is set by the parents. This is logical in a natural understanding of those properties, but seemingly it does not work, because in css the combination between a parent-child combination, where the parent size relies on the child and vice versa is problematic and while solving this, the max-properties seem to be changed... It is described here and @onkar-ruikar who showed this to me as an answer to my previous question gave some explanation for it. I prepared a jsfiddle to illustrate the problem, with the adapted example from above:

.overflowctl {
  height: auto;
  max-height: 100%;
}

#contentsmall {
  height: 50px;
}

#contentbig {
  height: 350px;
}

.content {
  background-image: linear-gradient(30deg, #005500 0%, #9922ee 100%);
  width: 120px;
}

.overflowctl {
  overflow-y: auto;
  overflow-x: hidden;
}

.frame {
  height: auto;
  max-height: calc( 100% - 40px );
  width: 120px;
  border-radius: 15px;
  padding: 20px;
  background: #221100;
}

.canvas {
  display: inline-block;
  height: 250px;
  width: 200px;
  margin-right: 50px;
  background: #ddccaa;
  vertical-align: top;
}
<div class='canvas'>
  <div class='frame'>
    <div id='ctlbig' class='overflowctl'>
      <div id='contentbig' class='content'>
      </div>
    </div>
  </div>
</div>

<div class='canvas'>
  <div class='frame'>
    <div id='ctlsmall' class='overflowctl'>
      <div id='contentsmall' class='content'>
      </div>
    </div>
  </div>
</div>

In my previous question, I somehow had an example where height and max-height worked differently, but I think the above better shows what I actually want, and the problem boils down to the same thing. The ugly solution would be, to get rid of the .overflowctl, but then my nice .frame (in reality the colors are better...^^) doesn't work anymore with the padding and also the combination of scroll bar and border-radius. I will add it at the end, just to show it, anyways. I would be happy about any hints how I can achieve my goal! :)

#contentsmall {
  height: 50px;
}

#contentbig {
  height: 350px;
}

.content {
  background-image: linear-gradient(30deg, #005500 0%, #9922ee 100%);
  width: 120px;
}

.frame {
  overflow-y: auto;
  overflow-x: hidden;
}

.frame {
  height: auto;
  max-height: calc( 100% - 40px );
  width: 120px;
  border-radius: 15px;
  padding: 20px;
  background: #221100;
}

.canvas {
  display: inline-block;
  height: 250px;
  width: 200px;
  margin-right: 50px;
  background: #ddccaa;
  vertical-align: top;
}
<div class='canvas'>
  <div class='frame'>
    <div id='contentbig' class='content'>
    </div>
  </div>
</div>

<div class='canvas'>
  <div class='frame'>
    <div id='contentsmall' class='content'>
    </div>
  </div>
</div>


Solution

  • Simply add this

    .frame {
      display:grid;
      grid-template-rows:1fr;
    }
    

    This will give a reference for your percentage height.

    Full code

    .overflowctl {
      height: auto;
      max-height: 100%;
    }
    
    #contentsmall {
      height: 50px;
    }
    
    #contentbig {
      height: 350px;
    }
    
    .content {
      background-image: linear-gradient(30deg, #005500 0%, #9922ee 100%);
      width: 120px;
    }
    
    .overflowctl {
      overflow-y: auto;
      overflow-x: hidden;
    }
    
    .frame {
      height: auto;
      max-height: calc( 100% - 40px );
      width: 120px;
      border-radius: 15px;
      padding: 20px;
      background: #221100;
      display:grid;
      grid-template-rows:1fr;
    }
    
    .canvas {
      display: inline-block;
      height: 250px;
      width: 200px;
      margin-right: 50px;
      background: #ddccaa;
      vertical-align: top;
    }
    <div class='canvas'>
      <div class='frame'>
        <div id='ctlbig' class='overflowctl'>
          <div id='contentbig' class='content'>
          </div>
        </div>
      </div>
    </div>
    
    <div class='canvas'>
      <div class='frame'>
        <div id='ctlsmall' class='overflowctl'>
          <div id='contentsmall' class='content'>
          </div>
        </div>
      </div>
    </div>

    CSS grid and Flexbox have some special behavior related to percentage height.

    Related questions to get more details:

    Why does `height: 100%` value 'work' for child tags of grid-items?

    Percentage 'min-height' works only when element has indirect parent with 'display: flex'