cssobject-fit

Cover the container with image, but only if image is big enough (a mix of object-fit: cover and scale-down)


Example:

https://jsfiddle.net/killymxi/gjb10oqa/26/

<html>
  <body>
    <div class="container-1">
      1 - cover
    </div>
    <div class="container-2">
      2 - scale-down
    </div>
    <div class="container-1">
      <img src="http://placekitten.com/80/120"/>
    </div>
    <div class="container-2">
      <img src="http://placekitten.com/80/120"/>
    </div>
    <div class="container-1">
      <img src="http://placekitten.com/320/480"/>
    </div>
    <div class="container-2">
      <img src="http://placekitten.com/320/480"/>
    </div>
  </body>
</html>
body {
  display: grid;
  grid-template-columns: 200px 200px;
  grid-template-rows: auto 200px 200px;
  gap: 2px;
}

.container-1 {
  background-color: #dfd;
  display: flex;
}

.container-2 {
  background-color: #ddf;
  display: flex;
}

.container-1 > img {
  object-fit: cover;
  flex: 1 1;
}

.container-2 > img {
  object-fit: scale-down;
  flex: 1 1;
}

rendering of the example code

Is there a way to achieve combined behavior? (Top right and bottom left images.)

Paraphrasing the definition of scale-down, I want to achieve the following:

The content is sized as if none or cover were specified, whichever would result in a smaller concrete object size.

Note: I can't change the HTML layout (but I might be able to use two layers of containers - image inside tag inside tag) and image URLs are not static.

Update:
If CSS-only solution exists, I suspect it should involve recreating the cover fit in some way.
For now, I only see a partial solution, when image is known to always have aspect ratio on the same side relative to container (always taller or always wider).
Full solution seems to require a condition on content aspect ratio, which doesn't seem to be achievable today.
There is fit-content, but it can't participate in calc(), max(), etc., and no other ways to access natural size of images as in JS.
Best CSS-only attempt so far: https://jsfiddle.net/killymxi/gjb10oqa/30/


Solution

  • You could consider using JavaScript to inspect the size of the images and then apply object-fit: cover if they are large enough:

    function setSize(img) {
      img.style.objectFit = (img.naturalHeight > 200 && img.naturalWidth > 200)
        ? 'cover'
        : 'none';
    }
    
    document.querySelectorAll('img').forEach(img => {
      if (img.complete) {
        setSize(img);
      } else {
        img.addEventListener('load', () => setSize(img));
      }
    });
    body {
      display: grid;
      grid-template-columns: 200px 200px;
      grid-template-rows: auto 200px 200px;
      gap: 2px;
    }
    
    .container-1 {
      background-color: #dfd;
      display: flex;
    }
    
    .container-2 {
      background-color: #ddf;
      display: flex;
    }
    
    :is(.container-1, .container-2) > img {
      flex: 1 1;
    }
    <html>
      <body>
        <div class="container-1">
          1 - cover
        </div>
        <div class="container-2">
          2 - scale-down
        </div>
        <div class="container-1">
          <img src="http://placekitten.com/80/120"/>
        </div>
        <div class="container-2">
          <img src="http://placekitten.com/80/120"/>
        </div>
        <div class="container-1">
          <img src="http://placekitten.com/320/480"/>
        </div>
        <div class="container-2">
          <img src="http://placekitten.com/320/480"/>
        </div>
      </body>
    </html>