Layouts in CSS: Creating Layouts with CSS Grid

CSS Grid allows us to create two-dimensional grid-based layouts that were previously impossible, or only possible with lots of JavaScript.

Keep in mind that the CSS Grid specification is dense, and it introduces several new concepts that are a bit complex. Consider this section an overview rather than a comprehensive look at Grid. Don’t worry, though: we’ll point you to lots of resources for learning more.

1. The Grid Formatting Context

Adding display: grid to an element triggers a grid formatting context for that element and its children. In a grid formatting context, three things happen:

  • The element becomes a block-level element that participates in the normal flow.
  • Its children—whether elements or text nodes—create block-like, grid-level boxes that can be arranged into rows and columns. Immediate children of a grid container are grid items.
  • In a horizontal writing mode, each member in a grid row will have the same height as its tallest element (as determined by content), unless an explicit height value is set. When the document uses a vertical writing mode, it takes on the same length as its longest element (as determined by content).

The image below illustrates how using display: grid creates a block-level container, and block boxes for its children.


Using display: inline-grid works similarly. Children of inline-level grid containers create grid-level boxes, but the container itself participates in an inline formatting context.

By themselves, display: grid and display: inline-grid won’t automatically arrange these boxes into rows and columns. We also need to tell the browser where and how to place things.

Before creating your grid, determine whether you want a fixed number of columns and/or rows, whether you’d like the browser to calculate the number of columns and rows automatically, or whether you’d like a mix of the two. Knowing what kind of grid you want to create determines the approach you’ll take. Let’s look at a few techniques.

2. Defining a Grid Layout

After defining a grid container, we’ll need to tell the browser how many rows and columns our grid should contain. We can define the number of rows and columns using the grid-template- rows and grid-template-columns properties. They’re applied to the grid container.

Both grid-template-rows and grid-template-columns accept what’s known as a track list. The track list is a space-separated string that specifies grid line names and sizes of each position in the row or column.

Each value in a track list creates a new space—a track—within the row or column. You can use lengths, flexible length units (discussed later in this chapter), or percentages. You can also use  sizing values such as auto , min-content and max-conent .

Let’s define a grid with three columns, each 25rem units wide and two rows, each 10rem units tall:

.grid {

display: grid;

grid-template-columns: 35rem 35rem 35rem;

grid-template-rows: 10rem 10rem;

}

Let’s now apply that CSS to the following HTML. Yes, this is all the markup required:

<div class=”grid”>

<div>Grid item A</div>
<div>Grid item B</div>
<div>Grid item C</div>
<div>Grid item D</div>
<div>Grid item E</div>

</div>

We’ve created an explicit grid, organized into the columns and rows, with grid-template- columns and grid-tempLate-rows . The result is pictured below.

Here, we’ve created a grid of evenly sized rows and columns, but that isn’t a requirement of Grid. Let’s tweak our CSS slightly. We’ll change the value of grid-tempLate-coLumns to 40rem 35rem 25rem :

.grid {

display: grid;

grid-template-columns: 40rem 35rem 25rem;

grid-template-rows: 40rem 10rem;

}

Now the second column in our grid is narrower than the first and third, as pictured below.

3. Explicit Grid versus Implicit Grids

In the previous section, we explicitly stated that this grid should have six available grid cells formed by three columns and two rows. This is what’s known as an explicit grid. Here, our grid container only has five children. The remaining position is empty. What if we add more children to the container? When grid items exceed the number of explicitly defined cells, the remaining items are arranged in an implicit grid.

Now we have three rows. Notice, however, that our third row is only as tall as its contents and padding. It’s part of the grid because these items are the children of a grid container. Yet the row isn’t explicitly defined by grid-template-rows . What we have instead is an implicit grid—an explicit grid with additional grid items that exceed the defined number of explicit grid cells.

Items within an implicit grid are sized auto by default. Grid items will expand to accommodate their contents, or fill the remaining vertical space in the container—whichever is taller. If, for example, we set the height property of our container to 100rem , our implicit grid track will expand to be 60rem tall, because implicit grid rows expand to fill the available height of the container.

