Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a grid-area name absent in grid-template-areas create additional tracks?

Tags:

css

css-grid

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:
enter image description here

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>
like image 778
user3789797 Avatar asked Aug 16 '19 21:08

user3789797


People also ask

How do you name a grid template area?

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.

How do grid template areas work?

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.

Why are there gaps in my grid CSS?

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.

Should you use grid template areas?

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.


2 Answers

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:

  1. More than meets the eye: There's also an extra row!
  2. The grid-area property.
  3. The grid-template-areas property.
  4. The placement of unreferenced grid areas.

1. More than meets the eye: There's also an extra row!

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 */ 
}

enter image description here

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";
}

enter image description here

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>

2. The 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;
}

enter image description here

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>

3. The 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";
}

enter image description here

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;
}

enter image description here

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;
}

enter image description here

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>

4. The placement of unreferenced grid areas.

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.

enter image description here

The grid area is handled by the grid auto-placement algorithm, which appears to say this:

  1. Because grid-area: main isn't explicitly defined (see section 3 above), it belongs in the implicit grid.

  2. 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.

like image 140
Michael Benjamin Avatar answered Nov 15 '22 21:11

Michael Benjamin


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:

enter image description here

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.

enter image description here

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>

enter image description here

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:

enter image description here

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>

enter image description here

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>

enter image description here

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.

like image 29
Temani Afif Avatar answered Nov 15 '22 21:11

Temani Afif