[html] Maintain the aspect ratio of a div with CSS

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

This question is related to html css responsive-design aspect-ratio

The answer is


2020 solution - using Grid and padding of pseudo element

I have found some more fresh way to solve this case. This solution is a descendant of a any padding-bottom method, but without any position: absolute children, just using display: grid; and pseudo element.

Here we have .ratio::before with good old padding-bottom: XX% and grid-area: 1 / 1 / 1 / 1;, which forces the pseudo element to keep the position in grid. Although here we have width: 0; to prevent overflowing main element by this one (we cold use z-index here, but this one is shorter).

And our main element .ratio > *:first-child has the same position as .ratio::before, which is grid-area: 1 / 1 / 1 / 1;, so they both shares the same grid cell place. Now we can put any content in our div, and the pseudo element is the one who determines the width/height ratio. More about grid-area.

_x000D_
_x000D_
.ratio {
  display: grid;
  grid-template-columns: 1fr;
  max-width: 200px; /* just for instance, can be 100% and depends on parent */  
}

.ratio::before {
  content: '';
  width: 0;
  padding-bottom: calc(100% / (16/9)); /* here you can place any ratio */
  grid-area: 1 / 1 / 1 / 1;
}

.ratio>*:first-child {
  grid-area: 1 / 1 / 1 / 1; /* the same as ::before */
  background: rgba(0, 0, 0, 0.1); /* just for instance */
}
_x000D_
<div class="ratio">
  <div>16/9</div>
</div>
_x000D_
_x000D_
_x000D_


Although you can use CSS val and place you ratio in HTML using style attribute. Works with display: inline-grid as well.

_x000D_
_x000D_
.ratio {
  display: inline-grid;
  grid-template-columns: 1fr;
  width: 200px; /* just for instance, can be 100% and depends on parent */ 
  margin-right: 10px; /* just for instance */
}

.ratio::before {
  content: '';
  width: 0;
  padding-bottom: calc(100% / (var(--r))); /* here you can place any ratio */
  grid-area: 1 / 1 / 1 / 1;
}

.ratio>*:first-child {
  grid-area: 1 / 1 / 1 / 1; /* the same as ::before */
  background: rgba(0, 0, 0, 0.1); /* just for instance */
}
_x000D_
<div class="ratio" style="--r: 4/3;">
  <div>4/3</div>
</div>

<div class="ratio" style="--r: 16/9;">
  <div>16/9</div>
</div>
_x000D_
_x000D_
_x000D_


Basing on your solutions I've made some trick:

When you use it, your HTML will be only

<div data-keep-ratio="75%">
    <div>Main content</div>
</div>

To use it this way make: CSS:

*[data-keep-ratio] {
    display: block;
    width: 100%;
    position: relative;
}
*[data-keep-ratio] > * {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}

and js (jQuery)

$('*[data-keep-ratio]').each(function(){ 
    var ratio = $(this).data('keep-ratio');
    $(this).css('padding-bottom', ratio);
});

And having this you just set attr data-keep-ratio to height/width and that's it.


I'd like to share my solution, where I have an img-tag filling a certain aspect ratio. I couldn't use background because of lack of support of the CMS and I'd not prefer to use a style tag like so: <img style="background:url(...)" />. Also, the width is 100%, so it doesn't need to be set at a fixed size as in some of the solutions. It will scale responsively!

_x000D_
_x000D_
.wrapper {_x000D_
  width: 50%;_x000D_
}_x000D_
_x000D_
.image-container {_x000D_
  position: relative;_x000D_
  width: 100%;_x000D_
}_x000D_
_x000D_
.image-container::before {_x000D_
  content: "";_x000D_
  display: block;_x000D_
}_x000D_
_x000D_
.image-container img {_x000D_
  position: absolute;_x000D_
  top: 0;_x000D_
  left: 0;_x000D_
  width: 100%;_x000D_
  height: 100%;_x000D_
  object-fit: cover;_x000D_
}_x000D_
_x000D_
.ratio-4-3::before {_x000D_
  padding-top: 75%;_x000D_
}_x000D_
_x000D_
.ratio-3-1::before {_x000D_
  padding-top: calc(100% / 3);_x000D_
}_x000D_
_x000D_
.ratio-2-1::before {_x000D_
  padding-top: 50%;_x000D_
}
_x000D_
<div class="wrapper"> <!-- Just to make things a bit smaller -->_x000D_
  <p>_x000D_
  Example of an 4:3 aspect ratio, filled by an image with an 1:1 ratio._x000D_
  </p>_x000D_
  <div class="image-container ratio-4-3"> <!-- Lets go for a 4:3 aspect ratio -->_x000D_
    <img src="https://placekitten.com/1000/1000/" alt="Kittens!" />_x000D_
  </div>_x000D_
  <p>_x000D_
  Just place other block elements around it; it will work just fine._x000D_
  </p>_x000D_
