Search code examples
cssresponsive-designaspect-ratio

How to maintain aspect ratio between 16:9 and 4:3 using CSS?


Goal is to render an object without letterboxing, as long as the aspect ratio is within given limits, such as between 16:9 and 4:3.

For example, when the available width of a 16:9 image is reduced below 16:9 aspect ratio, we want to clip the left and right side of the image. If the width is further reduced below 4:3 aspect ratio, we want to keep that aspect ratio and start scaling down.

In this example, you can see how the object scales. The problem with this approach is that scaling happens immediately, as soon as the available space is even one pixel off.

.iframe {
  border: 1px solid black;
  width: 350px;
  height: 350px;
  resize: both;
  overflow: auto;
}

.wrapper {
  background-color: #c05046;
  height: 100%;
  display: flex;
}

.image {
  margin: 0 auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
<div class="iframe">
  <div class="wrapper">
      <img class="image" src="https://i.ytimg.com/vi/PX-0Nrg4Yhw/maxresdefault.jpg">
  </div>
</div>

[An iframe is simulated in the examples and you can change its size for changing the available space.]

The fluid-ratio trick as explained at https://voormedia.com/blog/2012/11/responsive-background-images-with-fixed-or-fluid-aspect-ratios can somehow achieve scaling between 2 ratios, but has several shortcomings:

  • It works with a fixed height and only scales width, which makes it work horizontally, but not vertically.
  • The object is not an arbitrary object but rather a background-image, which means this trick probably won't work with video and other objects.

.iframe {
  background-color: #c05046;
  border: 1px solid black;
  width: 350px;
  height: 350px;
  resize: both;
  overflow: auto;
}

.column {
  max-width: 640px;
}

figure.fluidratio {
  margin: 0;
  padding-top: 15%; /* slope */
  height: 240px; /* start height */
  background-image: url(https://i.ytimg.com/vi/PX-0Nrg4Yhw/maxresdefault.jpg);
  background-size: cover;
  -moz-background-size: cover; /* Firefox 3.6 */
  background-position: center; /* Internet Explorer 7/8 */
}
<div class="iframe">
  <div class="column">
    <figure class="fluidratio"></figure>
  </div>
</div>

I saw somebody suggest using embedded SVG, but didn't get the examples to work.

Any feedback appreciated.


Solution

  • If I understood the problem right, you want to achieve something like this:

          .iframe {
            border: 1px solid black;
            width: 100%;
            height: 100%;
          }
    
          .container {
            width: 100%;
            max-width: 130vh;
            position: relative;
            margin: 0 auto;
          }
    
          .wrapper {
            background-color: #c05046;
            height: 0;
            padding-top: 75%;
            position: relative;
            width: 75%;
            left: 50%;
            transform: translateX(-50%);
            max-width: 640px;
          }
    
          .image {
            position: absolute;
            top: 0;
            left: 50%;
            transform: translateX(-50%);
            height: 100%;
          }
        <div class="iframe">
          <div class="container">
          <div class="wrapper">
              <img class="image" src="https://i.ytimg.com/vi/PX-0Nrg4Yhw/maxresdefault.jpg">
          </div>
          </div>
        </div>

    *the resizing is disabled here, please check in full screen with responsive mode.

    Using inner .wrapper, it keeps the part with ratio of 4/3 always visible, but it allows the content to overflow it boundaries. By max-height: 130vh its height keeps scaling also. As it is not based on background-image, it can be used with video as well as image.

    The main disadvantage is using a vh unit what keeps it relative to view port size, to parent container height. -> see comment