Cross-browser grid of images nested in flex container - css

Having a fixed-height, variable width grid of 'oversized' images (simplified: 2 images only, different only by orientation. In reality, these have various sizes and aspect ratios) inside a flex container (simplified: body) yields different results depending on the browser:
body {
display: flex;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 50px;
}
.grid>img {
width: 100%;
max-height: 100%;
border: 1px solid red;
object-fit: cover;
}
<body>
<div class="grid">
<img alt="portrait"
src="data:image/png;base64,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" />
<img alt="landscape"
src="data:image/png;base64,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" />
</div>
</body>
The expected result (both same size, aspect ratio preserved, cover) in Chrome 108.x:
and the result in FF 108.x:
I tried playing with various 'settings' (such as object-fit: cover, aspect-ratio: 1 / 1, nesting the images / the grid in divs with e.g. display: block, using reset.css or normalize.css, ...) but could not find a working solution.
What do I need to do to get the Chrome behaviour in FF?

Since width of 50px seems to matter, use grid-template-columns instead of -rows, and for the image use aspect-ratio: 1; and if you want object-fit: cover; to prevent image distortions
.grid {
display: grid;
grid-template-columns: 50px;
}
.grid>img {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
border: 1px solid red;
}
<div class="grid">
<img alt="width of img > height of row" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAADUExURf///6fEG8gAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAAcSURBVFjD7cEBDQAAAMKg909tDwcEAAAAAByoARPsAAFwJuooAAAAAElFTkSuQmCC"
/>
</div>

Related

css position two divs diffrently depending on screen size

I have project where I need to display two div's. Both div's have the same width and this width is a set width. So their width doesn't scale with the screen size.
The problem I am facing is that when they are large enough to fit next to each other they need to be positioned with space in between. Both outer sides of the the div's need to touch the sides of the screen.
But when the window scales down so that they are to big to fit next to each other. Then they need to be positioned in the center of the screen underneath each other.
I have tried with flexbox, flex-wrap and justify content: space between. But when the window scales down they are both positioned at the left side of the screen and not in the center.
Here is how the should look when the screen is large enough:
And when the screen is so small that they don't fit next to each other:
Using CSS-Grids, this problem is done much cleanly:
Media query is set so perfectly. No margins needed. We just need to use min-width equal to the sum of widths of our two divs.
.wrap {
width: 100vw;
height: 100vh;
display: grid;
grid-template-columns: 1fr;
background: green;
}
.wrap div {
height: 200px;
width: 200px;
background: black;
justify-self: center;
}
#media (min-width: 400px) {
/* min-width is calculated by width of two divs 2*200*/
.wrap {
grid-template-columns: 1fr 1fr;
}
.wrap div:first-child {
justify-self: start;
}
.wrap div:last-child {
justify-self: end;
}
}
<div class="wrap">
<div></div>
<div></div>
</div>
It's not very clear what you're trying to achive. Maybe you can do something like this:
<div class="wrap">
<div></div>
<div></div>
</div>
and
.wrap{
width: 100vw;
/* higher than 2 x box height */
height: 100vh;
display:flex;
justify-content:space-between;
background: green;
}
.wrap div{
height: 200px;
width: 200px;
background: black;
}
#media (max-width: 450px ){
/* 450px = 2 * box-size + min-gap-width(50) */
.wrap{
align-items:center;
flex-direction: column;
}
.wrap div{
margin: 50px;
}
}
codepen-demo

CSS grid dimensions