If we add enough items to create a fourth row, the height of our implicit grid items will be distributed evenly across the remaining 60rem of vertical space in the container. Their computed height will be 30rem each.

In our original example, we’ve explicitly defined only two rows with a height of 10rem each, so our third row defaults to auto sizing. Its height will adjust to the size of its contents and padding.

4. Specifying Track Size for an Implicit Grid

It’s possible, however, to set a kind of default height or width for implicit grid items using the grid-auto-rows and grid-auto-coLumns properties. Let’s update our CSS with grid-auto­rows :

.grid {

display: grid;

grid-template-columns:

25rem 15rem 25rem;

grid-template-rows: 10rem 10rem;

grid-auto-rows: 30rem;

}

Now items in our third row—and any subsequent rows—will be 30rem in height.

There’s one drawback to using the grid-auto-* properties: when the contents of a grid item exceed its dimensions, they will overflow the container (as shown below), and may be clipped visually by elements in other rows. This can happen when using length or percentage units.

One way to avoid this is to use the minmax() function. Let’s rewrite our CSS to use minmax() :

.grid {

display: grid;

grid-template-columns: 25rem 15rem 25rem;

grid-template-rows: 10rem 10rem;

grid-auto-rows: minmax( 30rem, auto );

}

As you may have guessed from its name, minmax() lets us define the minimum and maximum size of a track. It requires two arguments, the first of which is the minimum desired track size. The second argument is the maximum desired size.

In this case, our row will be at least 30rems high. But since we’ve set our maximum size to auto , our track will expand to accommodate the content of that cell. Arguments for minmax() can be lengths or percentages, or one of the auto , min-content , and max- content keywords. Flexible length units, discussed in the next section, are also valid.

Lengths and percentages can be used to define track sizes. Using them may mean that the grid items don’t fill the entire width or height of the container. For example, if our grid container is 70rem wide, grid-tempLate-coLumns: 25rem 15rem 25rem; will only fill about 90% of its horizontal space. On the other hand, if our grid container is only 50rem wide, the total width of our columns will overflow the container’s bounds. To prevent this, use flexible length units.

5. Creating Flexible Grids with Flex Units

Flexible length or flex units are expressed using the fr unit indicator. Flex units tell the browser what fraction or proportion of the leftover space in a grid container should be allocated to each grid item. They’re a ratio, not a true length value in the way px , em , or cm are.

There’s a formula for calculating the used width of an item when using flexible units: (flex * leftover space) + sum of all flex factors. Leftover space is what remains after deducting the known size of items (the specification calls this “definite size” ), the size of grid gaps, and grid item padding from the size of the grid container.

Consider the CSS below:

[id=grid] {

display: grid;

grid-template-columns: 3fr 2fr 1fr;

width: 1500px;

}

[id=grid] > div {

padding: 10px;

}

We’ll pair it with the following HTML:

<div id=”grid”>

<div>Grid item A</div>

<div>Grid item B</div>

<div>

Grid item C

<img src=”400×300.png”>

</div>

<div>Grid item D</div>

<div>Grid item E</div>

<div>Grid item F</div>

</div>

You can see the result in the image below, which illustrates how flexible length units maintain grid proportions, rather than absolute lengths.

The image in our grid has intrinsic dimensions: a width of 400 pixels and height of 300 pixels. As a result, the third column must be at least 400 pixels wide. Each grid item also has ten pixels of padding along both the horizontal and vertical axes. That increases the size of our column by 20 pixels, for a width of 420px .

Next, the browser subtracts that width from the width of our grid container: 1500 – 420 = 1080. That leaves 1,080 pixels of leftover space to distribute across the other two columns of this grid.

Dividing 1080 by 5—the sum of the flex factors of the first two columns—gives us a quotient of 216. Multiply the first two flex factors by that quotient and we end up with columns that are 648px (216 times 3) and 432px (216 times 2) wide, as shown above. It’s very similar to the way browsers distribute remaining space for flexible box layout.

Because these units are ratios and not absolute lengths, grid-temptate-cotumns: 2fr 2fr 2fr is equivalent to grid-template-columns: 1fr 1fr 1fr .

6. Using the grid-template Shorthand Property