</div>
_x000D_
_x000D_
_x000D_


Elliot inspired me to this solution - thanks:

aspectratio.png is a completely transparent PNG-file with the size of your preferred aspect-ratio, in my case 30x10 pixels.

HTML

<div class="eyecatcher">
  <img src="/img/aspectratio.png"/>
</div>

CSS3

.eyecatcher img {
  width: 100%;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url(../img/autoresized-picture.jpg);
}

Please note: background-size is a css3-feature which might not work with your target-browsers. You may check interoperability (f.e. on caniuse.com).


You can achieve that by using SVG.

It depends on a case, but in some it is really usefull. As an example - you can set background-image without setting fixed height or use it to embed youtube <iframe> with ratio 16:9 and position:absolute etc.

For 3:2 ratio set viewBox="0 0 3 2" and so on.

Example:

_x000D_
_x000D_
div{_x000D_
    background-color:red_x000D_
}_x000D_
svg{_x000D_
    width:100%;_x000D_
    display:block;_x000D_
    visibility:hidden_x000D_
}_x000D_
_x000D_
.demo-1{width:35%}_x000D_
.demo-2{width:20%}
_x000D_
<div class="demo-1">_x000D_
  <svg viewBox="0 0 3 2"></svg>_x000D_
</div>_x000D_
_x000D_
<hr>_x000D_
_x000D_
<div class="demo-2">_x000D_
  <svg viewBox="0 0 3 2"></svg>_x000D_
</div>
_x000D_
_x000D_
_x000D_


I have run into this issue quite some times, so I made a JS solution for it. This basically adjust the height of the domElement according the width of the element by the ratio you specify. You could use it as followed:

<div ratio="4x3"></div>

Please be aware that since it is setting the height of the element, the element should be either a display:block or display:inline-block.

https://github.com/JeffreyArts/html-ratio-component


You can use an svg. Make the container/wrapper position relative, put the svg first as staticly positioned and then put absolutely positioned content (top: 0; left:0; right:0; bottom:0;)

Example with 16:9 proportions:

image.svg: (can be inlined in src)

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 9" width="16" height="9"/>

CSS:

.container {
  position: relative;
}
.content {
  position: absolute;
  top:0; left:0; right:0; bottom:0;
}

HTML:

<div class="container">
  <img style="width: 100%" src="image.svg" />
  <div class="content"></div>
</div>

Note that inline svg doesn't seem to work, but you can urlencode the svg and embed it in img src attribute like this:

<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%209%22%20width%3D%2216%22%20height%3D%229%22%2F%3E" style="width: 100%;" />

This is an improvement on the accepted answer:

  • Uses pseudo elements instead of wrapper divs
  • The aspect ratio is based on the width of the box instead of its parent
  • The box will stretch vertically when the content becomes taller

