Deep Dive into Grid Layout Placement
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.
Now that we’ve finished this, it’s time to explain how you can use it. Although my colleague Sergio already wrote about this in 2014, the specification has changed since that time, so I think it’s better to try to explain the whole thing from scratch. This post is a kind of summary with examples of “Placing Grid Items” section of the CSS Grid Layout spec.
Grid lines #
This is probably one of the most important concepts of the grid layout spec.
The grid lines are the ones dividing horizontally and vertically a grid.
And they’re actually numbered, starting at 1
.
Let’s use a 3x2 grid as example to explain how this works:
<div style="display: grid;
grid-template-columns: 300px 200px 100px;
grid-template-rows: 100px 50px;">
</div>
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.
On top of that, there’re some handy shorthands:
grid-column
: Shorthand forgrid-column-start
andgrid-column-end
properties.grid-row
: Shorthand forgrid-row-start
andgrid-row-end
properties.grid-area
: Shorthand to set the 4 placement properties in just one declaration.
So, imagine that you add the following item in the previous grid:
<div style="grid-column-start: 2; grid-column-end: 4;
grid-row-start: 1; grid-row-end 2;">
</div>
Probably easier to read if you use some shorthands:
<div style="grid-column: 2 / 4; grid-row: 1 / 2;"></div>
This means that the grid item will be placed taking the 2nd and 3rd columns in the first row.
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:
<div style="grid-column: 2 / span 2; grid-row: 1;"></div>
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:
<div style="grid-column: -3 / -1; grid-row: -3 / -2;"></div>
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:
<div style="display: grid;
grid-template-columns: [start] 300px [main] 200px [aside] 100px [end];
grid-template-rows: [top] 100px [middle] 50px [bottom];">
</div>
And again if we want to place the item in the same position, we just need to reference the name of the lines:
<div style="grid-column: main / end; grid-row: top / middle;"></div>
One line can have several names, you just need to set them in the definition:
grid-template-rows: [top start] 100px [middle center] 50px [bottom end];
.
Also the names of the lines can be repeated. To reference them you’ve to use a number that can be again positive or negative. Let’s use a different example to showcase this:
<div style="display: grid;
grid-template-columns: [col] 200px [gap] 50px [col] 200px [gap] 50px [col] 200px [gap];">
</div>
And imagine that you place some items like this:
<div style="grid-column: col 2;"></div>
<div style="grid-column: col 1 / gap 2;"></div>
<div style="grid-column: col -1;"></div>
And of course, you can span to a named grid line:
<div style="grid-column: col 2 / span 2 gap;"></div>
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.
Let’s use a bigger grid (5x4) to show an example:
<div style="display: grid;
grid-template-columns: 100px 100px 100px 100px 100px;
grid-template-rows: 100px 100px 100px 100px;
grid-template-areas:
'title title title title aside'
'nav main main main aside'
'nav main main main aside'
'footer footer footer footer footer';">
</div>
And position one item in each of the areas:
<div style="grid-area: title;"></div>
<div style="grid-area: nav;"></div>
<div style="grid-area: main;"></div>
<div style="grid-area: aside;"></div>
<div style="grid-area: footer;"></div>
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:
<div style="grid-column: main-start / aside-end;
grid-row: title-start / nav-end;"></div>
And the same can happen the other way around. If you name lines using those suffixes, you’re actually creating implicit areas. So, we could just create the very same grid using named grid lines:
<div style="display: grid;
grid-template-columns: [title-start nav-start footer-start] 100px [main-start nav-end] 100px 100px 100px [aside-start title-end main-end] 100px [aside-end footer-end];
grid-template-rows: [title-start aside-start] 100px [nav-start main-start title-end] 100px 100px [footer-start nav-end main-end aside-end] 100px [footer-end];">
</div>
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.
This time let’s use a simple 2x1 grid:
<div style="display: grid;
grid-template-columns: 200px 200px;
grid-auto-columns: 100px;">
</div>
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.
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
);
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 & 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:
<div style="grid-column: foo;"></div>
<div style="grid-column: 2 / span 2 foo;"></div>
<div style="grid-column: -1 foo;"></div>
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:
<div style="display: grid;
grid-template-columns: 200px [middle] 200px;
grid-auto-columns: 100px;">
</div>
And now, let’s use place a few items referencing that “middle
” line:
<div style="grid-column: 3 middle;"></div>
<div style="grid-column: span 2 middle / 5;"></div>
<div style="grid-column: -4 / span middle;"></div>
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 #
Grid placement properties have a few special situations that are resolved by the conflict handling section of the spec.
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;
.
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! 😄
This is the definition of the grid container used in this example:
<div style="display: grid;
grid-template-columns: [left] 200px [main-start] 100px [center] 100px 100px [main-end] 50px 50px [right];
grid-template-rows: [top title-start] 50px [title-end main-start] 200px [main-end center] 150px 100px [bottom];
grid-auto-columns: 50px;
grid-auto-rows: 50px;">
</div>
And in the next picture you can see how different items will be placed.
You can try it live in our examples repository: http://igalia.github.io/css-grid-layout/grid-placement.html
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.
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!
Translations #
- Russian (February 3, 2016): Подробно о размещении элементов в грид-раскладке (CSS Grid Layout)
- Spanish (February 7, 2016): Guía exhaustiva de colocación de elementos en el Grid Layout
- French (February 23, 2016): Le positionnement dans les Grilles CSS