CSS grid: place items in "at least column 2" - css

I have the following HTML:
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
.special-1 is always in the first column. The number of .special-2s is variable, generated by user data. So approximately:
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 1fr;
}
.special-1 {
grid-column-start: 1;
}
Now the thing is, I want .special-2 to always be in at least the second column. But it can also show in the third or fourth. Is there anyway I can do this? grid-column-start: 2; doesn't work because it will put all of them in column 2 and none in the other columns. So I basically want grid-column: :not(1) or grid-column: 2 | 3 | 4 if you know what I mean. Any ideas?

You can approximate it like below. However, the order may not be like want.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* only 3 explicit clumns */
grid-auto-columns: 1fr; /* this will size the implicit one */
grid-auto-flow: dense; /* make sure to fill all the tracks */
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
/* place the #1 outside the explict column
it will create an implicit column at the start
*/
grid-column-end: -4;
background: blue;
}
/* place all the #2 in the implicit columns
pay attention to the order, they can be scrambled
*/
.special-2:nth-child(3n+1) {grid-column:1}
.special-2:nth-child(3n+2) {grid-column:2}
.special-2:nth-child(3n+3) {grid-column:3}
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
The above will make only 3 columns if there is no element special-1
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* only 3 explicit clumns */
grid-auto-columns: 1fr; /* this will size the implicit one */
grid-auto-flow: dense; /* make sure to fill all the tracks */
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
/* place the #1 outside the explict column
it will create an implicit column at the start
*/
grid-column-end: -4;
background: blue;
}
/* place all the #2 in the implicit columns
pay attention to the order, they can be scrambled
*/
.special-2:nth-child(3n+1) {grid-column:1}
.special-2:nth-child(3n+2) {grid-column:2}
.special-2:nth-child(3n+3) {grid-column:3}
<div class="grid">
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
Use the below if you will always have element special-1
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: dense;
grid-auto-rows: 100px;
grid-gap: 5px;
}
.special-2 {
background: red;
}
.special-1 {
grid-column: 1;
background: blue;
}
.special-2:nth-child(3n+1) {grid-column:2}
.special-2:nth-child(3n+2) {grid-column:3}
.special-2:nth-child(3n+3) {grid-column:4}
<div class="grid">
<div class="special-1"></div>
<div class="special-1"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>

The answer by Temani Afif surely works, but isn't very scalable. In my usecase there's some different column counts for different viewport sizes. I eventually decided to implement this differently. Note: if by the time you read this CSS Grid Level 2 has been released, this could trivially be solved using css subgrid: caniuse
Now for my solution: I changed my html as such:
<div class="grid">
<div class="special-1s"
<div class="special-1"></div>
<div class="special-1"></div>
</div>
<div class="special-2s">
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
<div class="special-2"></div>
</div>
</div>
And my css now looks somewhat like below. I forced the rows to a fixed height, which was okay in my use case and made this solution possible.
.grid {
display: flex;
}
.grid > div {
display: grid;
grid-auto-rows: 100px;
}
.special-1s {
flex: 1;
grid-template-columns: 1fr;
}
.special-2s {
--columns: 2;
flex: var(--columns);
grid-template-columns: repeat(var(--columns), 1fr);
#media (min-width: 1000px) {
--columns: 3;
}
}
By using the flex-property, I can fake consistent column-sizes across the width of .grid. The --columns property allows me to easily define extra media-queries. (I actually have a few more than shown here.)

Related

Interlave two or more rows using CSS Grid

