Deep Dive into Grid Layout Placement

Comprehensive review of the different methods provided by CSS Grid Layout spec for items positioning.

Posted by Manuel Rego Casasnovas on February 1, 2016

During the last months as part of my work in Igalia
I’ve been focused on finishing the new/missing bits of
the CSS Grid Layout Blink’s implementation related to items placement.
In summary the task was mostly related to 2 things:

Support implicit grid before explicit.
So the grid can add tracks on demand not only on the growing direction
(usually right/bottom) but also on the opposite one.

Fix unknown named grid lines resolution.
This is the case when an item is placed in a line called “foo”,
but no lines with that name exist in the grid.

These might seem quite simple tasks,
but they implied quite a lot of changes in the underlying implementation.
I ended up refactoring all the code to resolve grid positions
in order to complete them.
I even wrote a document explaining the whole plan
and all the technical details,
where you can find links to all the related patches.

Grid placement properties

In order to position the items inside the grid container,
you need to use the grid placement properties.
These properties are:

grid-column-start: Set the first vertical line for the item.

grid-column-end: Set the last vertical line for the item.

grid-row-start: Set the first horizontal line for the item.

grid-row-end: Set the last horizontal line for the item.

With these properties you define the area where the grid item will be placed.
In order to do that, you use the line numbers.

The initial value for these properties is auto, which makes possible
that items are automatically placed looking for empty cells inside the grid.
For more information about this, please review a previous post on the matter.

This means that the grid item will be placed taking the 2nd and 3rd columns
in the first row.

Place item using line numbers example

Cell spanning

Previous item was spanning 2 columns (2nd and 3rd ones) referencing the lines.
You could do the same using the span keyword,
together with the number of cells you want to span.

So, you could place the item in the very same position using:

<divstyle="grid-column: 2 / span 2; grid-row: 1;"></div>

Place item using span example

Note that here you’re not setting the end line for the row.
This means that grid-row-end takes a value of auto.
In this case auto defaults to a span of one.

Negative line numbers

So far we’ve only seen positive numbers,
but lines have also negative indexes.
Negative numbers allow you to reference the lines
starting from the end of the grid.

Following with the same example, you could place the item
again in the same position using the negative indexes:

<divstyle="grid-column: -3 / -1; grid-row: -3 / -2;"></div>

Place item using negative line numbers example

This might be really useful in some situations.
For example, if you want to be sure that the item is in the last column,
independently of the number of tracks,
you’ll just need to set: grid-column-end: -1;.

Named grid lines

Not only that, but you can also name the grid lines,
so you don’t need to remember the specific number to reference to them.

Let’s modify the definition of the grid,
keeping the size of tracks but adding names to the lines:

Grid areas

Better still, you can define grid areas and place items directly on them.
You have to use the grid-template-areas property
to put names to the different areas in your grid.
And you could use the grid-area shorthand directly to place the items.

Grid areas & Named grid lines

One interesting thing about areas and placement is that grid areas
create implicit names for the grid lines surrounding them.
These implicit names use the “-start” and “-end” suffixes.
And you can reference those lines when placing an item,
instead of using the whole area.

E.g. the “title” area from previous example
creates 4 implicit names for the lines (2 in each axis):

Left line: “title-start”

Right line: “title-end”

Top line: “title-start”

Bottom line: “title-end”

Following with that example you could place an item
using the implicit names:

All the examples of items positioned in this section
will be exactly in the same place with this new grid.

Implicit grid

With the grid definition properties
(grid-template-columns, grid-template-rows and grid-template-areas)
you determine the explicit number of tracks (columns and rows) in your grid.
However, grid spec allows you to place items outside of the explicit grid.
In order to support that, implicit tracks are created automatically,
the size of these tracks is controlled
by grid-auto-columns and grid-auto-rows properties.
In the following examples I’ll use red color
to highlight the implicit lines.

And imagine that you place an item in the 5th column (grid-column: 5;).
As the grid only has 2 columns, 3 implicit columns
will be added in order to position the item.

Implicit grid example

