24

I'm trying to create a fixed aspect ratio box that resizes to not overflow its parent.

The classic padding-bottom trick is only able to define height in terms of parent width, and as testable here allows the element to grow taller than the parent as width increases. Not good.

Using vh and vw however, we can accomplish what we want with the restriction that the parent is the dimensions of the viewport. Testable here.

<style>
  div {
    max-width: 90vw;
    max-height: 90vh;
    height: 50.625vw; /* height defined in terms of parent width (90*9/16) */
    width: 160vh; /* width defined in terms of parent height, which is missing from the padding-bottom trick (90*16/9) */
    background: linear-gradient(to right, gray, black, gray);
  }
</style>

<div></div>

Is there a way to have a vh and vw equivalent that references the parent instead of the viewport? Or is there a complimentary trick to the padding-bottom trick which fixes its problems? Where is the css ratio property?

Also, images have some sort of intrinsic ratio, but I'm unsure how to take advantage of this.

Community
  • 1
  • 1
Tet
  • 389
  • 1
  • 2
  • 7
  • I don't understand why the padding trick isn't good enough for you? It is a fixed aspect ratio box. Are you saying you want the same ratio to the viewport but within the parent? – Donnie D'Amato Aug 29 '16 at 01:22
  • 1
    I want the box to be restricted by the parent's height and width. It shouldn't become larger than the parent in either dimension. – Tet Aug 29 '16 at 01:26

1 Answers1

3

You can use something similar to what I did in Maintain aspect ratio with a fixed height, which takes advantage the intrinsic aspect ratio of <canvas> elements.

But here we need nested containers with two canvas.

#resize {
  resize: both;
  overflow: auto;
  width: 100px;
  height: 140px;
  padding: 20px;
  border: 1px solid black;
}
.ratio {
  position: relative;
  display: inline-block;
  vertical-align: middle;
}
.ratio.vertical, .ratio.vertical > canvas {
  height: 100%;
  max-width: 100%;
}
.ratio.horizontal, .ratio.horizontal > canvas {
  width: 100%;
  max-height: 100%;
}
.ratio > div {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
#contents {
  background: linear-gradient(to right, gray, black, gray);
}
<div id="resize">
  <div class="ratio vertical">
    <canvas width="16" height="9"></canvas>
    <div>
      <div class="ratio horizontal">
        <canvas width="16" height="9"></canvas>
        <div id="contents">
          Hello
        </div>
      </div>
    </div>
  </div>
</div>
Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Aargh, doesn't work on Chrome. I guess the element need to be forced to rerender, but `resize` event listeners only work on the window. But notice it works when you load the window, independently of the sizes of `#resize` – Oriol Aug 29 '16 at 03:02