Filling cells starting from the bottom in CSS Grid - css

I have a CSS grid with 3 rows. There may be less than 3 items to fill it, and I want to start filling it from the bottom.
I've created a jsFiddle for you to play with, but it currently fails to do what I want.
html, body, div {
height: 100%;
}
div {
display: grid;
grid-template-columns: 1;
grid-template-rows: repeat(3, 1fr);
/* MISSING RULE */
}
<div>
<p>Last item</p>
<p>Second last item</p>
<p>Top item</p>
</div>
Actual output:
Last item
Second last item
Top item
Desired output:
Top item
Second last item
Last item
I'd like to apply one rule to the <div> that contains the grid, rather than separate rules to the items in the grid, if that is possible.

There is no column-reverse function in CSS Grid, like there is in flexbox.
Therefore, getting grid areas to populate a container starting from the bottom isn't possible with a single rule in the container.
Here's how it would work in flexbox:
div {
display: flex;
flex-direction: column-reverse;
height: 100vh;
}
p {
flex: 1;
}
/* demo styles */
p {
margin: 0;
background-color: lightgreen;
}
p + p {
margin-bottom: 10px;
}
body {
margin: 0;
}
<div>
<p>Last item</p>
<p>Second last item</p>
<p>Top item</p>
</div>

If you want to use CSS Grid layout it will be like this. I modify your HTML. You have to write CSS property for grid child item where to start and where to end. grid-row-start: and grid-row-end:. Here is a CodePen.
.grid-item{
border: 2px solid red;
border-radius: 5px;
height: 200px;
padding: 10px;
width: 300px;
margin-left: auto;
margin-right: auto;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(3, 1fr);
}
.grid-item p{
margin: 0;
}
.grid-item .first-item{
grid-row-start: 1;
}
.grid-item .last-item{
grid-row-start: 3;
}
<div class="grid-item">
<p class="last-item">Last item</p>
<p class="second-item">Second last item</p>
<p class="first-item">Top item</p>
</div>
If you want to use flexbox it will be using two line code.
<div>
<p>Last item</p>
<p>Second last item</p>
<p>Top item</p>
</div>
CSS for this
div{
display: flex;
flex-direction: column-reverse;
}
The flex-direction CSS property sets how flex items are placed in the flex container defining the main axis and the direction (normal or reversed).

Related

Centering a grid with flexbox changes grid structure

