226

When I give an image a percent width or height only it will grow/shrink keeping its aspect ratio, but if I want the same effect with another element, is it possible at all to tie the width and the height together using percentage?

alex
  • 479,566
  • 201
  • 878
  • 984
ilyo
  • 35,851
  • 46
  • 106
  • 159

5 Answers5

544

You can do this using pure CSS; no JavaScript needed. This utilizes the (somewhat counterintuitive) fact that padding-top percentages are relative to the containing block's width. Here's an example:

.wrapper {
  width: 50%;
  /* whatever width you want */
  display: inline-block;
  position: relative;
}
.wrapper:after {
  padding-top: 56.25%;
  /* 16:9 ratio */
  display: block;
  content: '';
}
.main {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  /* fill parent */
  background-color: deepskyblue;
  /* let's see it! */
  color: white;
}
<div class="wrapper">
  <div class="main">
    This is your div with the specified aspect ratio.
  </div>
</div>
Chris
  • 26,544
  • 5
  • 58
  • 71
  • 28
    i have to say this is awesome, and it even work on IE. big +1 – Peter Aug 25 '12 at 11:03
  • It surely is, never would have though of that :) but why did you delete your jQuery answer? it could be useful too – ilyo Aug 25 '12 at 11:39
  • @Abody97 why would the `padding-top` be related to the width of the element? – ilyo Aug 25 '12 at 21:08
  • 2
    @IlyaD Take a look at this from the CSS2 specification: [little link](http://www.w3.org/TR/CSS21/box.html#propdef-padding-top). – Chris Aug 25 '12 at 21:24
  • Thanks! Is it possible to make it vice versa: let the width depend on the height? – Andrej Jul 08 '13 at 11:37
  • 8
    You, sir, have won the Internet. This is huge, especially for background images on responsive designs. Thank you! – Vidal Quevedo Oct 11 '13 at 23:27
  • 50
    For the mathematically impaired like me: to calculate the padding-top percentage of something else than 16:9 ratios, use this formula (for example using a 22:5 ratio where 22 is A and 5 is B): B / (A / 100) = C%. So 22:5 is 5 / .22 = 22.72%. – Dreamdealer Mar 14 '14 at 13:21
  • To those, like me, who hate the fiddles of CSS and JavaScript, this is - I believe - the best solution you'll find (and I've just wasted a morning looking), including within ASP.NET MVC. Many thanks. – Andy Brown Apr 25 '14 at 11:26
  • This isn't exact. It's actually off by 50-200 pixels for those looking for an EXACT aspect ratio. – bryan Jun 02 '14 at 20:20
  • @bryan: Good luck finding anything that isn't an approximation. – BoltClock Dec 05 '14 at 16:38
  • @Chris, may I ask you to have a look at a responsive design related question here : http://tinyurl.com/nadfh2u ? – Istiaque Ahmed Apr 12 '15 at 15:00
  • If html had been designed to only display squares, there wouldn't be web developers, but everyone at home would know the few commands to color and size a square. I don't know why everyone assumes an "element" is just a colored square. In this case, there can't be content in there with all the padding feeling the box. An extra abs-pos element could not contain text normally since it would overflow once it reaches the end. In other words, it won' grow with it. I won't vote it up because it annoys me that I can't even vote it down (since everyone else seem to like colorful squares) – sergio Jul 19 '15 at 18:24
  • Amazing solution to the problem that works brilliantly, wish i could upvote more – Addzy Aug 17 '15 at 13:59
  • 1
    How to set vertically center `
    ` from body ?
    – Krunal Mevada Sep 21 '15 at 06:42
  • What if I don't want an absolute position? – Rodrigo Ruiz Dec 02 '15 at 07:49
  • Just FYI: Bootstrap 3.2+ uses this approach with classes `embed-responsive` (`wrapper` in this answer) and `embed-responsive-item` (`main` here) it has 2 additional classes like `embed-responsive-16by9` and `embed-responsive-4by3` which has to be added to the wrapper div to determine the required proportions. – biesior Feb 23 '16 at 09:58
  • 1
    Is the display:inline-block necessary? – Trevor Mar 15 '16 at 22:46
  • The only thing I would change here is the "display: block;" on the .wrapper class.. The reason is that inline-block elements produce a few pixel of blank (vertical) space after the element. – dBlaze Oct 28 '16 at 13:00