We can also indicate the number of rows and columns using the grid-template property. Its syntax is as follows:

grid-template:

/ [column track list]

Remember this block of CSS from earlier in the chapter?

.grid {

display: grid;

grid-template-columns: 25rem 25rem 25rem;

grid-template-rows: 10rem 10rem;

}

We can combine the second and third lines using grid-template :

.grid {

display: grid;

grid-template: 10rem 10rem / 25rem 25rem 25rem;

}

For clarity, however, you may still prefer to use the longhand properties.

7. Repeating Rows and Columns

In many cases, you’ll want grid columns or rows that repeat automatically; think of a list of products for sale, or recipe search results. Grid offers a syntax for that—the repeat() function:

.grid {

display: grid;

grid-template-columns: repeat( 3, 1fr );

}

The repeat() function accepts two arguments:

  • the number of times to repeat the track list
  • a track list to repeat

Arguments must be separated by a comma. The first argument may be a positive integer, or the auto-fit or auto-fill keywords. The above CSS produces the following grid. Our 1fr track list is repeated three times.

We could also use a two-column pattern that repeats twice. For example, grid-template- columns: repeat( 2, 1fr 3fr ); produces a four-column grid. As the next image shows, the first and third columns are one third the width of the second and fourth. In both cases, the value of grid-template-rows is auto .

8. Repeating Columns with auto-fit or auto-fill

Both of the preceding examples tell the browser: “Here’s a track list pattern; please repeat it X number of times.” What you may want to tell the browser instead, is: “Please fit as many columns or rows as you can within this grid container.” For that, we can use auto-fit or auto-fill as the first argument for repeat() , in combination with minmax() .

What’s the difference between auto-fit and auto-fill ?

  • auto-fit fits as many grid items as it can within a track line, and collapses empty tracks.
  • auto-fill fits as many grid items as it can within a track line, but doesn’t collapse empty tracks.

This difference becomes apparent when the grid container’s width exceeds the maximum total width of its grid items. Let’s compare some CSS:

.grid {

display: grid;

width: 1500px;

}

.autofill {

grid-template-columns: repeat( auto-fill, minmax( 100px, 1fr ) );

}

.autofit {

grid-template-columns: repeat( auto-fit, minmax( 100px, 1fr ) );

}

And let’s apply this CSS to the HTML below:

<div class=”grid autofill”>

<div>Grid item A</div>
<div>Grid item B</div>
<div>Grid item C</div>
<div>Grid item D </div>
<div>Grid item E</div>

</div>

<div class=”grid autofit”>

<div>Grid item A</div>
<div>Grid item B</div>
<div>Grid item C</div>
<div>Grid item D </div>
<div>Grid item E</div>

</div>

The only difference between these two grid layouts is that one uses auto-fiLL and the other uses auto-fit . But compare the two grids in the image below.

In both grids, the total maximum width of the grid items is less than that of the grid container. However, in the top grid—our auto-fiLL grid—that excess space is filled in by additional, empty grid items. The following image provides a visualization of the difference between auto-fiLL and auto-fit provided by the Firefox grid inspector.

The empty, auto-fiLL cells are highlighted with dotted lines. Compare that to the bottom grid, in which each grid item is stretched to fit the available space.

9. Line-based Grid Placement

So far, we’ve discussed simple grids that are neatly aligned rows and columns of boxes. But Grid layout is far more robust and flexible than that. We can also use it to create complex layouts, like the one pictured below.

The layout pictured above uses line-based grid placement, a core feature of CSS Grid. We’ll look at how to create this layout later in this section. But first, let’s discuss grid lines.

10. Understanding Grid Lines

Grid lines are horizontal and vertical lines that separate rows and columns, as shown below. These lines exist on each side of a row or column, but don’t affect its dimensions.

The space between each grid line is known as a grid track. A grid track can be a row or a column; the phrase itself is a generic term for both. Grid columns and grid rows intersect to form grid cells.

Most desktop browsers have grid layout inspectors as part of their developer tools. In Firefox, look for the crosshatch icon between display and grid . Clicking that crosshatch icon displays (or hides) the grid overlay.

In Chrome, Safari and Edge, look instead for the grid label next to a grid container in the Elements panel. Clicking the label activates the grid inspector in those browsers.

