Is it possible to select a specific grid column or row with CSS?
For example, say I have a 3 row by 2 column CSS Grid Layout: grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;. How would I select all elements from the 2nd column? For example: grid:nth-child(column:2) (just my idea, not valid code).
I have tried nth-child selectors on the div elements, but this does not allow me to specify row or column when the items are automatically placed by the Grid Layout engine.
body {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.item {
background: #999;
}
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Right Justify</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
<div class="item">
<p>Customer Name</p>
<p>Element 1 | Element 2</p>
</div>
To style an arbitrary row, you could use a wrapper element with its display set to contents. See the code snippet below:
.grid-container {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 2px;
}
.grid-item {
border: 1px solid black;
padding: 5px;
}
.grid-row-wrapper {
display: contents;
}
.grid-row-wrapper > .grid-item {
background: skyblue;
}
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-row-wrapper">
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
</div>
<div class="grid-item">11</div>
<div class="grid-item">12</div>
<div class="grid-item">13</div>
<div class="grid-item">14</div>
<div class="grid-item">15</div>
<div class="grid-item">16</div>
<div class="grid-item">17</div>
<div class="grid-item">18</div>
<div class="grid-item">19</div>
<div class="grid-item">20</div>
</div>
EDIT: As with all implementations, you should check to ensure it works in your target environment(s). You can check the compatibility table on MDN or caniuse.com for support for display: contents:
https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility
https://caniuse.com/#search=display%3A%20contents
There are no column or row elements that you can target but if the grid is uniform (same number of cells in each row) you can select cells. Here are some examples.
1. Columns
Last column in a 5-column grid:
.item:nth-child(5n) { /* ... */ }
Fourth (2nd last) column in a 5-column grid:
.item:nth-child(5n-1) { /* ... */ }
First (5th last) column in a 5-column grid:
.item:nth-child(5n-4) { /* ... */ }
2. Rows
First row in a 5-column grid (first five cells):
.item:nth-child(-n+5) { /* ... */ }
Second row in a 5-column grid (cells from 6 to 10):
.item:nth-child(n+6):nth-child(-n+10) { /* ... */ }
Third row in a 5-column grid (cells from 11 to 15):
.item:nth-child(n+11):nth-child(-n+15) { /* ... */ }
Last row in a 5-column grid with 20 cells (cells from 16 onward):
.item:nth-child(n+16) { /* ... */ }
Not possible with CSS.
CSS targets HTML elements, attributes and attribute values.
Grid columns and rows have none of these "hooks".
You'll have to target the grid items directly.
You wrote:
For example, say I have a 3 row by 2 column CSS Grid Layout: grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;. How would I select all elements from the 2nd column?
grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-gap: 10px;
padding: 10px;
height: 50vh;
background-color: gray;
}
grid-item {
background-color: lightgreen;
}
grid-item:nth-child(2n) {
border: 2px dashed red;
}
<grid-container>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
</grid-container>
You can not. You have no such selectors.
But that is strange, because you can easily target row/colum from CSS
#item3 {
background-color: blue;
grid-row: span 2 / 7;
}
This is natural to expect something:
div[style*="display:grid"]:grid-row(3) {
background-color: blue;
}
div[style*="display:grid"]:grid-column(3) {
background-color: green;
}
Do not the reasons that draft for this is not proposed yet
UPD
Seems there are for columns: https://drafts.csswg.org/selectors-4/#the-nth-col-pseudo
UPD
Issue at W3C repo
If you ever want to style a row the same principle applies.
Taking that example from above:
grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-gap: 10px;
padding: 10px;
height: 50vh;
background-color: gray;
}
grid-item {
background-color: lightgreen;
}
grid-item:nth-child(4n+3),grid-item:nth-child(4n) {
border: 2px dashed red;
}
<grid-container>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
<grid-item></grid-item>
</grid-container>
In the near future we will be able to do it thanks to the Grid-Structural Selectors
The double-association of a cell in a 2D grid (to its row and column) cannot be represented by parentage in a hierarchical markup language. Only one of those associations can be represented hierarchically: the other must be explicitly or implicitly defined in the document language semantics. In both HTML and DocBook, two of the most common hierarchical markup languages, the markup is row-primary (that is, the row associations are represented hierarchically); the columns must be implied. To be able to represent such implied column-based relationships, the column combinator and the :nth-col() and :nth-last-col() pseudo-classes are defined. In a column-primary format, these pseudo-classes match against row associations instead.
The one you need here is :nth-col() that behave the same way as :nth-child()
The :nth-col(An+B) pseudo-class notation represents a cell element belonging to a column that has An+B-1 columns before it ref
Related
This question already has answers here:
Targeting flex items on the last or specific row
(10 answers)
Closed 1 year ago.
I have a container with a dynamic number of items.
Each line can contain up to 4 items. If there are more than 4 items, the next item will start a new line (image 1). If there are less than 4 items it's OK, they just won't fill the whole line (image 2).
But I'm having troubles with the spaces between them:
I tried to use margin-right but it affects the last items in the lines (e.g.: item #4).
I tried to use justify-content: space-between but it looks good only for 4 items and up. For 3 and bellow, it creates a big space between them and I want them to look as in image 2.
Any other elegant / easy solutions?
.container {
display: flex;
flex-wrap: wrap;
/* justify-content: space-between; */
}
.item {
display: inline-block;
width: calc(25% - 12px);
/* margin-right: 12px; */
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
You can use css grid, you have to use display: grid;, use grid-template-columns to set the amount of columns that you want (1fr = 1 parent container fraction) and finally use grid-gap to set the space between your items.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
<div class="item">
#5
</div>
<div class="item">
#6
</div>
</div>
More info about Css grid Here!
In class .item, is defined width with calc(25% - 12px). Remember, 25% is just 4 items in each line. 20% is 5 items in each line.
So, change the width to calc(20% - 12px)
While CSS Grid is possibly the better solution for the problem, it's entirely possible to solve the problem with CSS flex-box layout, using the gap property and taking advantage – as did your original code – of the calc() function:
// this is to allow you to dynamically add more .item elements
// so you see that it should meet your needs containing more
// elements.
// we use document.querySelector() to retrieve the first element
// that matches the selector (if any exist, otherwise null):
const button = document.querySelector('button'),
// defining a named function to handle addition of new .item
// elements:
addMore = () => {
// finding the first .item element on the page:
let base = document.querySelector('.item');
// finding the .container element, and using
// .append() to attach a cloned copy of the first
// .item:
document.querySelector('.container').append(base.cloneNode(true));
}
// binding the named - addMore() - function as the event-handler
// for the 'click' event:
button.addEventListener('click', addMore);
*,
::before,
::after {
/* selecting all elements, and the pseudo-elements ::before
and ::after, setting their box-sizing model to border-box
in order that their widths include their border and padding
*/
box-sizing: border-box;
/* removing margin and padding: */
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-wrap: wrap;
/* using the gap property to place a 0.5em 'gutter'
between adjacent elements, both horizontally and
vertically: */
gap: 0.5em;
}
.item {
/* setting the flex-grow: to 1, flex-shrink to 0,
and flex-basis to the result of 20% of the parent-
width minus 0.5em (the gap-space): */
flex: 1 0 calc(20% - 0.5em);
}
/* irrelevant, purely for aesthetics */
*,
::before,
::after {
line-height: 2em;
}
.container {
border: 1px solid #000;
counter-reset: itemCount;
width: 90vw;
margin-inline: auto;
}
.item {
background-color: lightblue;
flex: 1 0 calc(20% - 0.5em);
}
.item:nth-child(odd) {
background-color: palegreen;
}
.item::before {
content: counter(itemCount, decimal-leading-zero);
counter-increment: itemCount;
}
<button type="button">Add another item</button>
<div class="container">
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
</div>
JS Fiddle demo.
References:
box-sizing.
calc().
gap.
margin-inline.
I am trying to Auto-Sizing Columns in CSS Grid. Like if I have one child-div it will cover full space in mother-div. But if there are multiple child-div it will allow specific number of child-div in a row . Here I am using CSS grid. But I cant do it. Here is my code
<div class="mother-div">
<div class="child-div>
</div>
<div class="child-div>
</div>
</div>
css
.child-div {
background-color: #257790;
height: 100px;
width: 100%;
}
.mother-div {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 1em;
}
What you are looking is almost automatic, grid will behave like this almost out of the box.
You only need to set grid-auto-flow to the direction that you want
To limit the number of items per row, you need to set them to a specific column, using nth-child selector
this style:
.child-div:nth-child(5n+1) {
grid-column: 1;
}
selects childs for n values 0, 1, 2, and so on, that in the formula 5n+1 gives values 1, 6, 11, 16, ...
All those children will go to column 1, that matches the requirement for 5 elements per row.
.child-div {
background-color: #257790;
height: 50px;
width: 100%;
}
.child-div:nth-child(5n+1) {
grid-column: 1;
}
.child-div:nth-child(5n+2) {
grid-column: 2;
}
.child-div:nth-child(5n+3) {
grid-column: 3;
}
.child-div:nth-child(5n+4) {
grid-column: 4;
}
.child-div:nth-child(5n) {
grid-column: 5;
}
.mother-div {
margin: 10px;
width: 500px;
border: solid 3px black;
display: grid;
grid-gap: 1em;
grid-auto-flow: column;
}
<div class="mother-div">
<div class="child-div">
</div>
</div>
<div class="mother-div">
<div class="child-div">
</div>
<div class="child-div">
</div>
</div>
<div class="mother-div">
<div class="child-div">1
</div>
<div class="child-div">2
</div>
<div class="child-div">3
</div>
<div class="child-div">4
</div>
<div class="child-div">5
</div>
<div class="child-div">6
</div>
</div>
I'm trying to align a logo and navigation bar in one row across the top of a website using CSS grid.
I've written out the code but can't work out what I'm doing wrong as to why it's not working: https://codepen.io/chloewb/pen/wRRewQ
.logo{
grid-area: logo;
background:white;}
.navi{
grid-area: navi;
background:Yellow;}
.section1{
grid-area: features;
background:LightSalmon;}
.section2{
grid-area: technology;
background:PaleTurquoise;}
.section3{
grid-area: pricing;
background:LightPink;}
.section4{
grid-area: email;
background:PaleGreen;}
.container {
display: grid;
grid-template-rows: repeat (5, auto);
grid-template-columns: 1fr 1fr 1fr;
font-size: 40px;
width: 100%;
background: grey;
grid-template-areas:
"logo navi navi"
"features features features"
"technology technology technology"
"pricing pricing pricing"
"email email email";}
The first thing to notice is that, when you use display: grid on a container element, its direct children will become grid-items, and to these items is that the grid layout you build will apply.
So let's say we have the following:
<div class="container">
<div class="child-1">
<div class="child-2"></div>
<div class="child-2"></div>
</div>
<div class="child-1"></div>
<div class="child-1"></div>
<div class="child-1"></div>
</div>
And this CSS:
.container{
display: grid;
}
Then only the child-1 will become grid items and be able to get properties like grid-area applied to them; everything else inside .child-1, like .child-2 will behave normally, as if there's no Grid. Unless you also specify the .child-1 element to be a grid with display: grid.
In your case, you header element is a direct child of the .container element, so it is a grid item and can be positioned on any place on the grid, but the logo and navi elements are children of header, so the grid layout does not apply to them. You would either have to take them out of the header so the rules you wrote take effect, or create another grid in the header and let it use the full first row. See this example and notice how the nesting of the elements affect them.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: minmax(50px, auto);
grid-template-areas: "logo navi navi";
margin-bottom: 20px;
border: 1px solid black;
}
.logo {
border: 1px solid red;
grid-area: logo;
}
.navi {
border: 1px solid blue;
grid-area: navi;
}
<div class="container">
<div class="logo">Logo</div>
<div class="navi">Nav</div>
</div>
<div class="container">
<header>
<div class="logo">Logo</div>
<div class="navi">Nav</div>
</header>
</div>
Imagine we have 2 CSS Grid containers with dynamic columns count based on width.
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
The grid works perfectly, but what if we need to have another grid to have the 1st column to be same as in another grid with the code shown above, but it's another column to span through more cells - depending on how many cells are in the current row.
To better understand issue, there are images:
On more narrow wrapper:
We would need to apply something like grid-column: span ALL (if something like that exists), with meaning that ALL = till the end of current row.
What is really important is that "First" column should always align with "1" column.
Code to run example is here:
.grid div {
/* Not important fancy styles */
height: 40px;
text-align: center;
padding-top: 20px;
}
.grid {
width: 350px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
background-color: silver;
}
.grid-second {
background-color: red;
}
.grid-another {
background-color: purple;
border: 1px solid gray;
}
<div class="grid">
<div class="grid-first">
First
</div>
<div class="grid-second">
Second (Want till end)
</div>
</div>
<!-- Another same grid -->
<div class="grid">
<div class="grid-another">
1
</div>
<div class="grid-another">
2
</div>
<div class="grid-another">
3
</div>
<div class="grid-another">
4
</div>
</div>
PS. please do not post solutions using media query. I am interested in any (even little hacky solution), which will work without usage of media queries.
Here are two interesting sections in the CSS Grid specification:
7.1. The Explicit Grid
Numeric indexes in the grid-placement properties count from the edges
of the explicit grid. Positive indexes count from the start side,
while negative indexes count from the end side.
also here...
8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
If a negative integer is given, it instead counts in reverse, starting
from the end edge of the explicit grid.
In other words, when dealing with an explicit grid, which means a grid defined by these properties:
grid-template-rows
grid-template-columns
grid-template-areas
grid (which is the shorthand for the three properties above, among others)
... you can make a grid area span all columns by setting this rule:
grid-column: 1 / -1;
That tells the grid area to span from the first column line to the last column line, which I believe meets your stated objective:
"We would need to apply something like grid-column: span ALL (if something like that exists), with meaning that ALL = till the end of current row."
jsFiddle demo
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
background-color: silver;
}
.grid-second {
grid-column: 2 / -1;
background-color: red;
}
/* Not important fancy styles */
.grid div {
height: 40px;
text-align: center;
padding-top: 20px;
}
.grid-another {
background-color: purple;
border: 1px solid gray;
}
<div class="grid">
<div class="grid-first">First</div>
<div class="grid-second">Second (Want till end)</div>
</div>
<!-- Another same grid -->
<div class="grid">
<div class="grid-another">1</div>
<div class="grid-another">2</div>
<div class="grid-another">3</div>
<div class="grid-another">4</div>
<div class="grid-another">1</div>
<div class="grid-another">2</div>
<div class="grid-another">3</div>
<div class="grid-another">4</div>
<div class="grid-another">1</div>
<div class="grid-another">2</div>
<div class="grid-another">3</div>
<div class="grid-another">4</div>
</div>
I'm trying create template for rows in my grid block:
grid-template-rows: repeat(3, 150px);
I know, this template should be work for first 3 rows.
However, from 4 row this template is not work.
Can i make template for all rows?
P.S.
This template work only for 1st row.
grid-template-rows: 150px;
Use grid-auto-rows (automatically generated rows) instead of grid-template-rows (manually generated rows). In current case grid-auto-rows: 150px will do the trick. Demo:
.grid {
display: grid;
grid-auto-rows: 150px;
/* space between columns for demo */
grid-gap: 10px;
}
/* just styles for demo */
.grid__item {
background-color: tomato;
color: white;
}
<div class="grid">
<div class="grid__item">One</div>
<div class="grid__item">Two</div>
<div class="grid__item">Three</div>
<div class="grid__item">Four</div>
<div class="grid__item">Five</div>
<div class="grid__item">Six</div>
<div class="grid__item">Seven</div>
<div class="grid__item">Eight</div>
<div class="grid__item">Nine</div>
</div>