I'd like to know if there is a way to limit the dimensions of a grid (not the element to which it's applied, the grid itself). I explain :
Let's say we've got a footer, with a dark background, spanning all the width of your page.
This footer contains 2 sub-blocks, classnames .footer__1 and .footer__2, and we want them to occupy 2/3 and 1/3 of the available width, with a 2rem gutter.
Is there a way to give the grid containing the two sub-blocks a grid-max-width (yes, i made that up) of 60rem, without changing the block width (remember, it has a background which must be full width), without using a wrapper ?
So, the occupied width (.footer__1 + gap + .footer__2) should be 60rem max, centered horizontally.
<footer>
<div class="footer__1">foo</div>
<div class="footer__2">whatever</div>
</footer>
.footer {
display: grid;
grid-template_columns: 2fr 1fr;
grid-column-gap: 2rem;
/* grid-max-width: 60rem */
}
Thanks
You have to wrap it with an additional element and limit the width to the additional element.
footer {
background-image: linear-gradient(20deg, red, pink);
}
.container {
display: grid;
grid-template-columns: 2fr 1fr;
grid-column-gap: 2rem;
/* just for demo purpose */
max-width: 300px;
margin: 0 auto;
min-height: 150px;
background: white;
}
<footer>
<div class="container">
<div class="footer__1">foo</div>
<div class="footer__2">whatever</div>
</div>
</footer>
Without another container, I think it is not possible.
However, you can mimic it by making the footer width: 60rem and set the background as a pseudo-element and expand it.
footer {
position: relative;
z-index: 1;
width: 60rem;
margin: 0 auto;
}
footer:before {
content: '';
position: absolute;
top: 0;
left: 50%;
z-index: -1;
transform: translateX(-50%);
width: 9999px;
height: 100%;
background: green;
}
<footer>
<div class="footer__1">foo</div>
<div class="footer__2">whatever</div>
</footer>
The best approach, as I think, is to add div.footer__inner that will hold the two divs - .footer__1 and .footer__2.

How do I create a simple title bar containing a logo, and preserve the logo's aspect ratio?