I am making a horizontal grid that contains 4 divs, I want the grid to be horizontally centered, so I wrap my grid container with another div so that it will act as a flexbox parent and try to center it (check the code below), while the grid has been centered the horizontal grid has now become a vertical grid
HTML:
<h2>My Simple Grid</h2>
<div class="grid-holder">
<div class="grid-container grid-container--fill">
<div class="grid-element">
1
</div>
<div class="grid-element">
2
</div>
<div class="grid-element">
3
</div>
<div class="grid-element">
4
</div>
</div>
</div>
CSS:
.grid-container {
display: grid;
}
.grid-container--fill {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
.grid-element {
background-color: darkGreen;
padding: 20px;
color: #fff;
border: 1px solid #fff;
}
.grid-holder{
display: flex;
flex-direction: column;
align-items: center;
}
body {
padding: 2em;
}
Current Result:
current result
Expected Outcome:
Grid elements 1,2,3,4 should remain horizontal while the grid-holder div remains centered, sorry for the bad drawing
my drawing of expected result
I also want to know why did I need to use flex-direction: column ? Am I making any silly mistake, or forgot the basics ? Thanks in advance.
After you added align-items: center; to your flex container grid-holder, And with grid-template-columns set to auto-fill for the class grid-container--fill many columns are implicitly created with each item to fill the whole row with as many columns as it can, then the elements will be centred but only one element will be shown in a row, To fix that change the auto-fill by 4 to precise the number of columns that should be displayed in each row as follow:
.grid-container {
display: grid;
}
.grid-container--fill {
grid-template-columns: repeat(4, minmax(100px, 1fr));
}
.grid-element {
background-color: darkGreen;
padding: 20px;
color: #fff;
border: 1px solid #fff;
}
.grid-holder{
display: flex;
flex-direction: column;
align-items: center;
}
body {
padding: 2em;
}
<h2>My Simple Grid</h2>
<div class="grid-holder">
<div class="grid-container grid-container--fill">
<div class="grid-element">
1
</div>
<div class="grid-element">
2
</div>
<div class="grid-element">
3
</div>
<div class="grid-element">
4
</div>
</div>
</div>

How to stack unknown number of generic content items in a CSS Grid template area

I haven't quite got my head around grid-template-areas, I suspect. Or maybe I do, but it is the behaviour of direct Grid descendant nodes that do not have a grid-area assigned.
Here is visually what I am trying to achieve:
Codepen is here: https://codepen.io/davewallace/pen/abYxWxE
Similar code example:
html, body , .container {
height: 100%;
margin: 0;
}
.container {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 1fr max-content;
gap: 40px 40px;
grid-template-areas:
"header header"
". graphic";
}
.header { grid-area: header; }
.graphic { grid-area: graphic; }
/* For presentation only, no need to copy the code below */
.container * {
border: 1px solid red;
position: relative;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Graphic</div>
<h4>Random element here</h4>
<p>Random element here</p>
<p>Random element here</p>
</div>
I seem to be able to only achieve two things currently:
Everything is in the right place, but all "generic content items" overlap on top of each other, where instead I want them stacked.
The title is in the right place, the image is sort of in the right place, but columns and/or rows are added automatically and do things like wrap under the image
The MDN docs for grid-template-area haven't really cleared the mix of behaviours up for me. I also tried using https://grid.layoutit.com/ to visualise the layout, and that seemed to get me off to a good start, but the default behaviour of the generic nodes has me confused.
You can approximate it like below:
body {
margin: 0;
}
.container {
min-height: 100vh;
display: grid;
grid-template-columns: 1fr max-content;
grid-auto-flow: dense;
gap: 0 40px; /* set only column gap */
}
.header {
grid-row: 1;
grid-column: 1/-1;
}
.graphic {
grid-column: 2;
grid-row: 2/ span 100; /* this will do the trick */
}
.container *:not(.graphic,:last-child) {
margin-bottom: 40px; /* replace the row gap */
}
/* For presentation only, no need to copy the code below */
.container * {
border: 1px solid red;
position: relative;
margin: 0;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Some Graphic here</div>
<h4>Random element here</h4>
<p>Random element here</p>
<p>Random element here</p>
</div>
I was trying to solve your CSS problem but it looks like there is no straightforward solution to it. According to the CSS spec, You cannot span a grid item to occupy all the rows/columns in an implicit grid.
Refer to this answer for more info - span grid item to all rows/columns
But I've come to a solution that could fix your grid by introducing a parent container for the random elements. In that way, you will only need two rows and two columns.
.container {
display: grid;
grid-template-columns: 1fr max-content;
gap: 40px 40px;
grid-template-areas: "header header" ". graphic";
}
.header {
grid-area: header;
}
.graphic {
grid-area: graphic;
}
/* Just to highlight */
.container * {
border: 1px solid;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Graphic</div>
<div class="random">
<h4>Random element here1</h4>
<p>Random element here2</p>
<p>Random element here3</p>
<p>Random element here4</p>
<p>Random element here4</p>
<p>Random element here4</p>
</div>
</div>

image in a css grid wont center

What it should do:
1 fr is an image of a product, 1fr is a description of the product (positioned on the right side of the image). the whole grid should be centered in the middle but it isnt. It centers but it leaves a huge gap between them.
HTML:
<div class="grid">
<img src="../images/s6.png" alt="photo of the item">
<div class="text">
<h1 class=>V.2</h1>
<h4>$54.00</h4>
<ul>
<li>Biquíni truangular com detalhes franzidos a lilás</li>
<li>Detalhes metálicos dourados</li>
<li>Alças ajustáveis</li>
<li>Cintura subida reversível</li>
<li>Opçāo de colocar copa</li>
</ul>
</div>
</div>
CSS:
.grid img{
width: 450px;
height: 450px;
border: solid 1px rgb(235, 235, 235);
border-radius: 3%;
padding: 6px;
}
.grid{
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
justify-items: center;
}
Both your img and div are equal width maintaining the correct gap between them. Your div's contents need to be justified left if you want to close that gap.
.text {
justify-self: start;
}
Here is some more information: https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self

Take 2 columns in 2-columns layout but not when 1-column layout in CSS grid without #media

The desired layout for wide screens:
The desired layout for narrow screens:
Initial CSS:
.Layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
grid-gap: 24px 40px;
}
If I'll set grid-column-start: span 2 for the third element, the layout will be simply broken (it' happen in CSS grid ignores minimal width specified in minmax() in second column question).
Please don't use the media queries because it will nullify the announced free-space-based responsiveness which became available in CSS grid.
Grid has two extremely powerful features for dealing with changes in
available space.
<...>
Layout that relies on media queries is tied to the view-port, this
isn’t modular — components within a system need to be able to adapt to
the space they have available.
🌎
Getting to know CSS Grid Layout
Please say clearly: "it's impossible" if you are sure that it so and skilled in CSS grid.
Use grid-column: 1/-1;
.box {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
grid-gap: 24px 40px;
}
span {
height: 50px;
background: blue;
}
.more {
grid-column: 1/-1;
background: red;
}
<div class="box">
<span></span>
<span></span>
<span class="more"></span>
<span></span>
<span></span>
</div>
That you can edit like below if you want to always have a maximum of 2 columns:
.box {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max(240px,40vw), 1fr));
grid-gap: 24px 40px;
}
span {
height: 50px;
background: blue;
}
.more {
grid-column: 1/-1;
background: red;
}
<div class="box">
<span></span>
<span></span>
<span class="more"></span>
<span></span>
<span></span>
</div>
Finally I think I could handle your problem, but by using flex instead of grid. It is pretty tricky.
The main element is a class called wrapper, which has display: flex;.
You can insert your "grid items" there, now called wrapper-container.
I need this helper class to recreate the grid-gap property. Now I am using padding instead. Note that padding works a bit ditterent to grid-gap so I had to divide your gap by 2.
Each wrapper-container has a wrapper-container__item child, which contains your content. When you inspect these elements, you will notice, that they have at least a width of 240px which is caused by their min-width property.
When you want an element to span over two "columns" add the class wrapper-container__stretched to the wrapper-container. It is applying width: 100% so that the element will be fullsized. Elements which do not have the class, has flex: 1, so they will stay next to each other (just like a 2-column grid).
Feel free to ask when there are any ambiguities.
Codepen:
Responsive grid without media queries using flex-box
.wrapper{
position: relative;
width: 100%;
background: #dedede;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.wrapper-container{
position: relative;
padding: 12px 20px;
}
.wrapper-container:not(.wrapper-container__stretched){
flex: 1;
}
.wrapper-container.wrapper-container__stretched{
width: 100% !important;
}
.wrapper-container__item{
position: relative;
min-width: 240px;
min-height: 64px;
width: 100%;
}
.red{
background: #e53935;
}
.green{
background: #388e3c;
}
<section class="wrapper">
<!-- Row 1 -->
<div class="wrapper-container">
<div class="wrapper-container__item green">Item 1</div>
</div>
<div class="wrapper-container">
<div class="wrapper-container__item green">Item 2</div>
</div>
<!-- Row 2 -->
<div class="wrapper-container wrapper-container__stretched">
<div class="wrapper-container__item red">Item 3</div>
</div>
<!-- Row 3 -->
<div class="wrapper-container">
<div class="wrapper-container__item green">Item 4</div>
</div>
<div class="wrapper-container">
<div class="wrapper-container__item green">Item 5</div>
</div>
</section>
A posible solution using flex, like MichaelT
.layout {
border: solid 1px black;
margin: 2px;
position: relative;
}
.innerlayout {
display: flex;
flex-wrap: wrap;
position: relative;
left: -10px;
right: 10px;
width: calc(100% + 20px);
}
span {
background-color: green;
height: 40px;
min-width: 240px;
width: 40%;
margin: 15px 10px;
flex-grow: 1;
}
.wide {
width: 100%;
background-color: red;
}
#narrow {
width: 300px;
}
<div class="layout">
<div class="innerlayout">
<span></span>
<span></span>
<span class="wide"></span>
<span></span>
<span></span>
</div>
</div>
<div class="layout" id="narrow">
<div class="innerlayout">
<span></span>
<span></span>
<span class="wide"></span>
<span></span>
<span></span>
</div>
</div>

