Upcoming Course: Code Your Own Business w/ React + GraphQL!
We're live-coding on Twitch! Join us!
Placing, Spanning and Density in CSS Grid

Placing, Spanning and Density in CSS Grid

The most common things you learn in CSS Grid is usually related to the grid container and less about the grid items. A generic grid definition applied to the grid container is enough for a basic layout structure. However, when you need more control over the contents of the grid, the grid container can only help with very little cases.

Maybe you want a 4 x 2 grid but the first grid item should take up column 1 and 2 as well as row 1 and 2. Or maybe you want an item to span till the last grid column track when you have no idea what the runtime total number of the grid column track is.

Or maybe you want a masonry grid where images with different aspect ratios are fitted into a single grid:

Each grid item’s size is predefined and you need to find a way to size them relatively to other items in the grid container.

Placing Grid Items: Start Here, End There

Everything in the browser has a default style. Maybe 0; maybe auto. Maybe any other defaults which you need to be aware of before adjusting for custom values. When you have a grid, the grid items have placement values defined by grid-[x]-start and grid-[x]-end (where x can be column or row). The value is auto.

Essential Reading: Learn React from Scratch! (2019 Edition)

When next you see a grid like the one above, keep it at the back of your mind that each item in that grid has a placement value now matter how symmetric the flow looks:

    .container {
      display: grid;
      grid-gap: 10px;
      grid-template-columns: repeat(5, 1fr)
    }

    .item {
      grid-column-start: auto; /_ default _/
      grid-column-end: auto; /_ default _/
      grid-row-start: auto; /_ default _/
      grid-row-end: auto; /_ default _/
    }

Placing Columns Let’s see how wild we can get when we adjust these default values starting with columns:

    .item:nth-child(1) {
      grid-column-start: 1;
      grid-column-end: 5;
    }
  • We want the first item in the grid .item:nth-child(1)
  • To start at line one, grid-column-start: 1;
  • And end at line 5, grid-column-end: 5;

[Warning] There is usually a subtle source of confusion here. Earlier when we defined this grid, we did so with 5 columns, grid-template-columns: repeat(5, 1fr). In that case, when we ask a grid item to start at 1 and end at 5, does that not mean it should span the entire row?

No. It does not span from the first track, instead it spanning starts from a line and ends at a line. Therefore, we were saying start at line 1 and end at line 5:

[Warning ends]

Placing Rows You guessed quite right if your guess was that the same rules apply to row-placing:

    .item:nth-child(22) {
      grid-row-start: 1;
      grid-row-end: 4;
    }

But, there is a but. grid-row-[x] will:

  1. Reset the grid item default placement to start at column line 1 and row line 1
  2. Then the placement spanning will occur

22 left its spot and moved up to lines 1 before spanning.

If you want it to stay at its original column track, then you have to explicitly state that:

    .item:nth-child(20) {
      grid-column-start: 3;
      grid-row-start: 5;
      grid-row-end: 10;
    }

When ONLY Start or End is Provided

You do not have to provide the placing properties in pairs. You can just provide one and the rest will stay as auto (default). The tricky thing to watch out for is that if you change only the value of grid-[x]-start, the grid item will start at the line, not the track:

    .item:nth-child(1) {
      grid-column-start: 5;
    }

On the other hand, if you specify end only, it will start from the specfied end value and span the grid inversely. For example, if you tell item 10 to end at 3, it will start from the nearest next line 2 and draw to line 3:

    .item:nth-child(10) {
      grid-column-end: 3;
    }

The Placement Shorthand There’s a shorthand syntax for grid-[x]-start and grid-[x]-end that allows you to do away with the -start and -end part:

    .item {
      grid-column: [start] / [end];
      grid-row: [start] / [end];
    }

