in the css-tricks complete-guide-to-css-grid it describes the justify-content property as follows:
justify-content Sometimes the total size of your grid might be less
than the size of its grid container. This could happen if all of your
grid items are sized with non-flexible units like px. In this case you
can set the alignment of the grid within the grid container. This
property aligns the grid along the inline (row) axis (as opposed to
align-content which aligns the grid along the block (column) axis).
Im trying to wrap my head around how this works. If I have a grid container with width: 100% and grid items with fixed width width: 20px and I set the grid container to justify-items: start the grid items will, as expected justify to the start. If however, I set the grid container to `grid-template-columns: repeat(4, 1fr) then the items no longer justify to the start? even though the items are sized with non-flexible units?
According to the css-tricks description I would have expected them to justify to the start of the row axis?
.wrapper{
width: 100%;
height: 100vh;
}
.grid{
display: grid;
grid-auto-flow: column;
justify-content: start;
align-content: start;
width: 100%;
padding: 1em;
background-color: powderblue;
}
.grid.explicit{
grid-template-columns: 1fr 1fr 1fr 1fr;
}
.grid__item{
width: 20px;
padding:0.6em;
background-color: lime;
}
<div class="wrapper">
Explicit columns
<pre>
grid-template-columns: 1fr 1fr 1fr 1fr;
</pre>
<div class="grid explicit">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
Implicit columns
<div class="grid">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
From the specification
If the grid’s outer edges do not correspond to the grid container’s content edges (for example, if no columns are flex-sized), the grid tracks are aligned within the content box according to the justify-content and align-content properties on the grid container.
So if any column is using 1fr, justify-content is useless.
To use easy words: You grid container contain tracks (columns) that are sized either based on grid-template-columns or automatically. When using 1fr 1fr 1fr 1fr You define 4 equal tracks taking all the available space. There is no free space so there is nothing to align because justify-content align the columns not the item inside them (that are equal to 20px).
When you don't define any size, the columns will get auto sized based on their content and in this case you will have tracks having their size equal to 20px and inside them items also equal to 20px. In this case, justify-content will align your columns.
Worth to note that the default alignment is stretch so in the second case if you omit the justify-content, your auto sized column will be stretched to fill all the space equally to have a similiar behavior as 1fr.
to better understand, here is some illustration:
.grid {
display: grid;
grid-auto-flow: column;
justify-content: start;
margin: 1em;
background-color: powderblue;
}
.grid__item {
width: 20px;
padding: 0.6em;
background-color: lime;
}
4 equal columns
<div class="grid" style="grid-template-columns: 1fr 1fr 1fr 1fr">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
4 equal columns having 100px width (we have free space)
<div class="grid" style="grid-template-columns: 100px 100px 100px 100px">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
we align the items inside the columns
<div class="grid" style="grid-template-columns: 100px 100px 100px 100px;justify-items:center">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
<div class="grid" style="grid-template-columns: 100px 100px 100px 100px;justify-items:end">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
we move the columns to the end
<div class="grid" style="grid-template-columns: 100px 100px 100px 100px;
justify-content: end;justify-items:end">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
We make the column auto sized (nothing to align inside them)
<div class="grid" style="justify-items:center">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
<div class="grid" style="justify-items:end">
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
<div class="grid__item">item</div>
</div>
Related
a grid with five columns
col_space are equal and flexible and they are just for space
col_1 and col_2 are content columns and fixed size
question - can I have the same funcionality without col_space at all ?
just like this:
<div class='wrap'>
<div class='col_1'>lorem</div>
<div class='col_2'>ipsum</div>
</div>
.wrap{
display:grid;
grid-template-columns:1fr 99px 1fr 54px 1fr;
height:50vh;
}
.col_space{background:lightblue;}
.col_1, .col_2{background:orange;}
<div class='wrap'>
<div class='col_space'></div>
<div class='col_1'>lorem</div>
<div class='col_space'></div>
<div class='col_2'>ipsum</div>
<div class='col_space'></div>
</div>
space-evenly on justify—content will do this,
.wrap{
display:grid;
grid-template-columns:99px 54px;
height:50vh;
justify-content: space-evenly;
}
.wrap{background:lightblue;}
.col_1, .col_2{background:orange;}
<div class='wrap'>
<div class='col_1'>lorem</div>
<div class='col_1'>ipsum</div>
</div>
Background
I am trying to build a page with main section containing a dynamic grid in between a header and footer. As users use the page, book shaped .grid-item divs are appended to the .grid. Initially, when the .grid is empty, I want the <main> to expand to fill the page space so the footer isn't floating half way up the page. As the grid grows beyond the confines of this space, I want the page to grow vertically so that it scrolls.
Book shape
By book shaped, I mean that the column should be 2/3 the size of the height for each grid item.
Problem
The problem is that only the columns of the grid are behaving as intended with the items equally sized. In the row direction, only the first row is working as intended and the following rows are only the height of the content. As a result, they are no longer book shaped.
Solutions tried
I added flex: 1 to the <main> so that it can grow as needed. I also used grid-template-rows: repeat(auto-fill, minmax(300px, 1fr)); so that it would auto-fill the grid with equally sized rows.
Following this, I tried to auto-fill the grid with static sized columns and rows:
grid-template-columns: repeat(auto-fill, 200px);
grid-template-rows: repeat(auto-fill, 300px);
After this, I did some reading on the repeat() function and auto-fill to see where my misunderstanding is. I also searched stack-overflow for similar issues and attempted some mentioned solutions like changing my flex-grow and flex-basis for the <main> but I could not resolve the issue.
Closest Solution
I found that if I change the rows explicitly instead of using auto-fill, then the rows work as intended.
grid-template-rows: repeat(12, minmax(250px, 1fr));
This would however require for me to use javascript to change the styling of the .grid as I append children. This solution doesn't work because if the page width changes so that there are fewer columns, the rows would increase automatically, so it would add a lot of extra work to calculate the required rows using javascript before changing the styling. For this reason, I am ideally looking for a CSS only solution.
body {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 0;
}
header, footer {
height: 40px;
}
main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
background-color: grey;
width: 100%;
}
.grid {
display: grid;
width: 60vw;
margin: 5px var(--margin-size) 5px var(--margin-size);
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: repeat(auto-fill, minmax(300px, 1fr));
gap: 10px;
}
.grid-item {
background-color: red;
}
<body>
<header>
Header
</header>
<main>
<div class="grid">
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
</div>
</main>
<footer>
Footer
</footer>
<body>
My question is how can I make the grid grow beyond the confines of the screen while retaining the book shape of the grid children?
I was able to get it working as intended with:
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: minmax(300px, 1fr);
Working snippet below:
body {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 0;
}
header, footer {
height: 40px;
}
main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
background-color: grey;
width: 100%;
}
.grid {
display: grid;
width: 60vw;
margin: 5px var(--margin-size) 5px var(--margin-size);
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: minmax(300px, 1fr);
gap: 10px;
}
.grid-item {
background-color: red;
}
<body>
<header>
Header
</header>
<main>
<div class="grid">
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
<div class="grid-item">
Grid Item
</div>
</div>
</main>
<footer>
Footer
</footer>
<body>
I'm trying to arrange a set of statistics such that:
they are displayed on a single horizontal line
the enclosing element is no wider than it needs to be to contain the content
there should be a fixed gap between statistics
I tried implementing this using display: grid. Here is my approach:
.outer {
background-color: yellow;
display: inline-block;
}
.stats {
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
<div class="outer">
<div class="stats">
<div class="stat">
<strong>Value:</strong> 1,234,568
</div>
<div class="stat">
<strong>Another value:</strong> 98,765
</div>
<div class="stat">
<strong>Test value:</strong> 83,263
</div>
</div>
</div>
Unfortunately, this results in some strange overlapping. Despite there being plenty of room for the .outer element to expand, the statistics are wrapped over several lines and the text runs into other columns:
How can I avoid this problem? I tried adding:
.stat {
white-space: nowrap;
}
...but the text still "runs" together. What am I missing?
The main problem stems from this declaration:
grid-template-columns: repeat(auto-fit, minmax(0, 1fr))
You're setting the columns to shrink to zero width.
Also, 1fr does nothing here, because there is no free space in the container. (You have the primary container set to inline-block, i.e., min-width. This means no extra space.)
At a minimum, these commands appear to be confusing the inline-block algorithm.
Consider leaving the columns at auto width (a default setting).
And, perhaps, setting them to appear in the first row. (I used the grid-auto-flow: column technique.)
.outer {
background-color: yellow;
display: inline-block;
}
.stats {
display: grid;
grid-gap: 20px;
grid-auto-flow: column;
}
<div class="outer">
<div class="stats">
<div class="stat">
<strong>Value:</strong> 1,234,568
</div>
<div class="stat">
<strong>Another value:</strong> 98,765
</div>
<div class="stat">
<strong>Test value:</strong> 83,263
</div>
</div>
</div>
Problem details
I wrote code like this. Here, the image should be spread by the space reserved by fr because thewidth, height, andobject-fit properties work. So I thought the text of the second .item would overflow.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
In Firefox this causes text to run out,
Not so with Chrome.
Also, wrapping the image in a div element instead of just below the grid item will fix the problem.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<div class="content">
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div class="content">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
</div>
Unrelated but similar articles
I found a question that might be related, but I don't think this problem is related to my question because the source code of this question text works correctly in Firefox and Chrome.
Chrome / Safari not filling 100% height of flex parent
Heights rendering differently in Chrome and Firefox
Using 'height: 100%' and 'align-items: stretch' in flexbox
Why is my Grid element's height not being calculated correctly?
Unrelated but similar bugs
I also looked for related bug tickets, but these were already stated to have been fixed and may not be relevant to my problem.
https://bugs.chromium.org/p/chromium/issues/detail?id=426898
https://github.com/philipwalton/flexbugs/issues/197
Quesiton
Is this difference a bug without a bug ticket or undefined behavior?
If this is not an undefined behavior, which is the correct behavior?
If this is not an undefined behavior, which is the correct behavior?
I would say, Firefox is doing correctly here and Chrome is half correct.
First, you can reduce your code to the following as grid-template-rows: 1fr is not needed to have the behavior
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Then you can use any percentage value (either smaller or bigger than 100%) and you will see that chrome will do nothing in the second case where we have text:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 50%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
As I explained here you are facing a particular behavior of percentage height. By default percentage height is relative to the explicit height of the containing block and in our case we didn't set any explicit height to our row (1fr isn't explicit). Considering the CSS2 specification we should fail to auto
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 value computes to 'auto'. ref
Chrome is doing this with the second image. It consider the height to be auto which is not completely wrong.
In the CSS3 specification we have another section that allow the browser to do more effort:
Sometimes the size of a percentage-sized box’s containing block depends on the intrinsic size contribution of the box itself, creating a cyclic dependency. When calculating the intrinsic size contribution of such a box (including any calculations for a content-based automatic minimum size), a cyclic percentage—that is, a percentage value that would resolve against a containing block size which itself depends on that percentage—is resolved specially: ref
Then you have a set of complex rules and the two importants parts are:
Otherwise, the percentage is resolved against the containing block’s size. (The containing block’s size is not re-resolved based on the resulting size of the box; the contents might thus overflow or underflow the containing block).
And one rule with the note:
Note: Grid items and flex items do allow percentages to resolve in this case.
To make it easy, the browser will first ignore the percentage height to calculate the height of each grid track based on the content to have the following:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
/*height: 100%;*/
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Then considering the previous calculated height of grid track we resolve the percentage height to get the following and we don't get back to recalculate the height of the track again:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
The text will logically overflow since the height was defined by the image+text and we told to the image to take all the height making the text outside and making the image filling all the space.
Using any percentage value will give the same result which is make the image X% of the height defined by the image+text.
Setting an explicit height will make both behave the same since we have no complex calculation and we can rely on the CSS2 part of the specification:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows:200px;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="item">
<img src="https://unsplash.it/450/450">
</div>
<div class="item">
<img src="https://unsplash.it/450/450">
text
</div>
</div>
Adding an extra wrapper will make the height fail to auto in all the cases as it become more complex1 for browsers to handle such case.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 958%;
object-fit: cover;
}
<div class="container">
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
text
</div>
</div>
Adding height:100% to the extra wrapper will put as back to the previous cases and height can be resolved again:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
background: red;
gap: 10px;
}
img {
width: 100%;
height: 50%;
object-fit: cover;
}
.item>div {
height: 100%;
}
<div class="container">
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
</div>
<div class="item">
<div>
<img src="https://unsplash.it/450/450">
</div>
text
</div>
</div>
In this case, the div is taking the height defined by the image+text (the text will overflow) then the image inside the div will take half that height.
Related questions with similar situations:
https://stackoverflow.com/a/52137966/8620333
Chrome / Firefox percentage height differences in CSS Grid
Grid gap percentage without height
1: I won't be able to give the exact part explaining why we cannot resolve in this case.
Suppose we have a responsive grid container with indefinite number of child cells. Cells' widths and heights vary. Using only CSS (probably CSS Grid), how can we create such grid, that number of columns / rows and the width / height of each column / row is determined dynamically based on the container's size (without overflowing it) and cells' sizes in one of the following two ways:
Width / height for each column / row is determined based on the widest / tallest cell in that column / row,
Width / height for all the columns / rows is determined based on the widest / tallest cell in the grid?
When applied to column width, these two cases loosely correspond to, respectively, automatic and fixed layout algorithms for tables. Except we don't know the number of columns and rows; it needs to be somehow determined automatically.
The following examples demonstrate these two cases applied to column width. For each case there are two possible flow directions: row or column. Note that in the examples we had to set the number of columns and their sizes specifically. I would like those to be determined automatically.
Please try to replicate these examples in your answer without setting the exact number of columns, rows and any widths or heights.
* {
box-sizing: border-box;
}
.container {
display: grid;
flex-wrap: wrap;
grid-template-rows: repeat(3, auto);
justify-content: space-between;
border: 3px solid teal;
font-size: 20px;
}
.flex {
grid-template-columns: repeat(3, auto);
width: min-content;
}
.fixed {
grid-template-columns: repeat(3, 33.33%);
width: 28em;
}
.column {
grid-auto-flow: column;
}
.cell {
padding: 1em;
background: pink;
border: 1px dashed teal;
white-space: nowrap;
}
h3:not(:first-of-type) {
margin-top: 3em;
}
<h3>Flexible column width. Flow in rows</h3>
<div class="container flex row">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Buckle my shoe</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Knock at the door</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
</div>
<h3>Flexible column width. Flow in columns</h3>
<div class="container flex column">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Buckle my shoe</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Knock at the door</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
</div>
<h3>Fixed column width. Flow in rows</h3>
<div class="container fixed row">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Buckle my shoe</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Knock at the door</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
</div>
<h3>Fixed column width. Flow in columns</h3>
<div class="container fixed column">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Buckle my shoe</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Knock at the door</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
</div>
I had the same problem, using column, and this was fixed by adding grid-template-columns: repeat(auto-fit, minmax(baseValue,maxValue)); to the parent element