Again you can also create implicit tracks with items that span several cells.
For example, if an item takes 3 columns starting on the 2nd one
(grid-column: 2 / span 3);

Implicit grid with span example

Originally, the implicit tracks could only be added at the end.
But now it’s possible to add implicit tracks before the explicit grid.
For example, if you place an item using grid-column: -5;
it’ll add 2 columns on the left and it’ll be placed in the -2nd column.

Implicit grid before explicit example

Implicit grid & Named grid lines

But this is not the only way to create implicit tracks,
you can also create them if you use undefined named grid lines.
This is more a way to show mistakes on the user CSS than a feature itself,
but maybe someone finds it useful.
The idea is that all the lines in the implicit grid
will take any random name you might need to position an item.

The basic example is placing items referencing
a nonexistent line called “foo”.
For example you will create 3 implicit columns
(1 before and 2 after the explicit grid)
with the following items:

Note that the simplest example grid-column: foo
is being placed in the 4th column
(adding an extra empty column just after the explicit grid).
This is because first line that is considered to be called “foo”
is the first implicit line (line 4),
so last line of the grid (line 3) is not included.

Also, the last item grid-column: -1 foo is placed on the -1th column
(maybe you was not expecting that).
This is because of you start looking for a line named “foo”
from the edge of the explicit grid.
So, you ignore lines -1, -2 and -3 (as they’re not called “foo”)
and consider line -4 (first line on the implicit grid) to have that name.

This is a bit trickier if the line actually exists,
as you’ve to count it too in order to place the item.
Specially it’s complex if you’re using span to a named grid line,
but there’re not enough lines.
In that case only implicit lines in the search direction
are considered to have that name.

Again, hopefully an example can help to understand this.
Let’s add a name to the middle line in the previous example:

The strange case here is grid-column: span 2 middle / 5;,
as you can see it takes from -1th column to 4th column (both included).
The item ends at line 5,
and it has to span 2 lines called “middle” to find the start.
You could think that it should count line 4 and line 2,
but, as explained before,
you have to start counting lines from the edge of the explicit grid.
So you actually count line 2
and then you’ve to consider the implicit lines on the left
to find the start position (line -4).

Special cases

For example, if you place an item where
the end line is before than the start line, both lines are swapped.
Thus, something like grid-column: 5 / 2; would become grid-column: 2 / 5;.

Another situation is the one in which you have span
in both the start and end positions.
The span for the end position is discarded.
So, grid-column: span 2 / span 3; would become grid-column: span 2;.
Which will use the grid placement algorithm to find an empty area
(of 2 columns in this particular example) to position itself.

Last one is the case when you only have a span to named grid line.
In that case, it’s replaced by span 1.
E.g. grid-column: span foo; will become grid-column: span 1;.

Placement special cases example

Recap

If you have read that far it seems you’re really interested on CSS Grid Layout.
The main conclusion is that the specification is really flexible
regarding how to place items on the grid.
As you can see there’re quite a lot of different ways
to position an item in the very same place.
Probably each person will get used to a few of them
and just forget about the rest.

IMHO the basic stuff (line indexes, spans, line names and areas)
is quite straightforward.
And the implicit grid is not that hard either.
Then negative indexes bring some fun.
However undefined named grid lines behavior is really tricky
(hopefully not something you should care about anyway).
But it’s true that I’m biased as I’ve been dealing with this for a long time.

Last, I thought it would be nice to finish this with a big example
which uses most of the things described in this post.
If you got it right and don’t get confused at all
you’re mastering grid layout placement. Congrats! 😄

Status

As commented on the introduction Blink’s implementation
should support now (Chrome 50+) all these different placement possibilities.
Igalia has been working on this implementation
and we’re porting it now to WebKit too.

On the other side, Gecko
has already support for it too, in this case developed by Mozilla.

Igalia and Bloomberg
working together to build a better web

Finally, as usual I’d like to highlight one more time
that all this work has been done as part of
a collaboration between Igalia and
Bloomberg.
Big thanks for your support!