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 named foo-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.