37

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?

web-tiki
  • 99,765
  • 32
  • 217
  • 249
Fijjit
  • 1,399
  • 2
  • 17
  • 31
  • See if this helps: http://www.mademyday.de/css-height-equals-width-with-pure-css.html – Dănuț Mihai Florian Oct 18 '14 at 10:08
  • And a 'so' answer that may be useful: [height-equal-to-dynamic-width-css-fluid-layout](https://stackoverflow.com/questions/5445491/height-equal-to-dynamic-width-css-fluid-layout/6615994#6615994). also: [css-only-proportional-resizing-of-elements](http://wellcaffeinated.net/articles/2012/12/10/very-simple-css-only-proportional-resizing-of-elements/) – Ryan Vincent Oct 18 '14 at 10:16
  • possible duplicate of [Sizing width of an element as a percentage of its height or vice versa in a fluid design?](http://stackoverflow.com/questions/17569322/sizing-width-of-an-element-as-a-percentage-of-its-height-or-vice-versa-in-a-flui) – l'L'l Oct 18 '14 at 10:45
  • 2
    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 Oct 18 '14 at 11:05

7 Answers7

27

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.

Salman A
  • 262,204
  • 82
  • 430
  • 521
  • 1
    Loading an image is fine for this interesting situation. I have resorted to using the same sized spacer.gifs, with responsive css, and various sized images for background swapping when the viewport got larger. It was fast and effective. – Christina Oct 18 '14 at 13:54
  • Wow - fantastic solution, thank you! I always avoid adding unnecessary markup, but this requirement is important enough to make it worth it on this occasion. Marked as answer. – Fijjit Oct 20 '14 at 09:45
  • 1
    When changing height of div.box in runtime, width doesn't change accordingly in Chrome 56. – Roman Dec 02 '16 at 05:46
  • 1
    It should be noted that the above demo doesn't work in Edge/IE on resizing the page – only on refresh. – Paracetamol Mar 28 '17 at 12:53
  • This is GENIUS! Thank you. – DanMad Dec 11 '17 at 04:05
  • 3
    one could replace the img element with `` or whatever dimensions – Apolo Aug 07 '18 at 09:36
  • Someone have to edit this post and undo the accepted answer. It is the end of 2021 and better solution is to use `aspect-ratio` css property as answered down the bottom of the thread already. Imagine people coming here and jumping straight to the accepted, most upvoted answer ‍♂️ – avepr Aug 26 '21 at 05:08
20

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>
web-tiki
  • 99,765
  • 32
  • 217
  • 249
  • Nice! That's smart! It's not exactly the question, but could be solution. – tzi Oct 18 '14 at 10:19
  • 10
    I'm not sure if this answers the question. The OP clearly stated `maintain the width of an element as a percentage of its height` not the height of the viewport. – Hashem Qolami Oct 18 '14 at 10:19
  • @HashemQolami yes, this will work only if the height of the element is set according to the viewport height. But I am not aware of any other css solution to do this. – web-tiki Oct 18 '14 at 10:21
10

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)

Daniel
  • 1,431
  • 2
  • 16
  • 36
7

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.

tzi
  • 8,719
  • 2
  • 25
  • 45
5

As of 2021 there is a property called aspect-ratio. Most browsers 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

Mandera
  • 2,647
  • 3
  • 21
  • 26
0

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!

Neal Ehardt
  • 10,334
  • 9
  • 41
  • 51
0

2023 Answer

.some-parent-with-changing-height {
   position: fixed; 
   bottom: 0; 
   top: 0; 
   left: 0; 
   right: 0; 
}

.element-that-keeps-ratio {
   background-color: blue; 
   height: 80%; 
   aspect-ratio: 1 / 1;
}
<div class="some-parent-with-changing-height">
   <div class="element-that-keeps-ratio">
   </div>
</div>
Ben Carp
  • 24,214
  • 9
  • 60
  • 72