Ensure that the element fits within the container without increasing in size

My current challenge involves solving a seemingly simple problem. Imagine I have a layout consisting of a header, content, and footer. Within the content section, there is a row of items that fill all available space. Each item has equal width and contains one child element.

The goal is for the children elements to perfectly fit within their parent containers, expanding to utilize maximum space without scaling up - similar to what object-fit: scale-down does for images.

Let's consider the sizes of the elements that need to be accommodated:

  1. 200 x 300
  2. 1920 x 1080
  3. 1080 x 1920

Here is the desired outcome:

https://i.sstatic.net/Oaxe6.png

I am facing difficulties in achieving this result; larger elements either overflow the container or are clipped.

html,
body {
  background-color: #000;
  height: 100%;
  margin: 0;
  padding: 0;
}

.layout {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.top,
.bottom {
  background-color: #444;
  flex-shrink: 0;
  height: 50px;
}

.middle {
  background-color: #222;
  flex-grow: 1;
  overflow: hidden;
}

.items {
  display: flex;
  gap: 20px;
  height: 100%;
  overflow: hidden;
}

.item {
  align-items: center;
  background-color: #666;
  display: flex;
  flex-grow: 1;
  justify-content: center;
  overflow: hidden;
}

.block {
  background-color: #f80;
  background-image: linear-gradient(45deg, orange, purple);
  height: 100%;
  width: 100%;
}

.block1 {
  max-height: 200px;
  max-width: 300px;
}

.block2 {
  max-height: 1080px;
  max-width: 1920px;
}

.block3 {
  max-height: 1920px;
  max-width: 1080px;
}

To summarize, I have rectangles with specific dimensions that need to scale down but not up while maintaining aspect ratio. I have experimented with the aspect-ratio property without success. Is it possible to achieve this without JavaScript calculations?

Answer №1

After receiving a suggestion from @hedfol to utilize aspect-ratio container queries, I was able to streamline the solution. I standardized all scenarios (4 cases involving horizontal/vertical containers and blocks) using the same approach. Additionally, I optimized the query by employing the aspect-ratio condition exclusively.

Here is an example pattern for a 1920 x 1080 block:

.block2 {
  aspect-ratio: 1920 / 1080;
  width: 1920px;
}

@container (aspect-ratio > 1920 / 1080) {
  .block2 {
    height: 1080px;
    max-height: 100%;
    width: auto;
  }
}

Below is the complete and functional snippet:

html,
body {
  background-color: #000;
  height: 100%;
  margin: 0;
  padding: 0;
}

.layout {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.top,
.bottom {
  background-color: #444;
  flex-shrink: 0;
  height: 50px;
}

.middle {
  background-color: #222;
  flex-grow: 1;
  overflow: hidden;
}

.items {
  display: flex;
  gap: 20px;
  height: 100%;
  overflow: hidden;
}

.item {
  align-items: center;
  background-color: #666;
  container-type: size;
  display: flex;
  flex-grow: 1;
  justify-content: center;
}

.block {
  background-image: linear-gradient(45deg, orange, purple);
}

.block1 {
  aspect-ratio: 300 / 200;
  width: 300px;
}

@container (aspect-ratio > 300 / 200) {
  .block1 {
    height: 200px;
    max-height: 100%;
    width: auto;
  }
}

.block2 {
  aspect-ratio: 1920 / 1080;
  width: 1920px;
}

@container (aspect-ratio > 1920 / 1080) {
  .block2 {
    height: 1080px;
    max-height: 100%;
    width: auto;
  }
}

.block3 {
  aspect-ratio: 1080 / 1920;
  width: 1080px;
}

@container (aspect-ratio > 1080 / 1920) {
  .block3 {
    height: 1920px;
    max-height: 100%;
    width: auto;
  }
}
<div class="layout">
  <div class="top"></div>
  <div class="middle">
    <div class="items">
      <div class="item">
        <div class="block block1"></div>
      </div>
      <div class="item">
        <div class="block block2"></div>
      </div>
      <div class="item">
        <div class="block block3"></div>
      </div>
    </div>
  </div>
  <div class="bottom"></div>
</div>

I am now considering whether CSS variables can be used to further simplify this solution.

Answer №2

You can actually achieve this without needing any @container requests. By providing the dimensions yourself, you can easily size the <canvas> element to maintain its aspect ratio.
Check out the following example that modifies your code:

html {
  height:100%;
}

body {
  background-color: #000;
  height: 100%;
  margin: 0;
  display: flex;
  flex-direction: column;
}

.layout {
  display: flex;
  flex-direction: column;
  flex: auto;
  overflow: hidden;
}

.top,
.bottom {
  background-color: #444;
  flex: none;
  height: 50px;
}

.middle {
  background-color: #222;
  display: flex;
  flex-direction: column;
  flex: auto;
  overflow: hidden;
}

.items {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  flex: auto;
  overflow: hidden;
}

.items-cell {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.item {
  position: relative;
  margin: auto;
  display: flex;
  max-height: 100%;
}

.item canvas{
  max-width: 100%;
  max-height: 100%;
}

.block {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(45deg, orange, purple);
}
<div class="layout">
  <div class="top"></div>
    <div class="middle">
      <div class="items">
        <div class="items-cell">
        <div class="item" style="aspect-ratio: 200/300;">
          <canvas width="200" height="300"></canvas>
          <div class="block"></div>
        </div>
      </div>
      <div class="items-cell">
        <div class="item" style="aspect-ratio: 1080/1920;">
          <canvas width="1080" height="1920"></canvas>
          <div class="block"></div>
        </div>
      </div>
      <div class="items-cell">
        <div class="item" style="aspect-ratio: 1920/1080;">
          <canvas width="1920" height="1080"></canvas>
          <div class="block"></div>
        </div>
      </div>
    </div>
  </div>
  <div class="bottom"></div>
</div>

Answer №3

Seems like achieving it is possible using the aspect-ratio property along with @container queries:

  1. To start, remove height: 100%; width: 100%; from .block to enable auto width and height.

  2. For "horizontal" blocks where width > height:
  • Delete max-height, retain max-width, and include width: 100%.
  • Integrate aspect-ratio with values based on width / height without pixels (aspect-ratio: 300 / 200; for .block1);
  • Implement an @container query to adjust sizing when space is limited. For .block1:
@container (aspect-ratio > 300 / 200) and (height < 200px) {
  .block1 {
    height: 100%;
    width: auto;
  }
}
  1. For "vertical" blocks where height > width:
  • Delete max-width, maintain max-height, and add height: 100%.
  • Add aspect-ratio similarly to the "horizontal" setup - width / height (aspect-ratio: 1080 / 1920; for .block3);
  • Configure an @container query to adjust sizing when there's not enough space. For .block3:
@container (aspect-ratio < 1080 / 1920) and (width < 1080px) {
  .block3 {
    width: 100%;
    height: auto;
  }
}
  1. Include container-type: size; to .item to define it as a size query container.

Snippet:

html,
body {
  background-color: #000;
  height: 100%;
  margin: 0;
  padding: 0;
}

.layout {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.top,
.bottom {
  background-color: #444;
  flex-shrink: 0;
  height: 50px;
}

.middle {
  background-color: #222;
  flex-grow: 1;
  overflow: hidden;
}

.items {
  display: flex;
  gap: 20px;
  height: 100%;
  overflow: hidden;
}

.item {
  align-items: center;
  background-color: #666;
  display: flex;
  flex-grow: 1;
  justify-content: center;
  overflow: hidden;
  container-type: size;
}

.block {
  background-color: #f80;
  background-image: linear-gradient(45deg, orange, purple);
}

.block1 {
  max-width: 300px;
  width: 100%;
  aspect-ratio: 300 / 200;
}

@container (aspect-ratio > 300 / 200) and (height < 200px) {
  .block1 {
    height: 100%;
    width: auto;
  }
}

.block2 {
  max-width: 1920px;
  width: 100%;
  aspect-ratio: 1920 / 1080;
}

@container (aspect-ratio > 1920 / 1080) and (height < 1080px) {
  .block2 {
    height: 100%;
    width: auto;
  }
}

.block3 {
  max-height: 1920px;
  height: 100%;
  aspect-ratio: 1080 / 1920;
}

@container (aspect-ratio < 1080 / 1920) and (width < 1080px) {
  .block3 {
    width: 100%;
    height: auto;
  }
}
<div class="layout">
  <div class="top"></div>
  <div class="middle">
    <div class="items">
      <div class="item">
        <div class="block block1"></div>
      </div>
      <div class="item">
        <div class="block block2"></div>
      </div>
      <div class="item">
        <div class="block block3"></div>
      </div>
    </div>
  </div>
  <div class="bottom"></div>
</div>

Update

This elaborate solution extends the simplified approach by author (@Robo Robok) utilizing CSS variables.

All properties are defined within a common .block class:

.block {
  --bw: var(--w);
  --bh: none;
  
  width: auto;
  width: calc(var(--bw) * 1px);
  
  height: calc(var(--bh) * 1px);
  max-height: calc(var(--bh)/var(--bh) * 100%);
  
  aspect-ratio: var(--w) / var(--h);
  background-image: linear-gradient(45deg, orange, purple);
}

Unique variables are set for each specific block, like so:

.block1 {
  --w: 300;
  --h: 200;
}
@container (aspect-ratio > 300 / 200) {
  .block1 {
    --bw: none;
    --bh: var(--h);
  }
}

While grouping @containers or utilizing variables and style() queries could potentially reduce the code, current limitations exist:

Targeting multiple containers in a single container query isn't feasible.

  • The aspect-ratio feature does not support CSS variables.
  • Experimental style() queries have incomplete browser support.

Snippet:

html,
body {
  background-color: #000;
  height: 100%;
  margin: 0;
  padding: 0;
}

.layout {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.top,
.bottom {
  background-color: #444;
  flex-shrink: 0;
  height: 50px;
}

.middle {
  background-color: #222;
  flex-grow: 1;
  overflow: hidden;
}

.items {
  display: flex;
  gap: 20px;
  height: 100%;
  overflow: hidden;
}

.item {
  align-items: center;
  background-color: #666;
  container-type: size;
  display: flex;
  flex-grow: 1;
  justify-content: center;
}

.block {
  --bw: var(--w);
  --bh: none;
  width: auto;
  width: calc(var(--bw) * 1px);
  height: calc(var(--bh) * 1px);
  max-height: calc(var(--bh)/var(--bh) * 100%);
  aspect-ratio: var(--w) / var(--h);
  background-image: linear-gradient(45deg, orange, purple);
}

.block1 {
  --w: 300;
  --h: 200;
}

@container (aspect-ratio > 300 / 200) {
  .block1 {
    --bw: none;
    --bh: var(--h);
  }
}

.block2 {
  --w: 1920;
  --h: 1080;
}

@container (aspect-ratio > 1920 / 1080) {
  .block2 {
    --bw: none;
    --bh: var(--h);
  }
}

.block3 {
  --w: 1080;
  --h: 1920;
}

@container (aspect-ratio > 1080 / 1920) {
  .block3 {
    --bw: none;
    --bh: var(--h);
  }
}
<div class="layout">
  <div class="top"></div>
  <div class="middle">
    <div class="items">
      <div class="item">
        <div class="block block1"></div>
      </div>
      <div class="item">
        <div class="block block2"></div>
      </div>
      <div class="item">
        <div class="block block3"></div>
      </div>
    </div>
  </div>
  <div class="bottom"></div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to have a full-sized background image within a div element

I need assistance achieving a full-sized background image inside a div. The div is positioned as the last element on the page, and I want it to stretch to the bottom of the page. So far, my code expands the background image to the sides, but when I adjus ...

What is the best way to navigate to the top of a form panel?

I'm currently developing a Sencha Touch mobile app. I have a form panel with multiple fields, so I made it scrollable. However, every time I open the screen (panel), scroll down, navigate to other screens, and then return to the form panel, it stays s ...

How to make a seamless full-width background with CSS

I'm in the process of developing a new website called WebSpace and I'm looking to extend the header background color to span from one end to the other. Currently, the layout appears like this: I want to eliminate the unnecessary white space on t ...

Leveraging CSS selectors and combinators such as (*, ~, >, <, +)

Working with CSS selector symbols has been a challenge for me as I strive to create intricate element selectors. Currently, I find myself at a standstill struggling to combine certain selector symbols together. For example, I am attempting to craft: body ...

"Experiencing technical difficulties: Website not functioning properly on

I recently created this website. While it functions perfectly on desktop computers, I am encountering issues when trying to access it on mobile or tablet devices. My instinct tells me that the problem may be related to a large css animation on the homepa ...

Adjustable height for Material UI tab components

I encountered an issue that I initially thought was related to the general configuration of my app and the height I assigned to my page wrapping classes. However, after creating a simple out-of-the-box material UI tab example, it became clear that this beh ...

"Keep the div locked to the screen when the bottom of the div aligns with the

Hey everyone! I'm currently grappling with a project that requires me to keep a div stuck to the screen (prevent scrolling) when its bottom reaches the bottom of the screen. I have two divs on the page, both with varying heights. My aim is to keep div ...

Aligning pictures in a vertical arrangement across various rows

Is it possible to style a bunch of images in CSS without defining anything in HTML, so that they appear in multiple rows aligned vertically with each row containing 5 or 6 images? The current code using .wrapper displays the images endlessly in one row. C ...

Having trouble displaying my JavaScript objects in distinct columns

I have encountered an issue where my 2 Javascript objects are not displaying in separate columns on the DOM. The requirement is to showcase the details of two individuals on a 2 Column page, with one person's information on each side. My approach inv ...

Adjusting content alignment for mobile devices and centering on desktop screens

I am currently working on creating a responsive website and I have a specific layout in mind. I want to center some text and an image both vertically and horizontally within a div that has a minimum height, leaving plenty of blank space above and below the ...

Footer content is displayed at the center of the page before anchoring itself to

Looking for some assistance here. Every time I refresh the page or open it anew, the footer oddly pops up in the middle before swiftly moving back down to the bottom where it belongs. It's quite bothersome and despite my best efforts, I haven't b ...

Displaying a dynamically populated variable using PHP within an isolated code block

One of the challenges I am facing involves a PHP loop responsible for loading images onto a slider: <div id="slider" class="flexslider"> <ul class="slides"> foreach($imglist as $image) { $caption = // Extracting metadata from each image ec ...

The Bootstrap 4 Dropdown Menu is positioned on the right side of its parent element

Just started using Bootstrap and have a query regarding my Navbar design. Check out my trial website at TEST-DOMAIN Currently utilizing Bootstrap 4.3.1 along with JQuery 3.4.1. The menu looks perfect on Desktop view. The dropdown displays as expected. I ...

jQuery's animation feature is experiencing significant delays when triggered by scrolling

Is there a way to make an image appear and disappear as the user scrolls by using the .animate() method? I have attempted to implement it, but unfortunately, it seems quite slow and sometimes the image does not reappear when scrolling back to the top. Take ...

How can I change the typing position with CSS for the <input> element?

After discovering this online code to add padding for the placeholder, I realized that when typing, the padding doesn't seem to be taking effect. ::-webkit-input-placeholder { padding: 20px; } This is what I see. In the image provided, I drew a bl ...

Aligning/spacing the items in a list vertically

Is it possible to add vertical spacing between specific <li> tags in a navigation list using CSS only, without relying on JavaScript or JQuery? For example: http://jsfiddle.net/ssqnY/ The spacing should adjust responsively based on the height of th ...

Ways to add space around the td element and properly align the content within it

I've recently delved into the realm of web development, and I'm encountering some challenges with HTML alignment. Despite exploring various resources and attempting to utilize align="left"/right/center attributes, I'm still struggling to ach ...

Unable to load CSS in Django

Having recently started using Django, I encountered an issue with loading CSS files. This is the directory structure in my Windows system: Scenario 1: . |__myproject |__+myproject |__+myapp |__-static | |__-css | ...

Experiencing an issue where the canvas element fails to render on mobile Chrome browser,

I've encountered an issue with a script that draws a canvas based on the background color of an image. The image is loaded dynamically from a database using PHP. The responsive functionality works fine on mobile Safari, but not on Chrome. When the re ...

The container div contains a pop-up that expands the height of the container, rather than overlapping on top

I am currently developing a grid system similar to Excel, but I am encountering issues with displaying pop-ups properly as the lower part gets cut off by the container div. This problem is reminiscent of a situation discussed in a Stack Overflow post titl ...