46

Bumming off Chris's idea, another option is to use pseudo elements so you don't need to use an absolutely positioned internal element.

<style>
.square {
    /* width within the parent.
       can be any percentage. */
    width: 100%;
}
.square:before {
    content: "";
    float: left;

    /* essentially the aspect ratio. 100% means the
       div will remain 100% as tall as it is wide, or
       square in other words.  */
    padding-bottom: 100%;
}
/* this is a clearfix. you can use whatever
   clearfix you usually use, add
   overflow:hidden to the parent element,
   or simply float the parent container. */
.square:after {
    content: "";
    display: table;
    clear: both;
}
</style>
<div class="square">
  <h1>Square</h1>
  <p>This div will maintain its aspect ratio.</p>
</div>

I've put together a demo here: http://codepen.io/tcmulder/pen/iqnDr


EDIT:

Now, bumming off of Isaac's idea, it's easier in modern browsers to simply use vw units to force aspect ratio (although I wouldn't also use vh as he does or the aspect ratio will change based on window height).

So, this simplifies things:

<style>
.square {
    /* width within the parent (could use vw instead of course) */
    width: 50%;
    /* set aspect ratio */
    height: 50vw;
}
</style>
<div class="square">
  <h1>Square</h1>
  <p>This div will maintain its aspect ratio.</p>
</div>

I've put together a modified demo here: https://codepen.io/tcmulder/pen/MdojRG?editors=1100

You could also set max-height, max-width, and/or min-height, min-width if you don't want it to grow ridiculously big or small, since it's based on the browser's width now and not the container and will grow/shrink indefinitely.

Note you can also scale the content inside the element if you set the font size to a vw measurement and all the innards to em measurements, and here's a demo for that: https://codepen.io/tcmulder/pen/VBJqLV?editors=1100

Tomas Mulder
  • 2,207
  • 2
  • 16
  • 18
31
<style>
#aspectRatio
{
  position:fixed;
  left:0px;
  top:0px;
  width:60vw;
  height:40vw;
  border:1px solid;
  font-size:10vw;
}
</style>
<body>
<div id="aspectRatio">Aspect Ratio?</div>
</body>

The key thing to note here is vw = viewport width, and vh = viewport height

Isaac
  • 11,409
  • 5
  • 33
  • 45
3

That's my solution

<div class="main" style="width: 100%;">
    <div class="container">
        <div class="sizing"></div>
        <div class="content"></div>
    </div>
</div>

.main {
    width: 100%;
}
.container {
    width: 30%;
    float: right;
    position: relative;
}
.sizing {
    width: 100%;
    padding-bottom: 50%;
    visibility: hidden;
}
.content {
    width: 100%;
    height: 100%;
    background-color: red;
    position: absolute;
    margin-top: -50%;
}

http://jsfiddle.net/aG4Fs/3/

Mykyta Shyrin
  • 312
  • 1
  • 14
2
(function( $ ) {
  $.fn.keepRatio = function(which) {
      var $this = $(this);
      var w = $this.width();
      var h = $this.height();
      var ratio = w/h;
      $(window).resize(function() {
          switch(which) {
              case 'width':
                  var nh = $this.width() / ratio;
                  $this.css('height', nh + 'px');
                  break;
              case 'height':
                  var nw = $this.height() * ratio;
                  $this.css('width', nw + 'px');
                  break;
          }
      });

  }
})( jQuery );      

$(document).ready(function(){
    $('#foo').keepRatio('width');
});

Working example: http://jsfiddle.net/QtftX/1/

Peter
  • 16,453
  • 8
  • 51
  • 77
  • This is great! I am using this script, however, if I use it on multiple DIV's and they each have different heights...once the script kicks in, it adjusts all heights to be the same...I cannot figure out why! Any ideas? I'd like to maintain the aspect ratio like this script does, but maintain the different heights for each. – Michael Giovanni Pumo Oct 02 '13 at 09:52
  • 2
    Because the function applied for a set of elements. The script should handle each element separately. Here is the updated code: http://jsfiddle.net/QtftX/100/ – onetdev Nov 23 '13 at 10:41
  • If you refresh the page the width and height resets again.. How can you block this? – Gilko Apr 24 '14 at 15:05
  • This doesn't work on iOS – bryan Jun 02 '14 at 20:19