CSS Grid Row Height Safari Bug - html

I've made a grid template with rows of 1fr 1fr 1fr. In the middle row, there are a list of inline images.
In Chrome and Firefox, the images respect the height of the grid row and adapt properly. However, in Safari 10.1.2 and Safari TP 31, there appears to be a combination of the images overflowing the row and not scaling the image widths appropriately.
Perhaps I'm doing something wrong? Or is this a Safari bug? And if so, is there a workaround?
Safari 10.1
Safari TP
Chrome 60
#grid {
height: 100vh;
display: grid;
grid-template-rows: 1fr 1fr 1fr;
}
#thumbnailContainer {
position: inherit;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
}
img {
display: inline;
height: 100%;
width: auto;
}
header,
footer {
background-color: dodgerblue;
}
<div id="grid">
<header>Header</header>
<div id="thumbnailContainer">
<img src="https://c2.staticflickr.com/8/7591/16903911106_b7ced9d758.jpg">
<img src="https://c1.staticflickr.com/9/8740/16927517701_810fcb2a7c.jpg">
<img src="https://c2.staticflickr.com/8/7637/16902583636_15138a68f0.jpg">
<img src="https://c2.staticflickr.com/8/7614/16927530091_6755845b13.jpg">
<img src="https://c1.staticflickr.com/9/8700/16741099010_d0ecd9df1f.jpg">
<img src="https://c1.staticflickr.com/9/8745/16927567841_74fd20d01d.jpg">
</div>
<footer>Footer</footer>
</div>
https://jsfiddle.net/fqkjhh6m/1/

Short Answer
The problem is that Safari is not recognizing the height: 100% on the img elements.
Explanation
This is not a Safari bug. It's just a different interpretation of the spec.
When dealing with percentage heights, some browsers (like Safari) adhere to the traditional interpretation of the spec, which requires a defined height on the parent.
10.5 Content height: the height
property
<percentage>
Specifies a percentage height. The percentage is calculated with
respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the used height is calculated as if auto was specified.
In other words, a percentage height on an in-flow element will be recognized only when the parent has a defined height.
Some browsers, such as Chrome and Firefox, have moved past this interpretation and now accept flex and grid heights as an adequate parent reference for a child with a percentage height.
But Safari is stuck in the past. This doesn't mean it's wrong, invalid or a bug.
The last substantive update to the CSS height definition was in 1998 (CSS2). With so many new CSS properties and technologies since that time, the definition has become obsolete, unclear and woefully incomplete. Until the definition is updated for modern use, browser rendering variations can be expected.
Solution
Since Safari doesn't recognize the height: 100% on the img elements, and you can't specify a height on the parent (#thumbnailContainer) because that height is defined by grid-template-rows: 1fr on the top-level container, you can try using flexbox.
By making #thumbnailContainer a flex container, you can define the size of the images (flex items) using flex properties.
#grid {
height: 100vh;
display: grid;
grid-template-rows: 1fr 1fr 1fr;
}
#thumbnailContainer {
display: flex;
overflow-x: auto;
overflow-y: hidden;
min-width: 0;
min-height: 0;
}
img {
flex: 0 0 35%;
min-width: 0;
object-fit: cover;
}
header, footer {
background-color: dodgerblue;
}
<div id="grid">
<header>Header</header>
<div id="thumbnailContainer">
<img src="https://c2.staticflickr.com/8/7591/16903911106_b7ced9d758.jpg">
<img src="https://c1.staticflickr.com/9/8740/16927517701_810fcb2a7c.jpg">
<img src="https://c2.staticflickr.com/8/7637/16902583636_15138a68f0.jpg">
<img src="https://c2.staticflickr.com/8/7614/16927530091_6755845b13.jpg">
<img src="https://c1.staticflickr.com/9/8700/16741099010_d0ecd9df1f.jpg">
<img src="https://c1.staticflickr.com/9/8745/16927567841_74fd20d01d.jpg">
</div>
<footer>Footer</footer>
</div>
jsFiddle
More information
Working with the CSS height property and percentage values
Chrome / Safari not filling 100% height of flex parent
Why doesn't flex item shrink past content size?
Why isn't object-fit working in flexbox?