Keep wrapped flex items on left while others centred

I'm trying to display a list of flex items with fixed width in the center of flexbox with wrap by using margin: auto. When wrap happens, the wrapped item also centers in its own container:
Is there a way to keep the wrapped item on the left while everything else is centred?
.parent {
display: flex;
flex-wrap: wrap;
height: 100px;
}
.children {
flex: 1;
border: 1px solid black;
max-width: 300px;
min-width: 300px;
margin: auto;
height: 100px;
margin-top: 1rem;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>
You're saying you want the items centered, but when there is only one item that wraps, you want it left-aligned.
The problem is that there is really no left-alignment in the flex container. Everything is centered, based on available space in the row. So the single item in the last row has no concept of what's going on above, and nothing to align with.
Here's what happens if you left-align the last item (on wider screens):
.parent {
display: flex;
flex-wrap: wrap;
}
.children {
border: 1px solid black;
max-width: 300px;
min-width: 300px;
margin: auto;
height: 100px;
margin-top: 1rem;
}
.children:last-child {
margin-left: 0;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>
What you need is a nested grid structure.
A top-level grid to establish the centering.
A nested grid for the wrapping.
In the demo below, you'll find a three-column grid. The left and right columns are empty (spacer) items, created with CSS pseudo-elements, and set to equal widths. This centers the middle item.
The middle item is also a grid container. Using the repeat() and auto-fill functions, the items can wrap, with individual items aligning left.
body {
display: grid;
grid-template-columns: auto 1fr auto;
}
body::before,
body::after {
content: ''; /* in grid (and flex) layouts, pseudo-elements on the container
are treated as items */
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: 100px;
grid-gap: 10px;
justify-content: center; /* centers the columns (not the items, like in flex) */
}
.children {
border: 1px solid black;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>
jsFiddle demo

Resources