Here are some examples that can replace the code samples we have written so far:

    .item {
      grid-column: 1 / 5; /__ Line 1 to 5 /
      grid-row: 1 / 4; /*_ Line 1 to 4 __/
      grid-column: 5 / auto; / Line 5 to 6 *_/
      grid-column: auto / 3; /_ Line 2 to 3 _/
    }

Spanning: Start/End Here, Take n

We have been using the word “span” to describe the amount of tracks a placement should occupy. When we say line 1 to 5, we are saying the grid item should span from 1 to 5. I usually refer to this as implicit spanning.

There is a span keyword that serves as a value. When this is used, I prefer to refer to it as explicit spanning. Placing and spanning are flexible enough that you can use both implicit and explicit spanning as value to a single property.

Henceforth, we will just stick with the shorthand since it’s more terse.

Spanning Columns Let’s start with looking at how we can span an item across a column track:

    .item:nth-child(1) {
      grid-column: span 5;
    }

This is an alternative to the first example we wrote:

    .item:nth-child(1) {
      grid-column: 1 / 5;
    }

But we can go crazy:

    .item:nth-child(1) {
      grid-column: span 2 / 5;
    }

You can read the above as: “end at 5, the span 2 tracks backward”:

Another example:

    .item:nth-child(1) {
      grid-column: 2 / span 2;
    }

Which is just a more intuitive way of saying: “Start at col line 2 and end at col line 4 (after 2 jumps)”:

Spanning Rows Everything you have learned so far in this post about rows applies without any exception. So don’t expect any surprises:

    .item:nth-child(1) {
      grid-column: 2 / span 2;
      /_ highlight line_/
      grid-row: span 5
    }

We are still spanning the first time but this time we are spanning on the row axis. The browser will draw the box down to occupy 5 tracks:

Excess Placement and Spanning

Recall that we have explicitly defined our grid to have 5 columns. What do you think would happen if we span or place an item from column 6 up:

    .item:nth-child(1) {
      grid-column: span 10; /_ 1 _/
      grid-column: 1 / 10; /_ 2 _/
      grid-column: 7 / 10; /_ 3 _/
      grid-column: 1 / span 10; /_ 4 _/
    }

Golden rule: Implicit tracks will be created to accommodate them

Negative Integers as Values

Negative integers are as useful as the positives — they inverse the placement or spanning. Hence, instead of starting from the first column or row, the placement or spanning will start from the last column or row.

The following code:

    .item:nth-child(1) {
      grid-column: 1 / 5
    }

is the direct opposite of:

    .item:nth-child(1) {
      grid-column: -1 / -5
    }

Since we are starting at the end to draw backwards as I mentioned above, there will be room for item 2 in column 1 so it gets pushed down. We will look at using density to feel up such spaces created in such case (if the content is not symmetric).

There is a great hack that you can use to span to the end of a column or row without needing to know how many columns are defined by the grid container. This is useful when you are using auto-fill or auto-fit in defining a dynamic grid:

    .item:nth-child(1) {
      grid-column: 1 / -1
    }

Specifying -1 for row end or column end will make the grid item span till the end of grid starting at what ever start you provided (in this case line 1).

[Warning]

The negative integer value hack does not work on implicit grids. Therefore since we defined only column and row in the grid container, this will not do anything:

    .item:nth-child(1) {
      grid-row: 1 / -1;
    }

For it to work, you have to give an explicit row definition to the grid container. Eg:

    .container {
      grid-template-rows: repeat(10, 30px);
    }

[Warning end]

Density

In the previous examples, you must have seen some placement or spanning that caused empty spaces scattered in the middle of a grid. Here is an example of one:

Well you can close this spaces using grid-auto-flow property on the grid container to close up those white spaces:

    .container {
      grid-auto-flow: dense;
    }

One thing you should be aware of is that if you have symmetric content that needs to follow an order, making the flow dense will distort that order. This is a trade-off you have to make if you want a compact design while still placing grid items irregularly.

Notice how after placing item 1 at the end, the grid comes back to continue placing 2 at the beginning of the tracks.

Like this article? Follow @codebeast on Twitter