The image below shows the Firefox grid overlay in action.

Notice that each edge of each column in this grid is bounded by a grid line, and each of these lines has a numeric index. The same is true for each row.

Grid line numbering begins with 1, and the count begins at the start of the grid container. When the text direction is left to right, the starting point is the left edge. When the direction is right to left, the starting point is the right edge.

Each grid line may also have a negative index. Negative index counts begin with -1, and decrement from the ending edge of the explicit grid. So an index of -1 specifies the ending edge of the container, while an index of -2 specifies one grid line in from that one, and so on. (We’ll see an example of negative line numbers in use shortly.)

Line numbers can be used to place items within the grid using the grid-coLumn-start / grid- coLumn-end and grid-row-start / grid-row-end properties. Here’s an example:

.grid-10cols {

display: grid;

}

#a {

grid-column-start: 1;

grid-column-end: 11;

}

#b {

grid-column-start: 1;

grid-column-end: 6;

}

#c {

grid-column-start: 6;

grid-column-end: 11;

}

We’ll pair that CSS with the HTML below:

<div class=”grid-10cols”>

<div id=”a”>Grid item A</div>

<div id=”b”>Grid item B</div>

<div id=”c”>Grid item C</div>

</div>

The image below illustrates the result.

As shown above, #a fills the space between line 1 and line 11, or the entire width of the grid.

#b begins at the first line and ends at the sixth. #c begins at the sixth line and extends to line 11. With grid-*-start and grid-*-end , we’re telling the browser to align the starting and ending edges of our grid items with specific grid lines.

We haven’t used either of the grid-template-* properties in this example. By defining a start line and an end line, we’ve created ten implicit grid tracks. Note that because this grid hasn’t been explicitly defined—with something like grid-template-columns: repeat(10, 1fr) —we’ve lost the ability to use negative grid line indexes for placement.

11. Spanning Rows or Columns

In the example above, we’ve used line numbers to indicate where our grid items should begin and end. Another way to do this is with the span keyword. The span keyword indicates how many tracks—that is, how many rows or columns—a grid item should occupy. We could, in other words, rewrite our CSS like so:

.grid-10cols {

display: grid;

}

#a {

grid-column-start: span 10;

}

#b {

grid-column-start: span 5;

}

#c {

grid-column-start: span 5;

}

Again, span indicates how many columns or rows a grid item should occupy. Line indexes indicate where to align the edges of a grid item.

12. Complex Layouts with Line-based Placement

Let’s return to some code we used earlier. Once again, our markup is simple—a containing <div> with eight children:

<div class=”grid-10cols-complex”>

<div id=”a”>Grid item A</div>
<div id=”b”>Grid item B</div>
<div id=”c”>Grid item C</div>
<div id=”d”>Grid item D </div>
<div id=”e”>Grid item E</div>
<div id=”f”>Grid item F</div>
<div id=”g”>Grid item G</div>
<div id=”h”>Grid item H</div>

</div>

For this layout, we’ll explicitly define a five-row, ten-column grid:

.grid-10cols-complex {

display: grid;

/* Syntax: grid-template: [rows] / [columns] */

grid-template: repeat(5, 9.5rem) / repeat(10, 10%);

}

Explicitly defining a grid isn’t strictly necessary for line-based placement. In this case, however, it ensures that each box in our layout has the right proportions. The next step is to place our grid items:

#a, #h {

grid-column-start: span 10;    /* Span the entire grid */

}

#b {

grid-row-start: span 3; grid-column-start: span 2;

}

#c, #d {

grid-column-start: span 4;

}

#e, #f {

grid-column-start: span 3;

}

#f, #g {

grid-column-end: -1;   /* Begin from the container’s ending edge */

}

#g {

grid-column-start: span 5;

}

Here, both #a and #h span all ten columns of our grid, while the other elements span between two and five columns, as illustrated below. Element #b also spans three rows.

Notice that for #f and #g , we’ve used a negative line index. Remember that negative line indexes begin at the ending edge of the container. With grid-column-end: -1 , we’ve told the browser to align the ending edge of #f and #g with the ending edge of our grid container. These elements still span three and five columns, respectively, but their ending edges align with the ending edge of the container.