I'm trying to create a simple page with a title bar at the top, and a logo in the left of the title bar. I'm new to flexbox and grid and not great at CSS, but it seems sensible and idiomatic to use grid for the main layout, give the title bar a sensible height, and then use flexbox for the title bar itself. But I keep getting results that aren't quite right.
Attempt #1 had a huge problem with image scaling. After reading the spec the scaling seemed like a cyclic dependency caused by one </div> too many. Also I hadn't yet heard of object-fit to control the image aspect ratio.
Attempt #2 is mostly fine. The only issue is the 'letterboxing' of the image: the image content is fine but its box is too wide, due to how margins are handled. Letterboxing is exactly what object-fit: content and object-fit: scale-down are defined to do, but it's not what I want.
Attempt #3 looks just how I want it. But I only achieved that by introducing variables and explicitly setting image width and height, which I was hoping to avoid by adopting grid + flexbox. It also relies on knowing the image's aspect ratio and to sizing accordingly.
Is there a simple way to include an image in my title bar, to preserve the image's ratio, and to avoid letterboxing?
Snippets below. Thanks!
Attempt #1
* {
box-sizing: border-box;
}
.titleBar {
border: 1px solid orange;
align-items: center;
display: grid;
grid-template-rows: 150px;
height: 100%;
}
.titleBar div {
display: flex;
}
.titleBar img {
border: 5px solid lightgray;
max-height: 100%;
margin: 25px;
}
<html>
<body>
<div class="titleBar">
<div>
<img src='https://www.fillmurray.com/600/600' />
</div>
</div>
</body>
</html>
Attempt #2
* {
box-sizing: border-box;
}
body {
align-items: center;
display: grid;
grid-template-rows: 150px;
}
.titleBar {
border: 1px solid orange;
display: flex;
height: 100%;
}
.titleBar img {
border: 5px solid lightgray;
max-height: 100%;
margin: 25px;
object-fit: scale-down;
}
<html>
<body>
<div class="titleBar">
<img src='https://www.fillmurray.com/600/600' />
</div>
</body>
</html>
Attempt #3
* {
box-sizing: border-box;
font-family: 'Courier New', Courier, monospace;
--border-size: 1px;
--row-height: 150px;
--img-margin: 10px;
}
body {
display: grid;
grid-template-rows: var(--row-height);
}
.titleBar {
border: 1px solid orange;
align-items: center;
display: flex;
}
.titleBar img {
border: 5px solid lightgray;
margin: var(--img-margin);
max-height: calc(var(--row-height) - 2*var(--img-margin));
max-width: calc(var(--row-height) - 2*var(--img-margin));
object-fit: scale-down;
}
<html>
<body>
<div class="titleBar">
<img src='https://www.fillmurray.com/600/600' />
</div>
</body>
</html>
I think this is solved. There were 2 issues, one big and one little.
Issue #1 - the default align-items for flexbox is stretch. Since I'm taking up the whole height I didn't think that mattered, but deep in the W3C CSS Box Alignment Spec (section 6.1) it says this about stretch:
Note: The stretch keyword can cause elements to shrink, to fit their container.
This was causing my logo container to shrink vertically which caused letterboxing. Changing to align-items: center fixed that.
Issue #2 was less of a problem and more straightforward. E.g. I was placing an image with a 5:1 aspect ratio, a 10px border and a max-height of 100% inside a grid row with a fixed height of 100px. The layout engine calced a box of 500x100. Perfect. However, once the 10px borders were laid out, that left a content area of 480x80, and that's not 5:1. So it had no choice but to stretch, clip, or letterbox the image, depending on object-fit.
I'm not sure if my solution to Issue #2 is the best, but I decided to go with box-sizing: content-box, and to calc my height as 100% - 2*border size. Everything works, for all images of all aspect ratios, and I never have to specify width.
Snippet with 3 examples with 3 different aspect ratios:
* {
box-sizing: border-box;
}
body {
align-items: center;
border-top: 1px dotted gray;
display: grid;
grid-template-rows: 100px 100px 100px;
margin: 20px 0px 0px 0px;
;
padding: 0px;
row-gap: 25px;
}
.titleBar2 {
border: 1px solid orange;
display: flex;
height: 100%;
align-items: center;
margin: 0px;
column-gap: 25px;
}
.titleBar2 img {
box-sizing: content-box;
--border: 10px;
background-color: pink;
border: var(--border) solid lightgray;
max-height: calc(100% - 2*var(--border));
margin: 0px;
object-fit: contain;
}
<html>
<body>
<div class="titleBar2">
<img src='https://www.fillmurray.com/1200/600' />
<a href='https://www.fillmurray.com/1200/600' target='new'>https://www.fillmurray.com/1200/600</a>
</div>
<div class="titleBar2">
<img src='https://www.fillmurray.com/600/1200' />
<a href='https://www.fillmurray.com/600/1200' target='new'>https://www.fillmurray.com/600/1200</a>
</div>
<div class="titleBar2">
<img src='https://www.fillmurray.com/2000/400' />
<a href='https://www.fillmurray.com/2000/400' target='new'>https://www.fillmurray.com/2000/400</a>
</div>
</body>
</html>

items based on container height

