skip to Main Content

I couldn’t find a solution for growing a div inside div maintaining aspect-ratio except for images, but I want to do it with a div.
I basically want object-fit: contain behavior for a div inside a div, with some padding.
But it only works for wide, but not for narrow divs.

<div class="container">
  <div class="element">
  
  </div>
</div>

---

.container {
  display: flex;
  background: yellow;
  width: 320px; /* arbitrary values for testing */
  height: 190px; /* arbitrary values for testing */
  justify-content: center;
  align-items: center;
  padding: 16px;
}

.element {
  background: red;
  aspect-ratio: 1 / 3;
  width: 100%;
  max-height: 100%;
}

This code works with aspect-ratio: 3 / 1; but not with aspect-ratio: 1 / 3;.

.container {
  display: flex;
  background: yellow;
  width: 320px;
  height: 190px;
  justify-content: center;
  align-items: center;
  padding: 16px;
}

.element {
  background: red;
  aspect-ratio: 1 / 3;
  width: 100%;
  max-height: 100%;
}
<div class="container">
  <div class="element">
  
  </div>
</div>

My expected behavior is depending on the aspect-ratio (which I want to change via JavaScript later on, that the result looks like in the image below:

Left Aspect Ratio: 3/1; Right Aspect Ratio: 1/3

2

Answers


  1. Since you want it to behave like object-fit: contain, the .element should fill or the entire height or the entire width depending on which is the greater arm defined in the aspect ratio as w / h.

    Now the problem is for the .element to have at least one of the height or width set for the other one to be calculated according to the aspect ratio.

    Using max-height and max-width is not enough for that to happen unless you drop the flex layout on the container. But that means you’ll have hard time trying to center the .element because other strategies at such conditions are not viable. Before giving up I also tried using position: absolute with transform: translate(-50%, -50%) and left: 50%; top: 50%. Another solution using pseudoelements was suggested in comments but frankly I think it didn’t fulfill the object-fit requirement because it didn’t fill the whole available space confined by the container paddings.

    So to me, so far, the only solution I could find was just deciding which size to style as 100% between width and height depending on which side of the aspect ratio was the larger. So for example if the aspect ratio is 1 / 3 the height should fill 100% otherwise the width.

    This drawback requires you to take into account that the properties set via css should be consistent aspect-ratio, width and height. A workaround might be using css variables and deductions made over one single value to set all of those three.

    By the way since you also added that js will be involved in changing the aspect ratio of the element, it seemed to me legit to deal with it via javascript applying the strategy I described above.

    This is a short demo:

    const element = document.querySelector('.element');
    
    document.getElementById('flip').addEventListener('click',()=>{
      flipAspectRatio(element);  
    });
    
    function flipAspectRatio(element) { 
      //read the aspect-ratio currently set on the element as w / h
      const aspectRatio = window.getComputedStyle(element).getPropertyValue('aspect-ratio');        
      const [w, h] = aspectRatio.split('/').map(Number);    
      
      //if w is greater then h
      //(taking into account that we are going to flip them at the later step)
      if (h > w) {  
        //set width as 100% (and reset height as auto)
        element.style.width = '100%';
        element.style.height = 'auto';
      } else {    
        //set height as 100% (and reset width as auto)
        element.style.height = '100%';
        element.style.width = 'auto';
      }   
      //set the aspect ratio as flipped
      element.style.aspectRatio = `${h} / ${w}`;
    }
    .container {
      display: flex;  
      width: 320px; 
      height: 190px;
      justify-content: center;
      align-items: center;
      padding: 16px;
      
      background: yellow;
    }
    
    .element {  
      aspect-ratio: 1 / 3;
      height: 100%;
      
      background: red;
    }
    
    #flip{
      margin-top: 1em;
      font-size: 20px;
    }
    
    button{
      cursor: pointer;
    }
    <div class="container">
      <div class="element">  
      </div>
    </div>
    
    <button id="flip">flip aspect ratio</button>
    Login or Signup to reply.
  2. If you want to achieve this with just css, you’ll need some "hacking". For example, adding a <canvas> element that will store the required aspect-ratio:

    toggle.onclick = function() {
      const aspectRatio = this.classList.contains('active') ? '1/3' : '3/1';
      container.style.setProperty('--aspect-ratio', aspectRatio)
      this.classList.toggle('active')
    }
    .container {
      display: flex;
      background: yellow;
      width: 320px;
      height: 190px;
      justify-content: center;
      align-items: center;
      padding: 16px;
      --aspect-ratio: 1 / 3;
    }
    
    .wrapper {
      position: relative;
      max-height: 100%;
      max-width: 100%;
      aspect-ratio: var(--aspect-ratio);
      canvas {
        aspect-ratio: var(--aspect-ratio);
        max-width: 100%;
        max-height: 100%;
      }
    }
    
    .element {
      background: red;
      position: absolute;
      inset: 0;
    }
    <div id="container" class="container">
      <div class="wrapper">
        <canvas width="2000" height="2000"></canvas>
        <div class="element"></div>
      </div>
    </div>
    <br>
    <button id="toggle">Toggle aspect-ratio</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search