13. Using Named Grid Areas

One of the more clever aspects of CSS Grid is template areas. Template areas use the grid- template-areas property, and let us define our grid in terms of named slots. We can use template areas, in combination with grid placement properties, to define complex grid layouts that are still readable.

Take the layout shown in the image below. It’s a fairly conventional, two-column layout with a header, footer, and main content area, along with a right-hand navigation menu.

Here’s the markup we’ll use to create this page. It’s simplified to emphasize the document’s structure:

<!DOCTYPE html>

<html lang=”en-US”>

<head>

<title>GoodRecipes! Tuna with zucchini noodles</title>

</head>

<body>

<header>…</header>

<article>…</article>

<nav>…</nav>

<footer>…</footer>

</body>

</html>

Named template areas can be hard to understand at first. We still need to define rows and columns. Then we can define named areas that span some or all of those rows and columns using the grid-template-areas property. Here’s an example:

body {

display: grid;

/*

Using the longhand properties for the sake of clarity. We could also use grid-template: repeat(2, auto) / 4fr 1fr instead.

*/

grid-template-rows: repeat(2, auto); grid-template-columns: 4fr 1fr; grid-template-areas: “pagehead pagehead” “mains navigation” “pagefoot pagefoot”;

}

Yes, the syntax of grid-template-areas is a little weird. Template areas are strings and must be enclosed in single or double quotes. Each template area corresponds to a row in the grid. Columns within each row are delineated by a space.

Now, in order for grid-template-areas to work, we have to account for every position in the grid. That’s why we’re repeating pagehead and pagefoot . Repeating a name within a template string indicates that the area should span multiple columns.

Once we’ve defined our template areas, the last step is to assign our elements to each area using the grid-area property:

header {

grid-area: pagehead;

}

article {

grid-area: mains;

}

nav {

grid-area: navigation;

}

footer {

grid-area: pagefoot;

}

This tells the browser to place the <header> element in the pagehead area, the <articLe> element in the mains area, and so forth.

14. Spacing Grid Items

In all of the grids we’ve created thus far, the edges of our grid items abut each other. As with multicolumn layout, however, we can add gutters to our grid. We know that the coLumn-gap property adds spacing between columns. With grid layout, we can also use the row-gap property to add spacing between grid rows. Both properties apply to the grid container:

.grid {

display: grid;

grid-template: 20rem 20rem / 35rem 35rem 35rem 35rem;

column-gap: 1rem;

row-gap: 1rem;

}

The image below shows the effect of adding irem row and column gaps.

In a grid formatting context, coLumn-gap: normal and row-gap: normal resolve to a used value of 0px . That behavior differs from multicolumn layout, where coLumn-gap: normal resolves to a used value of iem .

Only length and percentage values are valid for coLumn-gap and row-gap (and the gap shorthand property). If you’d rather have the browser automatically distribute boxes along each grid axis, use justify-content or aLign-items instead. We’ll discuss both properties in the “Box Alignment and Distribution” section below.

15. The gap Shorthand Property

We can also specify both column-gap and row-gap at once using the gap shorthand property. The first value of gap becomes the size of the row gap; the second value is the column gap. Providing only one value sets the same gap size for both properties. In other words, we can rewrite column-gap: 1rem; row-gap: 1rem; as gap: 1rem; .

Older versions of the Grid specification defined grid-coLumn-gap and grid-row-gap properties. These have been replaced by coLumn-gap , which can also be used with multicolumn layout and Flexbox in most browsers. (Safari, as of version 15, doesn’t support the use of gap with multicolumn layout.) For compatibility with older browsers, include the legacy grid-row-gap and grid-coLumn-gap properties in addition to coLumn-gap and row- gap . Or, if you use the shorthand gap property, include grid-gap as well.

Using coLumn-gap and row-gap aren’t the only ways to space grid content. We can also use the justify-* and aLign* properties to distribute grid items within the available space. Since most of these properties are common to Grid and Flexbox, we’ll discuss them together in the section “Box Alignment and Distribution” later in this chapter.

16. Grid Items and Margins

