skip to Main Content

According to the MDN Web Docs, aspect-ratio should be able to be applied to @container. Unfortunately I cannot reproduce it.

The context is that I want to fill an element with dynamic aspect ratio completely with an image.

I explicitly do not want to use object-fit: cover; for this, as the image will be cropped and the initially invisible area can no longer be displayed later with e.g. pan and zoom. A hidden overflow is therefore desired for the parent element.

I am also open to other suggestions. Thanks in advance!

div {
  overflow: hidden;
  container-type: inline-size;
  container-name: wrapper;
  margin-bottom: 1em;
  background: lightcoral;
}

.landscape {
  aspect-ratio: 16/9;
  width: 200px;
}

.portrait {
  aspect-ratio: 9/16;
  height: 200px;
}

img {
  width: 100%;
  height: auto;
}

@container wrapper (aspect-ratio < 1) {
  img {
    width: auto;
    height: 100%;
  }
}
<div class="landscape">
  <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
</div>
<div class="portrait">
  <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
</div>

Edit: Image for clarification

Example

2

Answers


  1. The aspect-ratio condition in @container queries doesn’t seem to
    be implemented yet by most navigators, regarding
    Can I use?‘s entry about container queries.

    But you’ve already got the portrait and landscape classes, so
    you can probably do different rules without querying the aspect-ratio,
    especially as it is fixed by yourself.

    But you can still do queries on the width and then decide what to do.
    Just keep in mind that if you want to switch automatically between
    portrait and landscape, depending on the space available (width of the
    container), then I think you’ll have to add an inner div which contains
    the image and your other tools to zoom and pan.

    Here’s a way to make your image use the height:

    /* Google Material icons. */
    @import "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,200,0,0";
    
    /* Optimize space used, as the code snippet is so small. */
    body {
      margin: 0;
      padding: 0;
    }
    
    .image-box {
      overflow: hidden;
      container-type: inline-size;
      container-name: image-box;
      margin-bottom: .5em; /* Optimize space used, as the code snippet is so small. */
      background: lightcoral;
      line-height: 0; /* To avoid space below the image. */
      position: relative; /* To put the buttons absolute. */
    }
    
    .image-box.landscape {
      aspect-ratio: 16/9;
      max-width: 200px;
    }
    
    .image-box.portrait {
      aspect-ratio: 9/16;
      max-height: 200px;
    }
    
    /* All images should never be wider than the container. */
    img {
      max-width: 100%;
      height: auto;
    }
    
    .image-box.portrait img {
      max-width: none;
      width: auto;
      height: 100%;
      /* Proposition to center the image horizontally: */
      margin-left: 50%;
      transform: translatex(-50%);
    }
    
    .image-box .buttons {
      position: absolute;
      background: rgb(0 0 0 / 0.2);
      font-size: .5em;
      padding: .5em;
      bottom: 0;
      right: 0;
      display: flex;
    }
    
    .image-box.landscape .buttons {
      flex-direction: column;
    }
    
    .image-box button {
      background: none;
      border: none;
      padding: 0;
      color: rgb(255 255 255 / 0.7);
      cursor: pointer;
    }
    .image-box button:hover {
      color: white;
    }
    
    /* You can't use aspect-ratio in the condition because it's
       not yet implemented in most browsers. So use the
       portrait/landscape classes instead and do other specific
       rules based on the width of the conainer. */
    @container image-box (width < 200px) {
      /* Reduce the size of the buttons. */
      /* cqi = 1% of a query container's inline size. */
      .image-box.landscape button {
        font-size: min(24px, 16cqi);
      }
      .image-box.portrait button {
        font-size: min(24px, 20cqi);
      }
    }
    <div class="image-box landscape">
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
      <div class="buttons">
        <button class="material-symbols-outlined">zoom_in</button>
        <button class="material-symbols-outlined">zoom_out</button>
        <button class="material-symbols-outlined">download</button>
      </div>
    </div>
    
    <div class="image-box portrait">
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
      <div class="buttons">
        <button class="material-symbols-outlined">zoom_in</button>
        <button class="material-symbols-outlined">zoom_out</button>
        <button class="material-symbols-outlined">download</button>
      </div>
    </div>

    Notice that I replaced your div selector by .image-box so
    that it can’t break other divs used for other purpose.

    I didn’t think about the centred vertical alignment of the image
    in the landscape mode. We perhaps have to also use transforms
    or other CSS techniques.

    Login or Signup to reply.
  2. I’m not sure why you don’t want object-fit:cover? Based on your description it sounds like the ideal solution. Here I have made the image container resizable so you can see how it behaves at any aspect ratio. By default the images would be centred but I have added object-position: left top; to match your illustration.

    div {
      overflow: hidden;
      margin-bottom: 1em;
      background: lightcoral;
      aspect-ratio: 1;
      width: 160px;
      resize: both;
    }
    
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: left top;
    }
    <div>
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
    </div>(resize me)

    But to answer your question about container queries: Use container-type: size; instead of container-type: inline-size;. inline-size refers to just the width whereas size considers both width and height (and both are needed to calc aspect-ratio).

    div {
      overflow: hidden;
      container-type: size;
      container-name: wrapper;
      margin-bottom: 1em;
      background: lightcoral;
    }
    
    .landscape {
      aspect-ratio: 16/9;
      width: 200px;
    }
    
    .portrait {
      aspect-ratio: 9/16;
      height: 200px;
    }
    
    .dynamic {
      aspect-ratio: 1;
      height: 200px;
      resize: both;
    }
    
    img {
      width: 100%;
      height: auto;
    }
    
    @container wrapper (aspect-ratio <=1) {
      img {
        width: auto;
        height: 100%;
      }
    }
    <div class="landscape">
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
    </div>
    <div class="portrait">
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
    </div>
    The following is resizable and shows that the proposed @container solution is not as good as the object-fit one since it only really works at the fixes aspect sizes.
    <div class="dynamic">
      <img src="https://images.unsplash.com/photo-1722932319520-fdd0c213bf2c?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search