Don't ask me why, but wrapping the grid parent with a simple div solved my problem.
This article (https://newbedev.com/why-is-css-grid-row-height-different-in-safari) mentions:
Put display:grid on the div surrounding your grid container.
My problem was solved just by wrapping the grid container with a div. Just wanted to mention another solution. I hope someone can add an explanation for what is happening here.

Related

Overflow works on outer 100% height div but not inner 100% height div

I have a CSS grid with a row div for a header, a row div for content, and a row div for a footer. I want the content to fill the screen but keep the header and footer visible. I have a fixed-size div inside another div inside the content div. Everything except the fixed-size div is height: 100%.
If I apply overflow-y: auto to the content div, the scrollbar appears on the content div. This is great, but what I really want is for the scrollbar to appear on the div inside the content div instead.
https://jsfiddle.net/efth2akr/2/
If I apply overflow-y: auto to the div inside the content div instead, there is no scrollbar and the content div takes on the height of the fixed-size div. This pushes the footer down and puts a scrollbar on the whole page. What?? Isn't height: 100% supposed to be based on the parent height? Other questions that describe similar scenarios fail to put height: 100% all the way up the chain, but I haven't.
https://jsfiddle.net/t08u9wnk/2/
How can I achieve my desired behavior of having the scrollbar appear on the div inside the content div while maintaining a responsive layout? What am I not understanding about height: 100% in this scenario?
Browser: Microsoft Edge 103.0.1264.62
I think the approach you are trying to follow would work only if you set a fixed height or max-height to div.grid-content > div. Now the problem is that the height of this container is dynamic and depends on height: 100%, so you might be tempted to do max-height: 100% but here is a good reason for why this is not possible.
If you want div.grid-content > div to have a scroll and keep the layout of the entire page based on the height of the screen, I'd propose you to use an absolute positioned overlay.
Here is a modified snippet of your code explaining how it works:
html, body {
height: 100%;
width: 100%;
}
body {
/* prevents scroll on the entire page */
overflow: hidden;
}
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div.grid {
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 1fr;
height: 100%;
width: 100%;
}
div.grid-header, div.grid-footer {
background-color: #ff0000;
}
div.grid-content {
background-color: #00ff00;
/* set relative positioning to contain absolute child */
position: relative;
}
div.grid-content > div {
/* this is strategy for creating a container that adapts to the size of its relative parent using absolute positioning without the need of setting a fixed height to div.grid-content */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* add scroll on y axis */
overflow-y: auto;
}
div.grid-content > div > div {
height: 200vh;
width: 50%;
background-color: #0000ff;
}
<div class="grid">
<div class="grid-header">
<h3>
hi
</h3>
</div>
<div class="grid-content">
<div>
<div>
asdf
</div>
</div>
</div>
<div class="grid-footer">
<h3>
bye
</h3>
</div>
</div>
I have been bitten by this unintuitive design choice of CSS grid: https://github.com/w3c/csswg-drafts/issues/1777
In summary, CSS grid "1fr" behaves like minmax(auto, 1fr) (where 1fr is the actual fractional dimension of the grid). The minimum width/height of a "1fr" grid row/column is the auto width/height. If the auto width/height is larger than 1fr, the whole row/column will expand out past 1fr because minmax ignores the max when min > max!
This is why my problem has the same symptoms as all the other questions about height: 100% not working where the answer was that they had an ancestor with height: auto. It turns out 1fr can become auto!
Replacing:
grid-template-rows: auto 1fr auto;
with:
grid-template-rows: auto minmax(0, 1fr) auto;
fixes the problem.
https://jsfiddle.net/t08u9wnk/3/

CSS grid row overflows its container vertically

I would like to have a grid layout on a page where the grid stretches out to the entire viewport, and the rows have a minimum height. The simplest example would be a grid with a single cell (see code snippet below).
The problem I am having is that when the height of the viewport is less than the defined minimum row-height, the row vertically overflows its container. With the added red and green borders in the below example it's visible that the row's height isn't going below the defined 500 pixels, but the grid-container is still sized to the viewport which is now shorter than 500 pixels.
If I remove the height CSS attribute from the grid class, the container doesn't shrink below its content, but it also doesn't fill out the vertical space when the viewport is taller than 500 pixels. Since I want the grid to fill the entire page, I need the height CSS attribute. I've also added the min-height: fit-content attribute which is supposed to prevent the used value of the height property from becoming smaller than the value specified for min-height but it doesn't work (not with the defined fit-content value - it works as expected with an exact value, for example 300px).
In a similar question the culprit was the percentage values used for the gaps, but in this case there is nothing relatively sized. Even if replace the grid-template-rows: minmax(500px, 1fr); property with the fixed grid-template-rows: 500px;, it still behaves the same way.
body {
margin: 0;
}
.grid {
display: grid;
grid-template-rows: minmax(500px, 1fr);
height: 100vh;
min-height: fit-content;
width: 100vw;
}
.bordered {
border: 10px solid green;
}
<div class="grid bordered" style="border-color: red;">
<div class="bordered">Some content</div>
</div>
What I would like to have is a grid that fills out the entire viewport and where the grid-container is never smaller than its content. What am I missing?
Something to know is that as soon as a min height of a row, or the combined height of multiple rows, is greater than the height of the viewport, you will have a scroll. Beyond that, the snippet below, I hope does what you are looking for. I added comments in the code.
/* lines I added */
*{
box-sizing: border-box;
}
body {
margin: 0;
}
.grid {
display: grid;
/* 100 is for the small viewport here in the code snippet */
grid-template-rows: repeat(auto-fit, minmax(100px, 1fr));
min-height : 100vh;
}
.bordered {
border: 10px solid green;
}
<div class="grid bordered" style="border-color: red;">
<div class="bordered">Some content</div>
</div>

Wrong height of youtube-iframe inside flexbox item [duplicate]

I want to have a square div inside a flexbox. So I use:
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50%;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
This works fine in Chrome. But in Firefox, the parent squeezes to just one line.
How do I solve this in Firefox? I use version 44.
You can also view the code at https://jsbin.com/lakoxi/edit?html,css
2018 Update
The flexbox specification has been updated.
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items, like those on block
boxes, are resolved against the inline size of their containing block,
e.g. left/right/top/bottom percentages all resolve against their
containing block’s width in horizontal writing modes.
Original Answer - applies to FF and Edge versions released before 2018
From the flexbox specification:
Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.
Here's some more:
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items can be resolved against either:
their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
the inline axis (left/right/top/bottom percentages all resolve against width)
A User Agent must choose one of these two behaviors.
Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.
In addition to Michael_B's answer, here is a possible workaround.
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be an option, since it works similar (responsive).
Stack snippet
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50vw;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
Updated based on a comment
If a square is a must, and viewport units or script can't be used, here is another trick using a dummy image.
Note, as image also a SVG or a Base64 could be used as a datauri to save an extra round trip to the server
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
}
.inner img {
display: block;
width: 100%;
visibility: hidden;
}
<div class="outer">
<div class="inner">
<img src="http://placehold.it/10" alt="">
</div>
</div>