Grid items can have margins of their own. However, margins work a bit differently in a grid formatting context than they do in a block formatting context.

Grid cells, and the grid lines that bound them, form containing blocks for grid items. As a result, adjacent margins of grid items do not collapse. That’s the opposite of what happens in a block formatting context.

For grid items, top and bottom margins of 1rem result in 2rem of space between the content boxes, as shown above. And because grid item margins fall within the containing block, they may affect the dimensions of auto -sized grid tracks.

17. Images within Grids

Images within grid cells work similarly to the way they behave in multicolumn layouts. When the track size uses length or percentage units, images may overflow the grid cell if their dimensions exceed those of the cell.

However, when the track sizing function is auto , or uses flex units ( fr ), the track containing that grid cell expands to accommodate the image.

As in multicolumn layout, we can constrain the image dimensions to those of its grid cell by setting its width to 100%.

Floating an image or other elements within a grid cell works as you’d expect. But you can’t float a grid item. Floated siblings of grid containers also don’t intrude on the grid container.

18. Progressively Enhanced Layouts with Grid and display: contents

Earlier in this chapter, we talked about the contents value of the display property. Remember that display: contents prevents the browser from generating an element box. In a grid formatting context, this turns grandchild elements into grid items.

Say you have extra <div> elements in your markup to create a grid-like layout in browsers that don’t support CSS Grid, as we have in the following block of code:

<div class=”grid”>

<div class=”grid-row”>

<div>Item 1</div>

<div>Item 2</div>

<div>Item 3</div>

<div>Item 4</div>

</div>

<div class=”grid-row”>

<div>Item 5</div>

<div>Item 6</div>

<div>Item 7</div>

<div>Item 8</div>

</div>

</div>

In browsers that don’t support Grid, you might use Flexbox to create a grid-like layout like the one shown below:

.grid {

width: 80%;

margin: auto;

}

.grid-row {

display: flex;

}

.grid-row > * {

flex: 0 0 calc(25% – 2rem);

background-color: #121212;

color: var(–color-a);

margin: 0 2rem 2rem 0;

padding: 1rem;

}

Then to progressively enhance this layout, you might add display: grid to div.grid . Doing so, however, turns those .grid-row elements into grid items. As a result, the children of .grid-row don’t participate in a grid formatting context, as illustrated below.

If we add display: contents to .grid-row , though, the grandchild elements participate in the grid formatting context created by div.grid . Here’s that CSS:

@supports (display: grid) {

.grid {

display: grid;

grid-template-columns: repeat(4, 1fr);

}

.grid-row {

display: contents;

}

.grid-row > * {

width: unset;

}

}

The image below shows the resulting layout.

Notice that we didn’t undo or override the flex declaration. Since we’ve switched the display value to grid , flex-related properties no longer apply and they’re ignored.

Although display: contents can cause severe accessibility issues, in this particular instance it doesn’t. Unlike lists or headings, <div> elements don’t describe document structure, nor do they have defined functionality in the way that elements such as <button> and <input> do. Because there’s no behavior to break, using display: contents for our <div> element doesn’t break anything.

19. Grid Conclusion

CSS Grid is a dense topic. We’ve really just scratched the surface here. Luckily, there’s a wealth of resources that can help you learn more.

I believe in reading specifications where possible. In my opinion, the CSS Grid specification is quite readable, and it’s a good place to begin your own explorations of grid layout. But specifications do tend to contain a lot of jargon. They’re written not only for web developers, but also for those tasked with implementing them in browsers.

Rachel Andrew’s Grid by Example was created for a web developer audience. The site includes grid layout tutorials and a collection of common user interface patterns. Be sure to visit the site’s Resources section too. It’s a cornucopia of links that demonstrate what you can do with CSS Grid.

Jen Simmons’ Experimental Layout Lab is also chock-full of examples that illustrate Grid’s possibilities. If video is more your style, Simmons’ Layout Land YouTube channel includes video walk-throughs of grid and other layout topics.

When you need more of a cheatsheet-style reference, try “A Complete Guide to Grid , by CSS-Tricks.

Source: Brown Tiffany B (2021), CSS , SitePoint; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *