33
votes

I need to maintain the width of an element as a percentage of its height. So as the height changes, the width is updated.

The opposite is achievable by using a % value for padding-top, but padding-left as a percentage will be a percentage of the width of an object, not its height.

So with markup like this:

<div class="box">
  <div class="inner"></div>
</div>

I'd like to use something like this:

.box {
    position: absolute;
    margin-top: 50%;
    bottom: 0;
}
.inner {
    padding-left: 200%;
}

To ensure the box's aspect ratio is maintained according to it's height. The height is fluid because of it's % margin - as the window's height changes, the box's height will too.

I know how to achieve this with JavaScript, just wondering if there's a clean CSS-only solution?

6
Thanks - I did spot those similar questions, but some refer to ratio the wrong way round and anyway none of the answers helped. I I thought it would be useful to have a more specific question relating only to width as a percentage of height.Fijjit

6 Answers

28
votes

You can use an image that has the desired proportions as to help with proportional sizing (images can be scaled proportionally by setting one dimension to some value and other to auto). The image does not have to be visible, but it must occupy space.

.box {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 50%;
}
.size-helper {
  display: block;
  width: auto;
  height: 100%;
}
.inner {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(255, 255, 153, .8);
}
<div class="box">
  <img class="size-helper" src="//dummyimage.com/200x100/999/000" width="200" height="100">
  <div class="inner">
    1. box has fluid height<br>
    2. img has 2:1 aspect ratio, 100% height, auto width, static position<br>
    2.1 it thus maintains width = 200% of height<br>
    2.2 it defines the dimensions of the box<br>
    3. inner expands as much as box
  </div>
</div>

In the above example, box, inner and helper are all same size.

19
votes

You can use vh units for both height and width of your element so they both change according to the viewport height.

vh 1/100th of the height of the viewport. (MDN)

DEMO

.box {
    position: absolute;
    height:50vh;
    width:100vh;
    bottom: 0;
    background:teal;
}
<div class="box"></div>
7
votes

The CSS trick you wrote, works pretty well to keep ratio width / height on an element. It is based on the padding property that, when its value is in percent, is proportional to parent width, even for padding-top and padding-bottom.

There is no CSS property that could set an horizontal sizing proportionally to the parent height. So I think there is no clean CSS solution.

4
votes

There is another, more efficient way to achieve constant aspect ratio according to height.

You can place an empty svg so you dont have to load an external image.

HTML code:

    <svg xmlns="http://www.w3.org/2000/svg"
      height="100"
      width="200"
      class='placeholder-svg'
    />

CSS code:

.placeholder-svg {
  width: auto;
  height: 100%;
}

Change width/height to achieve desired aspect ratio.

Keep in mind, the svg might overflow.

http://www.w3.org/2000/svg is just a namespace. It doesn't load anything.

If you change placeholder-svg class to:

.placeholder-svg {
  width: 100%;
  height: auto;
}

then height is adjusted according to width.

Demo 1 Width is adjusted according to height and 2:1 aspect ratio.

Demo 2 same as above, but you can resize easily (uses React)

0
votes

I can't find a pure CSS solution. Here's a solution using CSS Element Queries JavaScript library.

var aspectRatio = 16/9;
var element = document.querySelector('.center');
function update() {
  element.style.width = (element.clientHeight * aspectRatio) + 'px';
}

new ResizeSensor(element, update);
update();

CodePen demo!

0
votes

As of 2021 there is a new property called aspect-ratio. Most browsers already support it

div {
    border: 1px solid;
    margin: 8px;
}

.box {
    width: 100px;
    min-height: 100px;
    resize: horizontal;
    overflow: auto;
}

.inner1 {
    aspect-ratio: 1/1;
}

.inner2 {
    aspect-ratio: 3/1;
}
<div class="box">
  <div class="inner1"></div>
  <div class="inner2"></div>
</div>

Run this snippet and resize the outer div manually to see the inner divs behavior