How do I properly align buttons to the bottom of the parent? [duplicate]

I want to have a square div inside a flexbox. So I use:
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50%;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
This works fine in Chrome. But in Firefox, the parent squeezes to just one line.
How do I solve this in Firefox? I use version 44.
You can also view the code at https://jsbin.com/lakoxi/edit?html,css
2018 Update
The flexbox specification has been updated.
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items, like those on block
boxes, are resolved against the inline size of their containing block,
e.g. left/right/top/bottom percentages all resolve against their
containing block’s width in horizontal writing modes.
Original Answer - applies to FF and Edge versions released before 2018
From the flexbox specification:
Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.
Here's some more:
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items can be resolved against either:
their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
the inline axis (left/right/top/bottom percentages all resolve against width)
A User Agent must choose one of these two behaviors.
Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.
In addition to Michael_B's answer, here is a possible workaround.
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be an option, since it works similar (responsive).
Stack snippet
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50vw;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
Updated based on a comment
If a square is a must, and viewport units or script can't be used, here is another trick using a dummy image.
Note, as image also a SVG or a Base64 could be used as a datauri to save an extra round trip to the server
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
}
.inner img {
display: block;
width: 100%;
visibility: hidden;
}
<div class="outer">
<div class="inner">
<img src="http://placehold.it/10" alt="">
</div>
</div>

Why doesn't percentage padding / margin work on flex items in Firefox and Edge?

I want to have a square div inside a flexbox. So I use:
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50%;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
This works fine in Chrome. But in Firefox, the parent squeezes to just one line.
How do I solve this in Firefox? I use version 44.
You can also view the code at https://jsbin.com/lakoxi/edit?html,css
2018 Update
The flexbox specification has been updated.
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items, like those on block
boxes, are resolved against the inline size of their containing block,
e.g. left/right/top/bottom percentages all resolve against their
containing block’s width in horizontal writing modes.
Original Answer - applies to FF and Edge versions released before 2018
From the flexbox specification:
Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.
Here's some more:
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items can be resolved against either:
their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
the inline axis (left/right/top/bottom percentages all resolve against width)
A User Agent must choose one of these two behaviors.
Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.
In addition to Michael_B's answer, here is a possible workaround.
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be an option, since it works similar (responsive).
Stack snippet
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50vw;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
Updated based on a comment
If a square is a must, and viewport units or script can't be used, here is another trick using a dummy image.
Note, as image also a SVG or a Base64 could be used as a datauri to save an extra round trip to the server
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
}
.inner img {
display: block;
width: 100%;
visibility: hidden;
}
<div class="outer">
<div class="inner">
<img src="http://placehold.it/10" alt="">
</div>
</div>