Categories
aspect-ratio css html responsive-design

Maintain the aspect ratio of a div with CSS

1325

I want to create a div that can change its width/height as the window’s width changes.

Are there any CSS3 rules that would allow the height to change according to the width, while maintaining its aspect ratio?

I know I can do this via JavaScript, but I would prefer using only CSS.

div keeping aspect ratio according to width of window

8

  • 4

    I wrote about different approaches to this in my CSS-Tricks article on scaling responsive animations

    Jan 5, 2017 at 14:45

  • 11

    I’ve come to this Q&A very late, but I must comment. Although it is very disciplined and within the SO rules of everyone answering to try their best to satisfy the “only CSS” demand of the question, it is quite surprising that with the exception of the answer by user007 low down on the page, no-one has mentioned the obvious: no reliable CSS-only solution exists, this problem requires Javascript. A newbie coming to this page could waste valuable time switching from one set of pros/cons to another before the lightbulb goes off: CSS-only won’t do.

    Nov 13, 2017 at 21:24


  • 1

    I wrote a little tool that makes it easy to preview and calculate aspect ratios, aspectrat.io. It also generates the the appropriate CSS/Sass/Less code that you can copy and paste into your projects. Hope people find it useful.

    Mar 8, 2018 at 22:41

  • 3

    This question is for setting height based on width. If you need the opposite, check out Setting Element Width Based on Height Via CSS.

    Jan 26, 2019 at 14:00


  • 3

    Css new property aspect ratio is now available. web.dev/aspect-ratio

    – AdamKniec

    Jan 30, 2021 at 8:10


1524

Just create a wrapper <div> with a percentage value for padding-bottom, like this:

.demoWrapper {
  padding: 10px;
  background: white;
  box-sizing: border-box;
  resize: horizontal;
  border: 1px dashed;
  overflow: auto;
  max-width: 100%;
  height: calc(100vh - 16px);
}

div {
  width: 100%;
  padding-bottom: 75%;
  background: gold; /** <-- For the demo **/
}
<div class="demoWrapper">
  <div></div>
</div>

It will result in a <div> with height equal to 75% of the width of its container (a 4:3 aspect ratio).

This relies on the fact that for padding :

The percentage is calculated with respect to the width of the generated box’s containing block […] (source: w3.org, emphasis mine)

Padding-bottom values for other aspect ratios and 100% width :

aspect ratio  | padding-bottom value
--------------|----------------------
    16:9      |       56.25%
    4:3       |       75%
    3:2       |       66.66%
    8:5       |       62.5%

Placing content in the div :

In order to keep the aspect ratio of the div and prevent its content from stretching it, you need to add an absolutely positioned child and stretch it to the edges of the wrapper with:

div.stretchy-wrapper {
  position: relative;
}

div.stretchy-wrapper > div {
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
}

Here’s a demo and another more in depth demo

4

  • 33

    @schellmax, it is because padding % is calculated relative to the current element’s width, where as height % is calculated relative to the parent element’s height. Furthermore, absolute positions are calculated relative to the outside container of an element, which includes the padding area. For more information Google “CSS Box Model”

    – Anson Kao

    Sep 17, 2012 at 20:33

  • 17

    This does not seem to work in a nested fashion. It works at the first level, but when trying to do the same thing inside of the div maintaining aspect ratio, the padding-bottom percentage seems to get applied to the width of the parent. here is an example where the .stretchy-wrap.onethird padding-bottom of 25% is actually 25% of the parent width. Can someone explain this?

    Feb 8, 2013 at 15:53

  • 4

    This answer is outdated as of 2021, see @Tiago’s answer.

    – Eneko

    Aug 31, 2021 at 16:21

  • 1

    If you can’t use aspect-ratio from 2021, here is an alternative solution: codepen.io/bagermen/pen/rNzOxMQ

    Oct 14, 2021 at 8:06


530

There are several ways to specify a fixed aspect ratio on an element like a div, here are 2 of them:

1. The aspect-ratio CSS property (new)

div {
  background: teal;
  width: 50%;
  aspect-ratio: 1 / 1;
}
<div>aspect-ratio: 1 / 1;</div>

This is the most simple and flexible solution. It directly specifies a fixed width to height (or height to width) aspect ratio for an element. This means you can also specify an aspect ratio according to the elements height.
It doesn’t rely on the parent width (like the padding technique) or the viewport size (like the following vw unit technique) it relies on the element’s own width or height More info on MDN. That is what make it so powerfull compared to other workarounds.

This is a modern property (2021). All modern browsers support it, see caniuse for precise browser support.

Here are a few examples with different aspect ratios :

.ar-1-1  {aspect-ratio: 1 / 1;}
.ar-3-2  {aspect-ratio: 3 / 2;}
.ar-4-3  {aspect-ratio: 4 / 3;}
.ar-16-9 {aspect-ratio: 16 / 9;}
.ar-2-3  {aspect-ratio: 2 / 3;}
.ar-3-4  {aspect-ratio: 3 / 4;}
.ar-9-16 {aspect-ratio: 9 / 16;}


/** For the demo : **/
body {
  display:flex;
  flex-wrap:wrap;
  align-items:flex-start;
}
div {
  background: teal;
  width: 23%;
  margin:1%;
  padding:20px 0;
  color:#fff;
  text-align:center;
}
<div class="ar-1-1">aspect-ratio: 1 / 1;</div>
<div class="ar-3-2">aspect-ratio: 3 / 2;</div>
<div class="ar-4-3">aspect-ratio: 4 / 3;</div>
<div class="ar-16-9">aspect-ratio: 16 / 9;</div>
<div class="ar-2-3">aspect-ratio: 2 / 3;</div>
<div class="ar-3-4">aspect-ratio: 3 / 4;</div>
<div class="ar-9-16">aspect-ratio: 9 / 16;</div>

2. Using vw units:

You can use vw units for both the width and height of the element. This allows the element’s aspect ratio to be preserved, based on the viewport width.

vw : 1/100th of the width of the viewport. [MDN]

Alternatively, you can also use vh for viewport height, or even vmin/vmax to use the lesser/greater of the viewport dimensions (discussion here).

Example: 1:1 aspect ratio

div {
  width: 20vw;
  height: 20vw;
  background: gold;
}
<div></div>

For other aspect ratios, you can use the following table to calculate the value for height according to the width of the element :

aspect ratio  |  multiply width by
-----------------------------------
     1:1      |         1
     1:3      |         3
     4:3      |        0.75
    16:9      |       0.5625

Example: 4×4 grid of square divs

body {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
div {
  width: 23vw;
  height: 23vw;
  margin: 0.5vw auto;
  background: gold;
}
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>

Here is a Fiddle with this demo and here is a solution to make a responsive grid of squares with verticaly and horizontaly centered content.


Browser support for vh/vw units is IE9+ see canIuse for more info

0

    134

    2022 solution – use the aspect-ratio CSS property

    <div class="demo"></div>
    
    .demo {
      background: black;
      width: 500px;
      aspect-ratio: 4/3;
    }
    

    Update: this solution is now supported by all evergreen browsers

    4

    • Note: When you only use the width property with 100% as value, this width will be calculated, so that it won’t fill the horizontal space. When you want to preserve the 100% width, you have to add both width and height with each 100% set. That way it will work (at least in the latest Chrome it works correctly).

      Oct 8, 2021 at 11:29

    • 3

      All evergreen browsers support this now 🎉

      – Mendy

      Nov 15, 2021 at 18:44

    • As of 2022, if you don’t have to support IE, this is the best way.

      Apr 6 at 20:02

    • Overwolf is still using a version of Chromium that does not support aspec-ratio. Instead you can use something like height: calc(100vw * 4 / 3);

      – Slion

      Jul 21 at 5:17