_x000D_
_x000D_
.box {_x000D_
  margin-top: 1em;_x000D_
  margin-bottom: 1em;_x000D_
  background-color: #CCC;_x000D_
}_x000D_
_x000D_
.fixed-ar::before {_x000D_
  content: "";_x000D_
  float: left;_x000D_
  width: 1px;_x000D_
  margin-left: -1px;_x000D_
}_x000D_
.fixed-ar::after {_x000D_
  content: "";_x000D_
  display: table;_x000D_
  clear: both;_x000D_
}_x000D_
_x000D_
.fixed-ar-16-9::before {_x000D_
  padding-top: 56.25%;_x000D_
}_x000D_
.fixed-ar-3-2::before {_x000D_
  padding-top: 66.66%;_x000D_
}_x000D_
.fixed-ar-4-3::before {_x000D_
  padding-top: 75%;_x000D_
}_x000D_
.fixed-ar-1-1::before {_x000D_
  padding-top: 100%;_x000D_
}_x000D_
_x000D_
.width-50 {_x000D_
  display: inline-block;_x000D_
  width: 50%;_x000D_
}_x000D_
.width-20 {_x000D_
  display: inline-block;_x000D_
  width: 20%;_x000D_
}
_x000D_
<div class="box fixed-ar fixed-ar-16-9">16:9 full width</div>_x000D_
<hr>_x000D_
<div class="box fixed-ar fixed-ar-16-9 width-50">16:9</div>_x000D_
<hr>_x000D_
<div class="box fixed-ar fixed-ar-16-9 width-20">16:9</div>_x000D_
<div class="box fixed-ar fixed-ar-3-2 width-20">3:2</div>_x000D_
<div class="box fixed-ar fixed-ar-4-3 width-20">4:3</div>_x000D_
<div class="box fixed-ar fixed-ar-1-1 width-20">1:1</div>_x000D_
<hr>_x000D_
<div class="box fixed-ar fixed-ar-16-9 width-20">16:9</div>_x000D_
<div class="box fixed-ar fixed-ar-16-9 width-50">16:9</div>
_x000D_
_x000D_
_x000D_


I've found a way to do this using CSS, but you have to be careful as it may change depending on the flow of your own web site. I've done it in order to embed video with a constant aspect ratio within a fluid width portion of my web site.

Say you have an embedded video like this:

<object>
     <param ... /><param ... />...
     <embed src="..." ...</embed>
</object>

You could then place this all inside a div with a "video" class. This video class will probably be the fluid element in your website such that, by itself, it has no direct height constraints, but when you resize the browser it will change in width according to the flow of the web site. This would be the element you are probably trying to get your embedded video in while maintaining a certain aspect ratio of the video.

In order to do this, I put an image before the embedded object within the "video" class div.

!!! The important part is that the image has the correct aspect ratio you wish to maintain. Also, make sure the size of the image is AT LEAST as big as the smallest you expect the video (or whatever you are maintaining the A.R. of) to get based on your layout. This will avoid any potential issues in the resolution of the image when it is percentage-resized. For example, if you wanted to maintain an aspect ratio of 3:2, don't just use a 3px by 2px image. It may work under some circumstances, but I haven't tested it, and it would probably be a good idea to avoid.

You should probably already have a minimum width like this defined for fluid elements of your web site. If not, it is a good idea to do so in order to avoid chopping elements off or having overlap when the browser window gets too small. It is better to have a scroll bar at some point. The smallest in width a web page should get is somewhere around ~600px (including any fixed width columns) because screen resolutions don't come smaller unless you are dealing with phone-friendly sites. !!!

I use a completely transparent png but I don't really think it ends up mattering if you do it right. Like this:

<div class="video">
     <img class="maintainaspectratio" src="maintainaspectratio.png" />
     <object>
          <param ... /><param ... />...
          <embed src="..." ...</embed>
     </object>
</div>

Now you should be able to add CSS similar to the following:

div.video { ...; position: relative; }
div.video img.maintainaspectratio { width: 100%; }
div.video object { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; }
div.video embed {width: 100%; height: 100%; }

Make sure you also remove any explicit height or width declaration within the object and embed tags that usually come with copy/pasted embed code.

The way it works depends on the position properties of the video class element and the item you want have maintain a certain aspect ratio. It takes advantage of the way an image will maintain its proper aspect ratio when resized in an element. It tells whatever else is in video class element to take full-advantage of the real estate provided by the dynamic image by forcing its width/height to 100% of the video class element being adjusted by the image.

Pretty cool, eh?

You might have to play around with it a bit to get it to work with your own design, but this actually works surprisingly well for me. The general concept is there.


Here's how I do it :

[data-aspect-ratio] {
    display: block;
    max-width: 100%;
    position: relative;
}

[data-aspect-ratio]:before {
    content: '';
    display: block;
}

[data-aspect-ratio] > * {
    display: block;
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    width: 100%;
}
[data-aspect-ratio="3:1"]:before {
    padding-top: 33.33%;
}
[data-aspect-ratio="2:1"]:before {
    padding-top: 50%;
}
[data-aspect-ratio="16:9"]:before {
    padding-top: 56.25%;
}
[data-aspect-ratio="3:2"]:before {
    padding-top: 66.66%;
}
[data-aspect-ratio="4:3"]:before {
    padding-top: 75%;
}
[data-aspect-ratio="1:1"]:before {
    padding-top: 100%;
}
[data-aspect-ratio="3:4"]:before {
    padding-top: 133.33%;
}
[data-aspect-ratio="2:3"]:before {
    padding-top: 150%;
}
[data-aspect-ratio="9:16"]:before {
    padding-top: 177.77%;
}
[data-aspect-ratio="1:2"]:before {
    padding-top: 200%;
}
[data-aspect-ratio="1:3"]:before {
    padding-top: 300%;
}

For example :

<div data-aspect-ratio="16:9"><iframe ...></iframe></div>

source


If you want to fit a square inside the viewport on either portrait or landscape view (as big as possible, but nothing sticking outside), switch between using vw/vh on orientation portrait/landscape:

@media (orientation:portrait ) {
  .square {
    width :100vw;
    height:100vw;
  }
} 
@media (orientation:landscape) {
  .square {
    width :100vh;
    height:100vh;
  }
} 

Just an idea or a hack.

_x000D_
_x000D_
div {_x000D_
  background-color: blue;_x000D_
  width: 10%;_x000D_
  transition: background-color 0.5s, width 0.5s;_x000D_
  font-size: 0;_x000D_
}_x000D_
_x000D_
div:hover {_x000D_
  width: 20%;_x000D_
  background-color: red;_x000D_
}_x000D_
  _x000D_
img {_x000D_
  width: 100%;_x000D_
  height: auto;_x000D_
  visibility: hidden;_x000D_
}
_x000D_
<div>_x000D_
  <!-- use an image with target aspect ratio. sample is a square -->_x000D_
  <img src="http://i.imgur.com/9OPnZNk.png" />_x000D_
</div>
_x000D_
_x000D_
_x000D_


I stumbled upon what I consider a smart solution for this problem, using <svg> and display:grid.

A display:grid element allows you to occupy the same space with two (or more) of its children, using the same grid-area.
This means means they are all flow content, overlapped, and out of the box the taller one sets the ratio.

One of them will be an <svg> in charge of setting the ratio. The other, actual content. If actual content is short and never fills up the entire ratio (and you just want it centered in a space with this ratio), simply center it (see first runnable snippet below).

<div class="ratio">
  <svg viewBox="0 0 1 1"></svg>
  <div>
    I'm square
  </div>
</div>
.ratio {
  display: grid;
}
.ratio > * {
  grid-area: 1/1;
}

Set <svg>s ratio to whatever you want:

  • <svg viewBox="0 0 4 3"></svg>
  • <svg viewBox="0 0 16 9"></svg>

_x000D_
_x000D_
.ratio {
  display: grid;
}
.ratio > * {
  grid-area: 1/1;
}

/* below code NOT needed for setting the ratio 
 * I just wanted to mark it visually
 * and center contents
 */
.ratio div {
  border: 1px solid red;
  display: flex;
  align-items: center;
  justify-content: center;
}
_x000D_
<div class="ratio">
  <svg viewBox="0 0 7 1"></svg>
  <div>
    Fixed ratio 7:1
  </div>
</div>
_x000D_
_x000D_
_x000D_


If you need a solution where the content element has more content you want confined into a scrollable area with desired ratio, set position:relative on the parent and position:absolute; height:100%; overflow-y: auto; on the content, allowing the flow content element (the <svg>) to set the size, therefore the ratio.

_x000D_
_x000D_
.ratio {
  display: grid;
  position: relative;
}
.ratio > * {
  grid-area: 1/1;
}


.ratio > div {
  height: 100%;
  overflow-y: auto;
  position: absolute;
  
  /* the rest is not needed */
  border: 1px solid red;
  padding: 0 1rem;
}
_x000D_
<div class="ratio">
  <svg viewBox="0 0 7 2"></svg>
  <div>
    <h1>Fixed ratio 7:2</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. A scelerisque purus semper eget. Sem nulla pharetra diam sit amet nisl suscipit adipiscing bibendum. A cras semper auctor neque vitae tempus quam pellentesque nec. Morbi enim nunc faucibus a pellentesque sit amet porttitor. Arcu odio ut sem nulla. Sed viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Cras tincidunt lobortis feugiat vivamus at augue eget. Laoreet sit amet cursus sit amet. Amet nulla facilisi morbi tempus iaculis urna id volutpat. Leo in vitae turpis massa sed elementum tempus egestas sed. Egestas integer eget aliquet nibh. Dolor sit amet consectetur adipiscing elit.

<p>Ut aliquam purus sit amet. Eget magna fermentum iaculis eu non diam phasellus vestibulum. Diam in arcu cursus euismod quis viverra nibh. Nullam vehicula ipsum a arcu cursus vitae congue. Vel orci porta non pulvinar neque laoreet suspendisse. At tellus at urna condimentum mattis pellentesque. Tristique senectus et netus et malesuada. Vel pretium lectus quam id leo in. Interdum velit euismod in pellentesque. Velit euismod in pellentesque massa placerat duis. Vitae suscipit tellus mauris a diam maecenas sed enim.

<p>Mauris a diam maecenas sed enim ut sem. In hendrerit gravida rutrum quisque. Amet dictum sit amet justo donec enim diam. Diam vulputate ut pharetra sit amet aliquam id. Urna porttitor rhoncus dolor purus non enim praesent. Purus in massa tempor nec feugiat nisl pretium. Sagittis vitae et leo duis ut. Facilisi nullam vehicula ipsum a arcu cursus vitae congue mauris. Volutpat odio facilisis mauris sit amet massa vitae tortor condimentum. Aliquam purus sit amet luctus venenatis lectus magna. Sit amet purus gravida quis blandit turpis. Enim eu turpis egestas pretium aenean. Consequat mauris nunc congue nisi. Nunc sed id semper risus in hendrerit gravida rutrum. Ante metus dictum at tempor. Blandit massa enim nec dui nunc mattis enim ut.
  </div>
</div>
_x000D_
_x000D_
_x000D_


As @emjay noted in a comment below, the ratio svg can be placed in one of the parent's pseudo-elements, as long as it's properly encoded:

_x000D_
_x000D_
.three-squares {
  display: grid;
  border: 1px solid red;
}

.three-squares > *, .three-squares:before {
  grid-area: 1/1;
}

.three-squares:before {
  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 1'%3E%3C/svg%3E");
  line-height: 0;
}
_x000D_
<div class="three-squares">  
  <div>I'm 3:1</div>
</div>
_x000D_
_x000D_
_x000D_

When used inside a pseudo-element, the <svg> becomes a replaced element which, by default, sits on a baseline of variable height (4px in Chrome, 3.5px in Firefox). The height of the baseline varies according to line-height, which is why we need to set line-height: 0 on the pseudo to get an accurate ratio. More details here.


I personally prefer the version where the <svg> is placed in markup, as I can have a single class (.ratio) dealing with containers of various ratios (as opposed to having a class for each individual ratio I might need).


Say that you like to maintain Width: 100px and the Height: 50px (i.e., 2:1) Just do this math:

.pb-2to1 {
  padding-bottom: calc(50 / 100 * 100%); // i.e., 2:1
}

I guess just using rem or em should solve the problem of fixed ratio but wont be hard bound to the screen as vw or vh or as painfull to use with flexbox as % is. Well, none of the answers worked properly for me, and in my case this attended me:

<div class="container">
   <div>
   </div>
</div>
.container {
   height: 100vh;
   width: 100vw;
}
.container div {
   height: 4em;
   width: 3em;
}

Or using rem. But anyway, any of them should make it.
rem uses the default font-size value as em use the closest font-size.


I'd like to share this as it has been a journey of a couple frustrating days to find a solution that worked for me. I was using these padding techniques (mentioned above about using some variation of padding and absolute positioning) to achieve 1:1 aspect ratio for a button like element that was inside of a grid/flex layout. The layout was set to be 100vh high so that it would always display as a single non-scrolling page.

The padding technique does work very well but it can easily break your grid layout and cause blowout/nasty scroll bars. The people who say that an absolute div can't affect layout are wrong in this case because the parent grows in both height and or width, that parent can mess with your layouts even if the child doesn't directly.

Normally this isn't an issue but the caveat comes when using grid. Grid is a 2D layout and it has the possibility to consider sizing and layout on both axises. I'm still trying to understand the exact nature of this but so far it seems like at the very least if you use this technique within a grid area that is constrained by fr units on both axises you will almost certainly experience blowout when the aspect-ratio item grows or otherwise changes the layout (display:none toggling and swapping grid areas with css were also layout changing issues that caused the blowout for me).

In my case it was that I constrained the height of a column that didn't need to be. Changing it to "auto" instead of "1fr" kept all my other layout the same and prevented blowout while still letting me keep my nice square buttons!

I don't know if this is the source of frustration for everyone here but it is an easy mistake to make and even using dev-tools won't give you an accurate idea of of where the blowout is coming from in many cases since it isn't really a case of an individual element blowing it out but rather the grid layout enlarging itself to keep those fr units accurate vertically and horizontally.


I just created a 2:1 div that resized to occupy the full width, but then shrinks the width if it would cause the top or bottom to exceed. But note, this will only work with the size of the window, not the size of the parent.

#scene {
    position: relative;
    top: 50vh;
    left: 50vw;
    width: 100vw;
    height: 50vw;
    max-height: 100vh;
    max-width: calc(100vh * 2);
    transform: translate(-50%, -50%);
}

I'm sure you can work out the correct %'s to use for 4:3 instead of 2:1.


To add to Web_Designer's answer, the <div> will have a height (entirely made up of bottom padding) of 75% of the width of it's containing element. Here's a good summary: http://mattsnider.com/css-using-percent-for-margin-and-padding/. I'm not sure why this should be so, but that's how it is.

If you want your div to be a width other than 100%, you need another wrapping div on which to set the width:

div.ar-outer{
    width: 60%; /* container; whatever width you want */
    margin: 0 auto; /* centered if you like */
}
div.ar {
    width:100%; /* 100% of width of container */
    padding-bottom: 75%; /* 75% of width of container */
    position:relative;
}
div.ar-inner {
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
}

I used something similar to Elliot's image trick recently to allow me to use CSS media queries to serve a different logo file depending on device resolution, but still scale proportionally as an <img> would naturally do (I set the logo as background image to a transparent .png with the correct aspect ratio). But Web_Designer's solution would save me an http request.


New in Chrome 88 and soon to follow in other browsers is the new CSS aspect-ratio property.

The aspect-ratio CSS property sets a preferred aspect ratio for the box, which will be used in the calculation of auto sizes and some other layout functions.

CSS Tricks Article

More Information

_x000D_
_x000D_
div {
  background: rebeccapurple;
  height:100px;
  margin:1em auto;
}

.square {
  aspect-ratio: 1 / 1;
  }
_x000D_
<div class="square">
  
</div>
_x000D_
_x000D_
_x000D_


As @web-tiki already show a way to use vh/vw, I also need a way to center in the screen, here is a snippet code for 9:16 portrait.

.container {
  width: 100vw;
  height: calc(100vw * 16 / 9);
  transform: translateY(calc((100vw * 16 / 9 - 100vh) / -2));
}

translateY will keep this center in the screen. calc(100vw * 16 / 9) is expected height for 9/16.(100vw * 16 / 9 - 100vh) is overflow height, so, pull up overflow height/2 will keep it center on screen.

For landscape, and keep 16:9, you show use

.container {
  width: 100vw;
  height: calc(100vw * 9 / 16);
  transform: translateY(calc((100vw * 9 / 16 - 100vh) / -2));
}

The ratio 9/16 is ease to change, no need to predefined 100:56.25 or 100:75.If you want to ensure height first, you should switch width and height, e.g. height:100vh;width: calc(100vh * 9 / 16) for 9:16 portrait.

If you want to adapted for different screen size, you may also interest

  • background-size cover/contain
    • Above style is similar to contain, depends on width:height ratio.
  • object-fit
    • cover/contain for img/video tag
  • @media (orientation: portrait)/@media (orientation: landscape)
    • Media query for portrait/landscape to change the ratio.

I used a new solution.

.squares{
  width: 30vw
  height: 30vw

To main aspect ratio

.aspect-ratio
  width: 10vw
  height: 10vh

However, this is relative to the entire viewport. So, if you need a div that is 30% of the viewport width, you can use 30vw instead, and since you know the width, you reuse them in height using calc and vw unit.


Well, we've recently received the ability to use the aspect-ratio property in CSS.

https://twitter.com/Una/status/1260980901934137345/photo/1

Note: Support is not the best yet ...

https://caniuse.com/#search=aspect-ratio

EDIT: Aspect ratio is now available !

https://web.dev/aspect-ratio/


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

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

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: 4x4 grid of square divs

_x000D_
_x000D_
body {_x000D_
  display: flex;_x000D_
  flex-wrap: wrap;_x000D_
  justify-content: space-between;_x000D_
}_x000D_
div {_x000D_
  width: 23vw;_x000D_
  height: 23vw;_x000D_
  margin: 0.5vw auto;_x000D_
  background: gold;_x000D_
}
_x000D_
<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>
_x000D_
_x000D_
_x000D_

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


2021 solution - use the aspect-ratio CSS property

(currently supported in Chromium, Safari Technology Preview, and Firefox Nightly)

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

lets say you have 2 divs the outher div is a container and the inner could be any element that you need to keep its ratio (img or an youtube iframe or whatever )

html looks like this :

<div class='container'>
  <div class='element'>
  </div><!-- end of element -->
<div><!-- end of container -->

lets say you need to keep the ratio of the "element" ratio => 4 to 1 or 2 to 1 ...

css looks like this

.container{
  position: relative;
  height: 0
  padding-bottom : 75% /* for 4 to 3 ratio */ 25% /* for 4 to 1 ratio ..*/
  
}

.element{
  width : 100%;
  height: 100%;
  position: absolute; 
  top : 0 ;
  bottom : 0 ;
  background : red; /* just for illustration */
}

padding when specified in % it is calculated based on width not height. .. so basically you it doesn't matter what your width it height will always be calculated based of that . which will keep the ratio .


As stated in here on w3schools.com and somewhat reiterated in this accepted answer, padding values as percentages (emphasis mine):

Specifies the padding in percent of the width of the containing element

Ergo, a correct example of a responsive DIV that keeps a 16:9 aspect ratio is as follows:

CSS

.parent {
    position: relative;
    width: 100%;
}
.child {
    position: relative;
    padding-bottom: calc(100% * 9 / 16);
}
.child > div {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

HTML

<div class="parent">
    <div class="child">
        <div>Aspect is kept when resizing</div>
    </div>
</div>

Demo on JSFiddle


While most answers are very cool, most of them require to have an image already sized correctly... Other solutions only work for a width and do not care of the height available, but sometimes you want to fit the content in a certain height too.

I've tried to couple them together to bring a fully portable and re-sizable solution... The trick is to use to auto scaling of an image but use an inline svg element instead of using a pre-rendered image or any form of second HTTP request...

_x000D_
_x000D_
div.holder{_x000D_
  background-color:red;_x000D_
  display:inline-block;_x000D_
  height:100px;_x000D_
  width:400px;_x000D_
}_x000D_
svg, img{_x000D_
  background-color:blue;_x000D_
  display:block;_x000D_
  height:auto;_x000D_
  width:auto;_x000D_
  max-width:100%;_x000D_
  max-height:100%;_x000D_
}_x000D_
.content_sizer{_x000D_
  position:relative;_x000D_
  display:inline-block;_x000D_
  height:100%;_x000D_
}_x000D_
.content{_x000D_
  position:absolute;_x000D_
  top:0;_x000D_
  bottom:0;_x000D_
  left:0;_x000D_
  right:0;_x000D_
  background-color:rgba(155,255,0,0.5);_x000D_
}
_x000D_
<div class="holder">_x000D_
  <div class="content_sizer">_x000D_
    <svg width=10000 height=5000 />_x000D_
    <div class="content">_x000D_
    </div>_x000D_
  </div>_x000D_
</div>
_x000D_
_x000D_
_x000D_

Notice that I have used big values in the width and height attributes of the SVG, as it needs to be bigger than the maximum expected size as it can only shrink. The example makes the div's ratio 10:5


A simple way of maintaining the aspect ratio, using the canvas element.

Try resizing the div below to see it in action.

For me, this approach worked best, so I am sharing it with others so they can benefit from it as well.

_x000D_
_x000D_
.cont {
  border: 5px solid blue;
  position: relative;
  width: 300px;
  padding: 0;
  margin: 5px;
  resize: horizontal;
  overflow: hidden;
}

.ratio {
  width: 100%;
  margin: 0;
  display: block;
}

.content {
  background-color: rgba(255, 0, 0, 0.5);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  margin: 0;
}
_x000D_
<div class="cont">
  <canvas class="ratio" width="16" height="9"></canvas>
  <div class="content">I am 16:9</div>
</div>
_x000D_
_x000D_
_x000D_

Also works with dynamic height!

_x000D_
_x000D_
.cont {
  border: 5px solid blue;
  position: relative;
  height: 170px;
  padding: 0;
  margin: 5px;
  resize: vertical;
  overflow: hidden;
  display: inline-block; /* so the div doesn't automatically expand to max width */
}

.ratio {
  height: 100%;
  margin: 0;
  display: block;
}

.content {
  background-color: rgba(255, 0, 0, 0.5);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  margin: 0;
}
_x000D_
<div class="cont">
  <canvas class="ratio" width="16" height="9"></canvas>
  <div class="content">I am 16:9</div>
</div>
_x000D_
_x000D_
_x000D_


Here is my solution for maintaining an 16:9 aspect ratio in portrait or landscape on a div with optional fixed margins.

It's a combination of width/height and max-width/max-height properties with vw units.

In this sample, 50px top and bottom margins are added on hover.

_x000D_
_x000D_
html {
  height: 100%;
}

body {
  margin: 0;
  height: 100%;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.fixedRatio {
  max-width: 100vw;
  max-height: calc(9 / 16 * 100vw);
  width: calc(16 / 9 * 100vh);
  height: 100vh;
  
  /* DEBUG */
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: blue;
  font-size: 2rem;
  font-family: 'Arial';
  color: white;
  transition: width 0.5s ease-in-out, height 0.5s ease-in-out; 
}

.fixedRatio:hover {
  width: calc(16 / 9 * (100vh - 100px));
  height: calc(100vh - 100px);
}
_x000D_
<div class='container'>
  <div class='fixedRatio'>
    16:9
  </div>
</div>
_x000D_
_x000D_
_x000D_

Demo on JSFiddle


Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width

Examples related to responsive-design

How to make flutter app responsive according to different screen size? Bootstrap 4: responsive sidebar menu to top navbar How to make canvas responsive Putting -moz-available and -webkit-fill-available in one width (css property) Bootstrap 3 - Responsive mp4-video Change hover color on a button with Bootstrap customization iOS 8 removed "minimal-ui" viewport property, are there other "soft fullscreen" solutions? Bootstrap full responsive navbar with logo or brand name text Bootstrap 3 truncate long text inside rows of a table in a responsive way Responsive Bootstrap Jumbotron Background Image

Examples related to aspect-ratio

Maintain aspect ratio of div but fill screen width and height in CSS? Grid of responsive squares Android Camera Preview Stretched How to style a div to be a responsive square? CSS force image resize and keep aspect ratio Resize UIImage by keeping Aspect ratio and width Is there a list of screen resolutions for all Android based phones and tablets? Maintain the aspect ratio of a div with CSS What's the algorithm to calculate aspect ratio?