I want to create a layout like this:
test
test
test
data-0
data-1
data-2
test
test
test
data-3
data-4
data-5
But the following code produces this:
test
test
test
test
test
test
data-0
data-1
data-2
data-3
data-4
data-5
This example shows the layout for 3 columns, 2*2 rows and 6 items. In practice I want to work with 10 columns, auto*2 rows and a dynamic amount of items. As a bonus, it would be awesome, if the amount of column adjusts based on the available width. The problem seems to be that overflowing elements are just added to the first row with a given name, instead of the first non-full row with a given name.
Current implementation using react:
const items = new Array(6)
.fill(undefined)
.map((e, i) => ({ title: "test", data: "data-" + i }));
const root = (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(3, auto)",
gridTemplateRows: "repeat(2, [title] auto [data] auto)",
}}
>
{items.map((item, i) => (
<React.Fragment key={i}>
<div style={{ gridRow: "title" }}>{item.title}</div>
<div style={{ gridRow: "data" }}>{item.data}</div>
</React.Fragment>
))}
</div>
);
ReactDOM.render(root, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Right at the end of: Subgrid - CSS - MDN
There is a short video example: Don't Wait to Use Subgrid for Better Card Layouts
The presented card layout can be used without change for the required use case. The grid components will have a row span of 2 (instead of 3).
As of writing this answer, the subgrid feature only works in firefox. The implementation for other major browsers has started. As a workaround, the video suggests using grid-template-rows: ...; before grid-template-rows: subgrid;.
.table {
display: grid;
grid-template-columns: repeat(3, 5em);
grid-template-rows: repeat(2, auto auto);
gap: 1em;
}
.card {
display: grid;
grid-row: span 2;
gap: 0;
/* fallback for browsers that don't support subgrid with following behaviour:
1fr: title gets as much space as it needs
auto: data is assumed to be of fixed size
*/
grid-template-rows: 1fr auto;
/* enable subgrid */
grid-template-rows: subgrid;
}
.title {
background: lightblue;
}
<div class="table">
<div class="card"><div class="title">long<br />title-0</div><div class="data">data-0</div></div>
<div class="card"><div class="title">title-1</div><div class="data">data-1</div></div>
<div class="card"><div class="title">title-2</div><div class="data">data-2</div></div>
<div class="card"><div class="title">title-3</div><div class="data">data-3</div></div>
<div class="card"><div class="title">title-4</div><div class="data">data-4</div></div>
<div class="card"><div class="title">title-5</div><div class="data">data-5</div></div>
</div>
You need some more css classes, you need to set positions using nth-child selector:
.container {
display: grid;
grid-template-columns: repeat(3, 50px);
grid-auto-rows: 30px;
grid-auto-flow: column dense;
}
.title:nth-child(6n+3) {
grid-column: 2;
background-color: lightblue;
}
.data:nth-child(6n+4) {
grid-column: 2;
background-color: lightblue;
}
.title:nth-child(6n+5) {
grid-column: 3;
background-color: lightgreen;
}
.data:nth-child(6n) {
grid-column: 3;
background-color: lightgreen;
}
<div class="container">
<div class="title">title1</div>
<div class="data">data1</div>
<div class="title">title2</div>
<div class="data">data2</div>
<div class="title">title3</div>
<div class="data">data3</div>
<div class="title">title4</div>
<div class="data">data4</div>
<div class="title">title5</div>
<div class="data">data5</div>
<div class="title">title6</div>
<div class="data">data6</div>
<div class="title">title7</div>
<div class="data">data7</div>
<div class="title">title8</div>
<div class="data">data8</div>
<div class="title">title9</div>
<div class="data">data9</div>
<div class="title">title10</div>
<div class="data">data10</div>
<div class="title">title</div>
<div class="data">data</div>
<div class="title">title</div>
<div class="data">data</div>
</div>

min-content with justify-self: right?

Is it possible with CSS3 grid to create such a layout?
All of the elements should be as wide as their content. And the last two elements should be right-aligned.
However, the way I've tried does not work. As soon as a column has min-content, the justify-self property doesn't do anything.
.grid {
display: grid;
grid-template-areas: "first second third";
grid-template-columns: min-content min-content min-content;
}
.first {
grid-area: first;
}
.second {
grid-area: second;
justify-self: right; /* doesn't work */
}
.third {
grid-area: third; /* doesn't work */
}
<div class="grid">
<div class="first">first</div>
<div class="second">second</div>
<div class="third">third</div>
</div>
This is only a downsized version of a much more complex layout, therefore:
I'm not looking for workarounds like creating wrappers
Stretching .first is not an option because there will be calculations on that element
The reason I chose grid over flex is because of the gaps
Using flexbox may be more appropriate? note the margin-right on the first element in container 1, and margin-left on the second element in container 2. auto margins are quite powerful in flex containers.
.container-1,
.container-2 {
display: flex;
background: lightgray;
}
.item {
width: max-content;
margin: 1em;
padding: 1em;
background: white;
}
.container-1 .item-1 {
margin-right: auto;
}
.container-2 .item-2 {
margin-left: auto;
}
<div class="container-1">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>
<div class="container-2">
<div class="item item-1">
item 1
</div>
<div class="item item-2">
item 2
</div>
<div class="item item-3">
item 3
</div>
</div>

How can I make a responsive css grid (display: grid) that has three equal boxes on the top row and then the rest of the rows are two equal size boxes?

I want to make a responsive css grid that looks like this:
box | box | box
b o x | b o x
b o x | b o x
and then when the screen size gets small enough, all of the columns collapse into a single column with each box on its own row.
Is this possible?
Edit:
For anyone who has this issue, changing the number of columns via media query from 6 to 1 was not working. However, I had the idea to make all of the items span 6 columns at the break point and that worked perfectly. This is an addition to the answer given by lawrence-witt and paulie-d.
By using a SIX column grid
.item {
height: 3em;
border: 2px solid green;
}
.container {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 4px;
width: 90%;
margin: 1em auto;
}
.item:nth-child(1),
.item:nth-child(2),
.item:nth-child(3) {
grid-column: span 2
}
.item:nth-child(4),
.item:nth-child(5) {
grid-column: span 3;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
After that it's just a matter of adding a media query to collapse the grid to a single column at the appropriate breakpoint,
Here is one way to do it using the nth-child feature. To make the grid responsive you simply add a media query that changes the styling of cells and grid at a certain screen width.
.grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
}
.cell {
border: 1px solid black;
grid-column: span 3;
}
.cell:nth-child(1),
.cell:nth-child(2),
.cell:nth-child(3){
grid-column: span 2;
}
<div class="grid">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
<div class="cell">Seven</div>
</div>
By using CSS flex layout and CSS media query you can achieve your need. See the below code, if the screen size reduced to 600px then it will change to single column layout.
.container {
display: flex;
width: 100%;
justify-content: space-between;
flex-direction: row;
margin-bottom: 10px;
}
.three-box-row > div {
width: 33%;
height: 50px;
}
.two-box-row > div {
width: 49%;
height: 50px;
}
#media only screen and (max-width: 600px) {
.container {
flex-direction: column;
}
.container > div {
width: 100%;
}
}
<body>
<div class="three-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
<div style="background-color: blue;">Box 3</div>
</div>
<div class="two-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
</div>
<div class="two-box-row container">
<div style="background-color: green;">Box 1</div>
<div style="background-color: blue;">Box 2</div>
</div>
</body>

How to achieve a 2 by 2 layout with empty cells with css grid?

I have a design where the entire page has a 3x3 column layout, however, one area of the page goes from 3 columns to 2, by just having negative space where every 3rd column used to be, like so:
Even when you add more div elements, like so:
I'm thinking the way to achieve this is using css grid with grid-areas, however, when uncommenting the two lines below this doesn't seem to work:
.inner {
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(3, 1fr);
// grid-template-areas: "c c .";
> div {
// grid-area: c;
}
}
Am I going the right way about this or would using Flexbox be more appropriate?
Link to a Codepen
You can consider an empty element that will take the third column/first row and you will have the needed result:
.inner {
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(3, 1fr);
}
.inner div {
background:red;
height:100px;
}
.inner:after {
content:"";
grid-row:1;
grid-column:3;
}
<div class='container'>
<h2>Title</h2>
<div class='inner'>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
</div>
</div>
UPDATE
with more element you can try this:
.inner {
display: grid;
grid-column-gap: 2rem;
grid-template-columns: repeat(3, 1fr);
}
.inner div {
background:red;
height:100px;
margin-bottom:2rem; /*to replace row gap*/
}
.inner:after {
content:"";
grid-row:1 / span 50; /*take all the third column*/
grid-column:3;
}
<div class='container'>
<h2>Title</h2>
<div class='inner'>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
<div>
Hey
</div>
</div>
</div>

Add a rule between CSS grid columns and rows

