I've created a simple CSS Grid, I decided to not specify grid-template
, grid-template-columns
, grid-template-rows
properties.
Instead, I started with grid-template-areas
, and assigned area names to the grid-items via grid-area
property.
After that, I was interested in what would happen if I remove grid-item from grid-template-areas
. The result was kind of strange.
The removed grid-item was placed on the right and separated by additional column.
The problem:
Why did this happen? Is this expected behaviour or did I miss something in my code? How can I remove this column?
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
header {
grid-area: header;
background: lightblue;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
The grid-template-areas property specifies areas within the grid layout. You can name grid items by using the grid-area property, and then reference to the name in the grid-template-areas property. Each area is defined by apostrophes. Use a period sign to refer to a grid item with no name.
The grid-template-areas property accepts one or more strings as a value. Each string (enclosed in quotes) represents a row of your grid. You can use the property on a grid that you have defined using grid-template-rows and grid-template-columns , or you can create your layout in which case all rows will be auto-sized.
The vertical gaps are caused by the images not filling the vertical space in the grid items. The problem is made worse with align-items: center on the container, which removes the align-items: stretch default. Essentially, there are no gaps between grid items.
CSS Grid Template Area Basics A CSS grid template area makes a visual representation of the grid using both columns and rows. This makes for a faster design process than when line-based placement or grid-column and grid-row values are used.
There are four parts to this answer. The first three help explain the fourth, which covers the reason for the extra column. If you're only interested in the answer, skip to the end.
Contents:
grid-area
property.grid-template-areas
property.You've only partially defined the problem. Yes, there's an extra column. But there's also an extra row.
Because you haven't defined a height on the grid container, the height defaults to auto
– the height of the content (more details). So any rows with no content simply collapse and are invisible.
This issue doesn't exist with width because, in this case, you're using a block-level container (created by display: grid
), which is designed to consume the full width of its parent, by default (more details).
So that's why you're not seeing the extra row. If you give the container some height, the row will appear.
body {
display: grid;
grid-template-areas:
"header"
"footer";
height: 150px; /* new */
}
body {
display: grid;
grid-template-areas:
"header"
"footer";
height: 150px; /* new */
}
header {
grid-area: header;
background: aqua;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
Note: If you had used display: inline-grid
, both the extra row and the extra column would have been invisible.
body {
display: inline-grid;
grid-template-areas:
"header"
"footer";
}
body {
display: inline-grid; /* adjustment */
grid-template-areas:
"header"
"footer";
}
header {
grid-area: header;
background: aqua;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
grid-area
property.Giving a name to the grid-area
property creates a named line for each side of the area.
For example, grid-area: header
resolves, in order, like this:
grid-row-start: header
grid-column-start: header
grid-row-end: header
grid-column-end: header
Like margin
, border
and padding
, the grid-area
property is a shorthand property. Unlike those properties, grid-area
has a counterclockwise resolution order (in LTR languages), as illustrated above.
Because named grid areas occupy space, they need rows and columns in which to exist. Hence, named grid areas always impact the layout, even when they're not referenced in grid-template-areas
.
So all that is necessary to "fix" your layout is to remove grid-area: main
.
main {
/* grid-area: main; */
background: darkorange;
}
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
header {
grid-area: header;
background: aqua;
}
main {
/* grid-area: main; */
background: darkorange;
}
footer {
grid-area: footer;
background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
grid-template-areas
property.Rows and columns (a/k/a tracks) that are created using grid-template-rows
, grid-template-columns
or grid-template-areas
belong to the explicit grid. Any tracks not defined by those properties belong to the implicit grid (source).
For every string listed in grid-template-areas
, a new row is created.
For every name or sequence of dots (...
) in the string, a new column is created (but this doesn't apply in this case because each string has only one name).
Your code creates an explicit grid with two rows and one column:
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
As you can see in the image, header
and footer
have their own rows and exist in column one, exactly as defined in grid-template-areas
.
The additional two rows and two columns are part of the implicit grid.
We can verify this by sizing them.
grid-template-columns
works only on explicit columns.
grid-auto-columns
works mostly on implicit columns (see note below).
body {
display: grid;
grid-template-areas: "header" "footer";
grid-template-columns: 1fr;
grid-auto-columns: 100px;
grid-template-rows: 100px 100px;
grid-auto-rows: 25px;
}
body {
display: grid;
grid-template-areas:
"header"
"footer";
grid-template-columns: 1fr;
grid-auto-columns: 100px;
grid-template-rows: 100px 100px;
grid-auto-rows: 25px;
}
header {
grid-area: header;
background: aqua;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
Note: If grid items are placed using grid-template-areas
(creating explicit tracks), but they are not sized using grid-template-columns
/ grid-template-rows
, then grid-auto-columns
/ grid-auto-rows
apply to them. (second paragraph)
body {
display: grid;
grid-template-areas:
"header"
"footer";
grid-auto-columns: 100px;
grid-auto-rows: 25px;
}
body {
display: grid;
grid-template-areas:
"header"
"footer";
grid-auto-columns: 100px;
grid-auto-rows: 25px;
}
header {
grid-area: header;
background: aqua;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: lightgreen;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
Note: To be perfectly honest, I'm about 75% sure this section is completely correct. The spec language was not 100% clear to me. I welcome feedback, corrections, and more accurate answers.
In your code you have a third grid area which is not referenced in grid-template-areas
.
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
main {
grid-area: main;
background: darkorange;
}
Where does grid-area: main
go?
As we've already seen, it gets sent into the implicit grid, two columns and two rows in.
The grid area is handled by the grid auto-placement algorithm, which appears to say this:
Because grid-area: main
isn't explicitly defined (see section 3 above), it belongs in the implicit grid.
Because grid column line 2 and grid row line 3 (the boundaries of the explicit grid) are named grid lines, new lines must be created in the implicit grid to accommodate the four named lines of grid-area: main
. This can only happen with an empty row and empty column in between, separating the explicit grid from the auto-placed implicit grid area.
This is an extension to what Michael_B already said in order to highlight how the implicit grid lines are created.
Let's start with an easy example:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header;
background: blue;
}
<div class="container">
<header>H</header>
</div>
We have a grid item where we only set grid-row-start
and the final result is two rows with an empty one. Both within the implicit grid since we didn't define any explicit one.
To understand what is happening let's refer to the specification:
The three properties grid-template-rows, grid-template-columns, and grid-template-areas together define the explicit grid of a grid container. ... If these properties don’t define any explicit tracks the explicit grid still contains one grid line in each axis. ref
So even if we define nothing, we still have an explicit grid with two lines. This is very important because without those line we will not have our empty row.
Now the part explaining grid-row-start:header
:
<custom-ident>
First attempt to match the grid area’s edge to a named grid area: if there is a named line with the name ''-start (for grid--start) / -end'' (for grid--end), contributes the first such line to the grid item’s placement.
Otherwise, treat this as if the integer
1
had been specified along with the<custom-ident>
.
It's clear that we will fall into the otherwise and will have grid-row-start:header 1
:
<integer> && <custom-ident>?
Contributes the Nth grid line to the grid item’s placement...
If a name is given as a
<custom-ident>
, only lines with that name are counted. If not enough lines with that name exist, all implicit grid lines are assumed to have that name for the purpose of finding this position.
In our case, we don't have enough lines with that name (we don't have any line at all) so we should add at least one line with that name and try to place our element, and since the integer is positive the item will be placed below that line:
The grid has a default line (red one), the use of header
generates a new implicit one below it (due to the default value 1
added automatically), and the element will be placed below that line creating an extra row.
If we use -1
will have only one row at the end:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header -1;
background: blue;
}
<div class="container">
<header>H</header>
</div>
In this case the implicit line is generated above the explict one and our element placed between both lines.
If a negative integer is given, it instead counts in reverse, starting from the end edge of the explicit grid.
The use of -1
and 1
at the same time will give us the following result:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header -1;
background: blue;
}
footer {
grid-row-start: header 1;
background: red;
}
<div class="container">
<header>H</header>
<footer>F</footer>
</div>
Here is another example with multiple items to illustrate that all implicit grid lines are assumed to have that name.
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header 1;
background: blue;
}
main {
grid-row-start: main 1;
background: red;
}
footer {
grid-row-start: footer -1;
background: green;
}
extra {
grid-row-start: extra 5;
background: orange;
}
<div class="container">
<header>H</header>
<main>M</main>
<footer>F</footer>
<extra>E</extra>
</div>
In this example we need in total 6 implicit lines, because all the used integers are within the range [-1,5]
(excluding 0
which is an invalid value), and, to place each element, all those lines will have the names defined for each element. That's why two elements with the same number will be in the same row (like main
and header
), since the reference line will be the same even with different names.
Now let's add grid-row-end
to our previous example:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header;
grid-row-end: header;
background: blue;
}
<div class="container">
<header>H</header>
</div>
Nothing will happen and will have the exact same result because:
If the start line is equal to the end line, remove the end line. ref
Let's use a different name:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header;
grid-row-end: foo;
background: blue;
}
<div class="container">
<header>H</header>
</div>
Still the same result because both are still equal (yes, they are equal!). Both values will be equal to <name> 1
, so both will need only one implicit line. The browser will then create one implicit line that has two different names, thus making both our values equal.
Let's change the value of one:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: header 1;
grid-row-end: foo 2;
background: blue;
}
<div class="container">
<header>H</header>
</div>
Again the same result, but with "different" code. In this case, we will have 2 implicit lines and our element will be placed between them.
Basically the name is not relevant when it comes to implicit grid, because all of them will share the same lines. It's only relevant when we define them inside the explicit grid:
.container {
width:100px;
display: inline-grid;
grid-auto-rows: 40px;
border: 1px solid;
}
header {
grid-row-start: hello 1;
grid-row-end: john 3;
background: blue;
}
main {
grid-row-start: main 1;
grid-row-end: hi 2;
background: red;
}
footer {
grid-row-start: footer 2;
grid-row-end: custom 5;
background: green;
}
extra {
grid-row-start: extra 3;
grid-row-end: fsdfsdfsdfsd 5;
background: orange;
}
<div class="container">
<header>H</header>
<main>M</main>
<footer>F</footer>
<extra>E</extra>
</div>
In the above example, you can update the names with any random string and you will always have the same result; it only depends on the integer:
All the above will logically behave the same considering grid-column-*
.
Now, we have everything we need to understand what is happening with the initial example.
First we have our explicit grid like below:
body {
display: grid;
grid-template-areas:
"header"
"footer";
/* No relevant but to better illustrate*/
grid-auto-rows:50px;
grid-auto-columns:50px;
}
header {
grid-area: header;
background: lightblue;
}
/*main {
grid-area: main;
background: darkorange;
}*/
footer {
grid-area: footer;
background: blue;
}
<header>Header</header>
<!--<main>Main</main>-->
<footer>Footer</footer>
The grid-template-areas property creates implicit named lines from the named grid areas in the template. For each named grid area foo, four implicit named lines are created: two named
foo-start
, naming the row-start and column-start lines of the named grid area, and two namedfoo-end
, naming the row-end and column-end lines of the named grid area. ref
Now if we add the third element with grid-area:main;
it means we have
grid-row-start:main 1;
grid-row-end:main 1;
grid-column-start:main 1;
grid-column-end:main 1;
We remove the *-end
because they are equal to *-start
grid-row-start:main 1
grid-column-start:main 1
Based on the previous explanation, we will need an extra implicit line called main
and our element will be placed below the horizontal one and at the right of the vertical one:
body {
display: grid;
grid-template-areas:
"header"
"footer";
/* No relevant but to better illustrate*/
grid-auto-rows:50px;
grid-auto-columns:50px;
}
header {
grid-area: header;
background: lightblue;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
If we remove the grid-auto-*
, the rows will be the height of their content, making the row between footer-end
and main
empty. The column will split the width of grid element which is a block element have a full width. That's why you only see an extra column and not the extra row:
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
header {
grid-area: header;
background: lightblue;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: blue;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
Another interesting observation is that if you add more elements with grid-area:<name>
, they will all sit above each other:
body {
display: grid;
grid-template-areas:
"header"
"footer";
}
header {
grid-area: header;
background: lightblue;
}
main {
grid-area: main;
background: darkorange;
}
footer {
grid-area: footer;
background: blue;
}
extra {
grid-area: extra;
background: red;
opacity:0.8;
}
more {
grid-area: more;
background: green;
opacity:0.3;
}
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
<extra>E</extra>
<more>More</more>
Based on the previous explanation, all of them will have the following:
grid-row-start:<name> 1;
grid-column-start:<name> 1;
Since the number is the same (the name is irrelevant as we already explained) they will all belong to the same area.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With