I have a nice little table with 4 elements which contain images.
The images are usually uploaded by users, so I don't have exact control over the image size.
What I am trying to do is create a 2 x 2 layout which resizes to fit the users screen and each box in the layout is given a 16:9 aspect ratio.
This works really well adjusting the window width, but if the user adjusts the height, the elements overflow rather than adjusting in height to fit the users screen.
You can see example here, and if you adjust your screen the horizontal width behavior is what I'm looking for, but adjusting vertically hides the images on smaller window sizes.
https://codepen.io/anon/pen/zaXEOL
.outer-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
max-height: 70vh;
max-width: 70vw;
overflow: hidden;
}
.holder {
border: 1px solid red;
max-width: 46%;
max-height: 46%;
margin: 1%;
display: flex;
align-items: center;
justify-content: center;
height: 0;
padding-top: 26%;
width: 100%;
position: relative;
}
img {
object-fit: contain;
max-height: 100%;
top: 0;
max-width: 100%;
width: 100%;
height: 100%
<div class="outer-grid">
<div class="holder">
<img src="http://fillmurray.com/200/300"/>
</div>
<div class="holder">
<img src="http://fillmurray.com/300/300"/>
</div>
<div class="holder">
<img src="http://fillmurray.com/300/200"/>
</div>
<div class="holder">
<img src="http://fillmurray.com/250/300"/>
</div>
</div>
The css I am using is fairly simple
.outer-grid {
display: grid;
grid-template-columns: repeat(2,1fr);
grid-template-rows: repeat(2,1fr);
grid-gap: 1rem;
max-height: 70vh;
max-width: 70vw;
overflow: hidden;
}
.holder {
border: 1px solid red;
max-width: 100%;
max-height: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 0;
padding-top: 56%;
position: relative;
}
img {
position: absolute;
display: block;
top: 0;
max-height: 100%;
max-width: 100%;
}
First, note that your layout doesn't work at all in Firefox and Edge. This is because the percentage padding trick you are using, when applied in a grid container, doesn't work in all browsers. Here is a detailed explanation: Percentage padding / margin on grid item ignored in Firefox
Second, percentage padding re-sizes images based on their width. That's the whole trick.
From the spec:
ยง 8.4 Padding properties: padding-top, padding-right,
padding-bottom, padding-left, and
padding
<percentage>
The percentage is calculated with respect to the width of the
generated box's containing block, even for padding-top and
padding-bottom.
The images can re-size on the horizontal axis because percentage padding is associated with width. They can't re-size on the vertical axis because percentage padding has no association with height.

CSS grid doesn't seem to work properly on mobile devices

I want to use a simple layout, that consists of
An image
Some text
The text should be on the bottom and all remaining space should be used by the image. It seemed a simple task, but it has been a frustrating journey. Finally, I nailed but this solution doesn't work on mobile devices (Android and/or Chrome).
JSFiddle here
It looks like this on the desktop:
Unfortunately, the iPad renders it like this:
The text is barely visible, because the image took all the space. On Android (with Chrome 65 installed) it shows some of the text, but not all of it.
I use the following HTML code
<html>
<body>
<h1>TEST 123</h1>
<div class="wrapper img-top">
<img class="image" src="..." />
<div class="text">
<h2>Header</h2>
<p>...</p>
</div>
</div>
</body>
</html>
The relevant parts of the CSS looks like this (full code on JSFiddle):
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
height: 100vh;
overflow: hidden;
}
.wrapper {
display: grid;
grid-gap: 10px;
height: calc(100vh - 85px);
margin-bottom: 20px;
text-align: center;
align-items: center;
}
.wrapper.img-top {
grid-template-rows: 1fr auto;
}
.wrapper.img-top .image {
grid-row: 1;
justify-self: center;
}
.wrapper.img-top .text {
grid-row: 2;
justify-self: center;
}
.wrapper img.image {
max-height: 100%;
max-width: 100%;
}
I guess that there is a problem with the automatic image sizing, but I don't know what to do next? I want to have it responsive, but it just is not going to work.
Your problem is that you put fixed height on img-top. Your img is, let's say, 900 px, it's not the same on screen as iPad and desktop. Since you put your image to be 1 fr of height, it is getting bigger as screen is getting smaller. So you have two solutions, as far as I can tell, first is to restrict the size of your image, max-height: 300px;, or try putting overflow auto on .img-top.
Thanks #Jakub Muda for the suggestion about using flexboxes. I have finally fixed the problem by moving to a flexbox and fix the remaining issues.
After switching to a flexbox, I kept the same problem. The image was sized to 100% of the size of the parent wrapper div. I have fixed this by adding overflow: hidden to the image, so it will not overflow. This worked fine, but some images were distorted, because the aspect-ratio wasn't properly maintained. Adding object-fit: contain fixed this issue.
The complete sample can be found at JSFiddle, but for completeness I have added the CSS here as well:
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
height: 100vh;
overflow: hidden;
}
.wrapper {
display: flex;
flex-direction: column;
height: calc(100vh - 85px);
margin-bottom: 20px;
text-align: center;
align-items: center;
}
.wrapper .image {
justify-self: center;
flex: auto;
overflow: hidden; /* Important to make sure the image stays within bounds */
object-fit: contain; /* Important to make sure the image keeps its aspect ratio */
}
.wrapper .text {
justify-self: center;
flex-basis: none;
padding-top: 10px;
}
.wrapper img.image {
max-height: 100%;
max-width: 100%;
}

Resources