Is there a css grid property to add a rule (vertical line) between grid columns, and a rule (horizontal line) between grid rows, in the same way, or similar, that column-rule works?
Is there a css grid property to add a rule (vertical line) between grid columns, and a rule (horizontal line) between grid rows, in the same way, or similar, that column-rule works?
NO
There is no such property.
CSS Grid rows and columns are entirely virtual and only indicate the start and end point of their respective areas for the browser's layout engine.
Another option is to think about the background colors of both your grid and your grid cells. If you can color the background of the grid and apply a neutral white to your elements, the grid background will bleed through the grid-gap. This effectively gets you grid rules.
Example:
.grid-container {
background-color: #111; /* color of the line between cells */
display: grid;
grid-gap: 1px; /* size of the line between cells */
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: minmax(min-content, max-content);
padding: 1px; /* size of the line around the grid */
}
.grid-item {
background-color: #fff; /* cells need a bg color for this to work */
min-height: 100px;
}
<section class="grid-container">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</section>
Downside is that you still need to do a lot of manual padding adjustments around the grid depending on your content and, if you have a grid with weird amounts of content, the background will bleed through.
But, for simple grids, this works more often than I think it should.
As #Paulie_D said, no there isn't. You would have to do something as hideous as this to get something even close it it - you can't even use grid-gap if you do this:
#grid{
display: inline-grid;
grid-template-rows: auto 2px auto 2px auto;
grid-template-columns: auto 2px auto 2px auto;
}
.item{
width: 5rem;
height: 5rem;
background: red;
}
.rule{
background:black;
}
<div id="grid">
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
<div class="rule"></div>
<div class="item"></div>
</div>
Another option you could do would be to target a specific div and designate that as your horizontal rule column by having it span multiple columns.
.wrapper {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
.wrapper>div {
background-color: #eee;
padding: 1em;
}
.fullRow {
grid-column: 1/ 4;
}
<div class="wrapper">
<div>
first column
</div>
<div>
second column
</div>
<div>
third column
</div>
<div class="fullRow">
<hr>
</div>
<div>
first column
</div>
<div>
second column
</div>
<div>
third column
</div>
<div class="fullRow">
<hr>
</div>
</div>
No pure grid-* way to do it but you can put borders on the child divs, just don't use grid-column-gap (padding instead). Showing some nth-child cleanups for inside-only rules, and some custom per-column text alignment.
.container {
display: grid;
grid-template-columns:
.15fr
.20fr
.05fr
.15fr
.08fr
.1fr
.20fr;
/*grid-column-gap: 0*/
}
.container>div {
border-top: 1px solid gainsboro;
border-left: 1px solid gainsboro;
padding: .2rem .4rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* get rid of leading border (optional) */
.container>div:nth-child(7n+1) {
border-left: unset;
}
/* get rid of top -most border (optional) */
.container>div:nth-child(-n+7) {
border-top: unset;
/* this is could also be the "header" row, bolding etc. goes here*/
}
/* custom per-column text alignments */
.container>div:nth-child(7n+3),
.container>div:nth-child(7n+5) {
text-align: end;
}
<div class="container">
<div>2019-11-14</div>
<div>Nov 10 - 13, 2019</div>
<div>4</div>
<div>Sun - Wed</div>
<div>669</div>
<div>Likely</div>
<div>North Carolina</div>
<div>2019-11-14</div>
<div>Nov 10 - 13, 2019</div>
<div>4</div>
<div>Sun - Wed</div>
<div>627</div>
<div>Likely</div>
<div>Nevada</div>
<div>2019-11-14</div>
<div>Nov 1 - 7, 2019</div>
<div>7</div>
<div>Fri - Thu</div>
<div>347</div>
<div>Adults</div>
<div>North Carolina</div>
<div>2019-11-13</div>
<div>Nov 1 - 13, 2019</div>
<div>13</div>
<div>Fri - Wed</div>
<div>695</div>
<div>Likely</div>
<div>California</div>
</div>
What about just using ::after selector and absolute position
.grid-container {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
column-gap: 41px;
}
.column {
position: relative;
background: pink;
}
.column::after {
display: block;
content: "";
background: red;
width: 1px;
height: 100%;
position: absolute;
top: 0px;
right: -21px;
}
.column:last-child::after {
display: none;
}
<div class="grid-container">
<div class="column">
Bear claw gingerbread danish chocolate cheesecake icing shortbread.
</div>
<div class="column">
Bear claw gingerbread danish chocolate cheesecake icing shortbread.
</div>
<div class="column">
Bear claw gingerbread danish chocolate cheesecake icing shortbread.
</div>
<div class="column">
Bear claw gingerbread danish chocolate cheesecake icing shortbread.
</div>
</div>
By using "hr" tag you can add horizontal line, but to add vertical line you have to give border using CSS.

Resources