Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

CSS  Module 11 – CSS Grid

Grid areas and named lines

Draw a layout as text with grid-template-areas, give grid lines names, and rearrange areas at breakpoints without changing the HTML or its reading order.

  • Beginner
  • 25 minutes
  • Examples run with Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free: 25 minutes

What you will learn

  • Describe a layout visually with grid-template-areas
  • Name grid lines and place items with them
  • Move areas at breakpoints without touching the HTML

Before you start

What you’ll learn · Before you start

What you will learn

  • Describe a layout visually with grid-template-areas
  • Name grid lines and place items with them
  • Move areas at breakpoints without touching the HTML

Before you start

On this page

grid-template-areas lets you draw a layout as text. Each string is a row and each word in it a cell, so "header header" "nav main" is a header across two columns above a navigation column and the main content. Items join an area by name with grid-area: header, and the browser works out the rows, columns and spans. Naming also works for lines: [content-start] in a track list gives a line a name, and an area named main gives you the lines main-start and main-end as well. The value of areas is that a layout change at a breakpoint becomes a new drawing, written next to the old one, while the HTML stays as it is. The cost is that visual order and reading order can drift apart, and the measurements here, from Chromium 153, Firefox 155 and WebKit 26.6, show by how much.

Drawing the layout in text

The page below has a header, a navigation, the main content, related links and a footer, in that order in the HTML. Each item names its area, and each breakpoint draws a new map of areas:

One page, three layouts HTML · areas.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>One page, three layouts</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .page { display: grid; gap: 16px; }
    .page > * { background: #dde; }

    .site-header { grid-area: header; block-size: 64px; }
    .site-nav    { grid-area: nav; }
    .content     { grid-area: main; block-size: 200px; }
    .related     { grid-area: aside; block-size: 120px; }
    .site-footer { grid-area: footer; block-size: 48px; }

    /* Phones: one column, in the order of the HTML. */
    .page {
      grid-template-areas:
        "header"
        "nav"
        "main"
        "aside"
        "footer";
    }
    /* From 768px: the navigation beside the content. */
    @media (min-width: 48rem) {
      .page {
        grid-template-columns: 12rem 1fr;
        grid-template-areas:
          "header header"
          "nav    main"
          "nav    aside"
          "footer footer";
      }
    }
    /* From 1200px: three columns. */
    @media (min-width: 75rem) {
      .page {
        grid-template-columns: 12rem 1fr 16rem;
        grid-template-areas:
          "header header header"
          "nav    main   aside"
          "footer footer footer";
      }
    }
  </style>
</head>
<body>
  <div class="page">
    <header class="site-header">Header</header>
    <nav class="site-nav">Navigation</nav>
    <main class="content">Main content</main>
    <aside class="related">Related</aside>
    <footer class="site-footer">Footer</footer>
  </div>
  <script>
    // Each area's rectangle at the current width: left edge, top edge, width and height, in px.
    const round = (n) => Math.round(n * 100) / 100;
    console.log(`viewport ${innerWidth}px: areas ${getComputedStyle(document.querySelector('.page')).gridTemplateAreas}`);
    for (const item of document.querySelector('.page').children) {
      const r = item.getBoundingClientRect();
      console.log(`  ${item.textContent}: x ${round(r.x)}, y ${round(r.y)}, ${round(r.width)} × ${round(r.height)}`);
    }
  </script>
</body>
</html>
The same five areas in one column on phones, two columns from 768 px and three from 1200 px, each beside its area strings.One HTML order, three maps of areasBelow 768 px: one columnheadernavmainasidefooter"header""nav""main""aside""footer"From 768 px: two columnsheadernavmainasidefooter"header header""nav main""nav aside""footer footer"From 1200 px: three columnsheadernavmainasidefooter"header header header""nav main aside""footer footer footer"The HTML order never changes:header, nav, main, aside, footer.

The lesson's page at three widths, and the strings that draw it

Text description of the diagram

Three wireframes of the lesson's page, one above the other, each with the strings of its grid-template-areas value beside it, one string per row.

  1. Below 768 px there is one column: header, nav, main, aside and footer are stacked in that order. The strings are "header", "nav", "main", "aside" and "footer".
  2. From 768 px there are two columns. The header and the footer run across both. The nav fills the left column of rows 2 and 3, while main sits to its right in row 2 and aside below main in row 3. The strings are "header header", "nav main", "nav aside" and "footer footer": repeating nav in two rows makes it two rows tall.
  3. From 1200 px there are three columns. The header and footer run across all three, and row 2 holds nav, main and aside side by side. The strings are "header header header", "nav main aside" and "footer footer footer".

Under the wireframes, a note says that the HTML order stays header, nav, main, aside, footer in every layout: only the CSS changes.

The three engines drew identical rectangles at each width, with 16 px gaps. At 360 px every area was 360 px wide, stacked in the HTML order and 64, 24, 200, 120 and 48 px tall. The two wider layouts, width × height in px:

Area 900 px 1440 px
header 900 × 64 1440 × 64
nav 192 × 336 192 × 200
main 692 × 200 960 × 200
aside 692 × 120 256 × 120
footer 900 × 48 1440 × 48

At 900 px the content starts 208 px from the left, after the 192 px navigation and one gap, and the related links sit under it at 296 px from the top; at 1440 px they move up beside it, 1184 px from the left. Repeating nav in two rows of the 900 px map made the navigation two rows tall, 200 + 16 + 120 = 336 px, beside both the content and the related links. At 1440 px the same five items sit in three columns, and nothing in the HTML changed. The rows were never sized: tracks that a template creates take their size from grid-auto-rows, which is auto unless you set it, so each row fitted its tallest item.

CSS Grid Generator Paint named areas across the cells of a grid and add breakpoints; the tool writes the grid-template-areas strings for each one.

The rules a template must follow

Four rules decide whether the browser accepts a template:

  • Every string is one row, and every row has the same number of cells. Spaces separate the cells; extra spaces only line the columns up for the reader.
  • A name repeated in neighbouring cells makes one area, across columns, down rows, or both.
  • A run of dots is an empty cell. . and ... both mean one cell with no area in it.
  • Every area must be a rectangle. An L shape, or the same name in two separate places, is not allowed.

Breaking a rule does not produce a half-right layout: the whole declaration is invalid and ignored, as if it had never been written. One more mistake is valid CSS and fails in another way. Three grids, each 400 px wide with two 1fr columns, 8 px gaps, and 100 by 40 px implicit tracks:

Three ways to break grid-template-areas HTML · broken-areas.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Three ways to break grid-template-areas</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .grid {
      display: grid;
      inline-size: 400px;
      grid-template-columns: 1fr 1fr;
      grid-auto-rows: 40px;
      grid-auto-columns: 100px;
      gap: 8px;
      margin-block-end: 24px;
    }
    .grid > * { background: #dde; }

    /* 1. Not a rectangle: "a" is L-shaped. */
    #l-shape { grid-template-areas: "a a" "a b"; }
    #l-shape .one { grid-area: a; }
    #l-shape .two { grid-area: b; }

    /* 2. Ragged rows: three cells in the first row, two in the second. */
    #ragged { grid-template-areas: "a a b" "c c"; }
    #ragged .one { grid-area: a; }
    #ragged .two { grid-area: c; }

    /* 3. A valid template, but the item asks for an area that does not exist. */
    #typo { grid-template-areas: "header header" "main side"; }
    #typo .one { grid-area: header; }
    #typo .two { grid-area: mian; }   /* "main", mistyped */
  </style>
</head>
<body>
  <div class="grid" id="l-shape"><div class="one">a</div><div class="two">b</div></div>
  <div class="grid" id="ragged"><div class="one">a</div><div class="two">c</div></div>
  <div class="grid" id="typo"><div class="one">header</div><div class="two">mian</div></div>
  <script>
    const round = (n) => Math.round(n * 100) / 100;
    for (const grid of document.querySelectorAll('.grid')) {
      const style = getComputedStyle(grid);
      const box = grid.getBoundingClientRect();
      console.log(`${grid.id}: grid-template-areas is ${style.gridTemplateAreas}; columns ${style.gridTemplateColumns}; rows ${style.gridTemplateRows}`);
      for (const item of grid.children) {
        const r = item.getBoundingClientRect();
        console.log(`  item ${item.textContent}: x ${round(r.x - box.x)}, y ${round(r.y - box.y)}, ${round(r.width)} × ${round(r.height)}`);
      }
      console.log(`  the grid is ${round(box.width)}px wide and its content ${grid.scrollWidth}px`);
    }
  </script>
</body>
</html>

What all three engines did:

Template Computed value Where the items went
An L-shaped area none Both into one new cell
Rows of 3 and 2 cells none Both into one new cell
Valid, one name mistyped the template A new row 4 and column 4

In the first two grids the computed value of grid-template-areas was none, and both items landed in the same new cell, at row 2 and column 4, one on top of the other.

The last row is the bug you will meet in real code. No area is called “mian”, so the browser looks for lines called mian-start and mian-end, finds none, and uses the first line after the explicit grid instead, in both directions. The item ended up below and to the right of the layout, two empty tracks appeared to reach it, and the two real columns shrank from 196 to 88 px to make room. When a layout suddenly grows a stray row and column, check every grid-area name against the template first. A program that reads templates the way a browser does:

Reading templates, and refusing two of them JavaScript · areas.mjs
// What a browser reads from grid-template-areas (CSS Grid Level 2): each string is a row, each word a cell, a run of
// dots an empty cell. Every row needs the same number of cells and every name must cover a rectangle, or the whole
// declaration is invalid. Each area gives an item its lines: <name>-start and <name>-end in both directions.

function areasOf(rows) {
  const grid = rows.map((row) => row.trim().split(/\s+/).map((cell) => (/^\.+$/.test(cell) ? null : cell)));
  if (grid.some((cells) => cells.length !== grid[0].length)) {
    throw new Error(`rows of ${grid.map((cells) => cells.length).join(' and ')} cells`);
  }
  const areas = new Map();
  grid.forEach((cells, r) => cells.forEach((name, c) => {
    if (!name) return;
    const a = areas.get(name) ?? { top: r, bottom: r, left: c, right: c, cells: 0 };
    areas.set(name, { top: Math.min(a.top, r), bottom: Math.max(a.bottom, r), left: Math.min(a.left, c), right: Math.max(a.right, c), cells: a.cells + 1 });
  }));
  for (const [name, a] of areas) {
    if (a.cells !== (a.bottom - a.top + 1) * (a.right - a.left + 1)) throw new Error(`"${name}" is not a rectangle`);
  }
  return areas;
}

const layouts = {
  'phones': ['header', 'nav', 'main', 'aside', 'footer'],
  'from 768px': ['header header', 'nav    main', 'nav    aside', 'footer footer'],
  'from 1200px': ['header header header', 'nav    main   aside', 'footer footer footer'],
  'L-shaped': ['a a', 'a b'],
  'ragged': ['a a b', 'c c'],
};
for (const [label, rows] of Object.entries(layouts)) {
  try {
    const areas = areasOf(rows);
    const list = [...areas].map(([name, a]) => `${name} ${a.top + 1} / ${a.left + 1} / ${a.bottom + 2} / ${a.right + 2}`);
    console.log(`${label}: ${list.join(', ')}`);
  } catch (error) {
    console.log(`${label}: invalid, ${error.message}; the declaration is ignored`);
  }
}

Output

phones: header 1 / 1 / 2 / 2, nav 2 / 1 / 3 / 2, main 3 / 1 / 4 / 2, aside 4 / 1 / 5 / 2, footer 5 / 1 / 6 / 2
from 768px: header 1 / 1 / 2 / 3, nav 2 / 1 / 4 / 2, main 2 / 2 / 3 / 3, aside 3 / 2 / 4 / 3, footer 4 / 1 / 5 / 3
from 1200px: header 1 / 1 / 2 / 4, nav 2 / 1 / 3 / 2, main 2 / 2 / 3 / 3, aside 2 / 3 / 3 / 4, footer 3 / 1 / 4 / 4
L-shaped: invalid, "a" is not a rectangle; the declaration is ignored
ragged: invalid, rows of 3 and 2 cells; the declaration is ignored

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node areas.mjs

The four numbers it prints for each area are row start, column start, row end and column end: the same order you would write in grid-area: 2 / 1 / 4 / 2 to place an item by lines instead of by name.

Visual order and reading order

Areas move boxes on screen. They do not move anything in the HTML, which is the order that keyboard focus and screen readers follow. That is fine when the move does not change the meaning, as when the navigation goes beside the content on a wide screen. It becomes a problem when the order people read in and the order they navigate in disagree. A common phone design puts the content first and the navigation after it:

Navigation moved below the content on phones HTML · order-trap.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Navigation moved below the content on phones</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .page { display: grid; gap: 16px; }
    .page > * { background: #dde; padding: 8px; }
    .site-header { grid-area: header; }
    .site-nav    { grid-area: nav; }
    .content     { grid-area: main; block-size: 400px; }
    .site-footer { grid-area: footer; }

    /* Phones: the content first, the navigation after it. The HTML is not changed. */
    .page {
      grid-template-areas:
        "header"
        "main"
        "nav"
        "footer";
    }
  </style>
</head>
<body>
  <div class="page">
    <header class="site-header"><a href="#home">Home</a></header>
    <nav class="site-nav"><a href="#shop">Shop</a> <a href="#offers">Offers</a></nav>
    <main class="content"><a href="#product">The product you came for</a></main>
    <footer class="site-footer"><a href="#help">Help</a></footer>
  </div>
</body>
</html>

At 360 px wide, Tab was pressed five times from the top of the page (Option-Tab in WebKit):

Link, in Tab order From the top
Home 11 px
Shop 499 px
Offers 499 px
The product you came for 67 px
Help 555 px

All three engines did exactly this. A keyboard user who can see the screen is taken from the header past the whole content to the menu at the bottom, then back up to the product, which is what WCAG 2.2 success criteria 1.3.2 (Meaningful Sequence) and 2.4.3 (Focus Order) warn about, and what the Grid specification means when it says placement is for visual, not logical, reordering. Two fixes keep the orders together. If the content should come first on every screen, put it first in the HTML and draw the areas around that order. If the navigation belongs at the top, keep it at the top on phones too, made compact, for example as a single menu button.

Naming lines

Line numbers work, but names survive changes to a track list. Put names in square brackets between the track sizes, and place items between them. A classic use is an article whose text keeps a readable width while some elements run from edge to edge:

A full-bleed article with named lines HTML · full-bleed.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>A full-bleed article with named lines</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .article {
      display: grid;
      row-gap: 16px;
      grid-template-columns:
        [full-start] minmax(1rem, 1fr)
        [content-start] minmax(0, 40rem) [content-end]
        minmax(1rem, 1fr) [full-end];
    }
    .article > * { grid-column: content; margin: 0; background: #dde; }   /* content-start to content-end */
    .article > .full-bleed { grid-column: full; }                /* full-start to full-end */
  </style>
</head>
<body>
  <article class="article">
    <h1>A heading</h1>
    <p>A paragraph of the article, kept to a readable width.</p>
    <figure class="full-bleed">A photo that runs from edge to edge</figure>
    <p>The text goes on at the same width as before.</p>
  </article>
  <script>
    const round = (n) => Math.round(n * 100) / 100;
    const article = document.querySelector('.article');
    console.log(`viewport ${innerWidth}px: columns ${getComputedStyle(article).gridTemplateColumns}`);
    for (const item of article.children) {
      const r = item.getBoundingClientRect();
      console.log(`  ${item.tagName.toLowerCase()}${item.className ? '.' + item.className : ''}: x ${round(r.x)}, width ${round(r.width)}`);
    }
  </script>
</body>
</html>
At Columns, px Text, figure
360 px 16 · 328 · 16 328, 360 px
1440 px 400 · 640 · 400 640, 1440 px

All three engines agreed: the text started 16 px from the left on the phone and 400 px on the wide screen, and the figure started at 0 on both. The middle column grows to its 40rem limit first and the two side columns share what is left, never less than 1rem each, so the text never touches the edges of a phone. grid-column: content works because of another naming rule: lines called content-start and content-end make an area called content, just as an area named main makes lines called main-start and main-end. Two more details matter. A line can carry several names, as in [content-end gutter-start]. And the first run of this page gave the figure a 40 px margin on each side, because browsers give figure side margins by default; grid items keep their margins, which do not collapse into the gaps, so the example resets them to 0.

The grid-template shorthand

grid-template sets the rows, the columns and the areas in one declaration, with each row’s size after its string and the column sizes after a slash. It is compact and hard to edit: changing one column means rewriting a block that mixes three properties. The longhands are friendlier in code that a team maintains. The grid shorthand goes further and also resets grid-auto-rows, grid-auto-columns and grid-auto-flow, which can undo settings made elsewhere.

Longhands, then the same layout as a shorthand CSS · template.css
/* The 768px layout written with longhands: each part can be read and changed on its own. */
.page {
  grid-template-columns: 12rem 1fr;
  grid-template-rows: auto 1fr auto auto;
  grid-template-areas:
    "header header"
    "nav    main"
    "nav    aside"
    "footer footer";
}

/* The same layout with the grid-template shorthand: each row's size follows its string, and the column sizes come
   last, after a slash. */
.page {
  grid-template:
    "header header" auto
    "nav    main"   1fr
    "nav    aside"  auto
    "footer footer" auto
    / 12rem 1fr;
}

Practise: a dashboard in three layouts

Exercise · Medium · JavaScript

Draw a dashboard's three layouts with grid areas

A dashboard has five parts, named title, kpis, chart, table and alerts, in that order in its HTML. Its three wireframes, written as rows of cells from top to bottom:

- Below 600 px: one column, the five parts stacked in the HTML order. - From 600 px to 1023 px: two columns. Row 1: title across both. Row 2: kpis, then alerts. Row 3: chart across both. Row 4: table across both. - From 1024 px: three columns. Row 1: title across all three. Row 2: kpis, chart, then alerts. Row 3: table across the first two columns, while alerts carries on down the third.

Write two functions in dashboard.mjs and export them.

The first, dashboardAreas(width), returns the value of grid-template-areas for a viewport width px wide, as an array of strings, one per row (["title", "kpis", …] for the phone layout).

The second, placeOf(rows, name), returns where the browser puts an item with grid-area: <name> for such an array: { row: [start, end], column: [start, end] }, the lines the area runs between (line 1 is the top or left edge). Like a browser, it returns null when the template is invalid (rows with different numbers of cells, or a name whose cells do not form one rectangle) or when no cell has that name. A run of dots, such as . or ..., is one empty cell.

The sample tests import both functions from dashboard.mjs and run in your browser.

Starter code · dashboard.mjs

/** The value of grid-template-areas for a viewport `width` px wide: one string per row. */
export function dashboardAreas(width) {
  // Replace this line with your code.
  return ['title kpis chart table alerts'];
}

/** The lines an item with grid-area: <name> sits between, or null. */
export function placeOf(rows, name) {
  // Replace this line with your code.
  return null;
}
The sample tests · dashboard.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { dashboardAreas, placeOf } from './dashboard.mjs';

const at = (width) => (name) => placeOf(dashboardAreas(width), name);

test('placeOf reads names, spans and empty cells', () => {
  const rows = ['header header', 'nav main', '. footer'];
  assert.deepEqual(placeOf(rows, 'header'), { row: [1, 2], column: [1, 3] });
  assert.deepEqual(placeOf(rows, 'nav'), { row: [2, 3], column: [1, 2] });
  assert.deepEqual(placeOf(rows, 'footer'), { row: [3, 4], column: [2, 3] });
  assert.deepEqual(placeOf(['a ... b'], 'b'), { row: [1, 2], column: [3, 4] });
  assert.equal(placeOf(rows, 'aside'), null);
});

test('placeOf refuses what a browser refuses', () => {
  assert.equal(placeOf(['a a', 'a b'], 'b'), null);
  assert.equal(placeOf(['a a b', 'c c'], 'a'), null);
  assert.deepEqual(placeOf(['a a', 'a a'], 'a'), { row: [1, 3], column: [1, 3] });
});

test('the phone layout at 360px', () => {
  const place = at(360);
  assert.deepEqual(place('title'), { row: [1, 2], column: [1, 2] });
  assert.deepEqual(place('kpis'), { row: [2, 3], column: [1, 2] });
  assert.deepEqual(place('chart'), { row: [3, 4], column: [1, 2] });
  assert.deepEqual(place('table'), { row: [4, 5], column: [1, 2] });
  assert.deepEqual(place('alerts'), { row: [5, 6], column: [1, 2] });
});

test('the two-column layout at 600 and 900px', () => {
  for (const width of [600, 900]) {
    const place = at(width);
    assert.deepEqual(place('title'), { row: [1, 2], column: [1, 3] });
    assert.deepEqual(place('kpis'), { row: [2, 3], column: [1, 2] });
    assert.deepEqual(place('alerts'), { row: [2, 3], column: [2, 3] });
    assert.deepEqual(place('chart'), { row: [3, 4], column: [1, 3] });
    assert.deepEqual(place('table'), { row: [4, 5], column: [1, 3] });
  }
});

test('the three-column layout at 1024 and 1440px', () => {
  for (const width of [1024, 1440]) {
    const place = at(width);
    assert.deepEqual(place('title'), { row: [1, 2], column: [1, 4] });
    assert.deepEqual(place('kpis'), { row: [2, 3], column: [1, 2] });
    assert.deepEqual(place('chart'), { row: [2, 3], column: [2, 3] });
    assert.deepEqual(place('alerts'), { row: [2, 4], column: [3, 4] });
    assert.deepEqual(place('table'), { row: [3, 4], column: [1, 3] });
  }
});

test('just below each breakpoint', () => {
  assert.equal(dashboardAreas(599).length, 5);
  assert.equal(dashboardAreas(1023).length, 4);
});
A hint

Split each string on whitespace to get its cells, and check that every row has as many cells as the first. For a name, collect the row and column of every cell with that name: its area runs from the smallest row to the largest row plus 1, and the same for columns. It is a rectangle only if the number of cells equals rows × columns.

The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Interview questions

Fresher · What do the strings in grid-template-areas describe, and what does a dot mean?

Each string is one row of the grid, and each word in it is one cell, so the strings draw the layout as a small map. Writing the same name in neighbouring cells makes one named area that spans them, and an item joins it with grid-area: name. A dot, or a run of dots such as ..., is a cell that belongs to no area; items placed automatically can still land there. The browser accepts the map only if every row has the same number of cells and every area is a rectangle; otherwise the whole declaration is ignored. Rows created this way take their size from grid-auto-rows unless grid-template-rows sizes them. Areas also give each item lines named <name>-start and <name>-end, and they change only what you see, never the HTML order that Tab and screen readers follow.

Key takeaways

  • Each string of grid-template-areas is a row, each word a cell; repeat a name to span, use dots for empty cells.
  • Rows with different numbers of cells, or an area that is not a rectangle, make the whole declaration invalid.
  • A mistyped grid-area name is valid CSS: the item lands in new tracks after the explicit grid and squeezes the rest.
  • Areas create lines named <name>-start and <name>-end, and lines with those names create an area.
  • Named lines such as [content-start] keep placements readable and stable when tracks change.
  • Moving areas changes only the visual order; keep the HTML in the order people should read and tab through.
  • Prefer the longhands to grid-template and grid in shared code; grid also resets the implicit-grid properties.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 Which value of grid-template-areas does a browser accept?

    Choose one answer.

    Show the answer to question 1

    Answer: "a a b" "a a c"

    In the first value, "a" covers a two-by-two rectangle and both rows have three cells, so it is valid. In "a b a" the two cells named a are not next to each other, "a a" "b" has rows of different lengths, and "a a" "a b" makes an L shape. Each of those makes the whole declaration invalid, and the browser ignores it.

  2. Question 2 of 6 An item has grid-area: sidebar, but no cell of the template is called sidebar. Where does it go?

    Choose one answer.

    Show the answer to question 2

    Answer: Into a new row and a new column added after the explicit grid, at its bottom right

    With no area and no line of that name, the browser looks for the lines sidebar-start and sidebar-end among the implicit lines, which all count as having every name. The first implicit line comes after the explicit grid in both directions, so the item lands in a new row and column. In the lesson's run the mistyped item also shrank the real columns from 196 to 88 px in all three engines.

  3. Question 3 of 6 Naming an area main in the template gives you something else as well. What?

    Choose one answer.

    Show the answer to question 3

    Answer: Lines called main-start and main-end in both directions, which grid-row and grid-column can use

    Every named area creates implicit named lines at its edges, so grid-column: main-start / main-end and grid-area: main place an item the same way. It works the other way round too: lines named content-start and content-end make an area called content.

  4. Question 4 of 6 On phones, a template moves the nav below the main content, without changing the HTML (header, nav, main, footer). What does a keyboard user meet?

    Choose one answer.

    Show the answer to question 4

    Answer: Tab goes from the header down to the nav at the bottom, then back up to the content

    Focus follows the HTML, not the grid. In the lesson's run, all three engines moved focus from Home (11 px from the top) to Shop (499 px), back up to the product link (67 px), then down to Help (555 px). If the content should come first, change the order of the HTML.

  5. Question 5 of 6 The article grid is [full-start] minmax(1rem, 1fr) [content-start] minmax(0, 40rem) [content-end] minmax(1rem, 1fr) [full-end], 1000px wide, with 16px to the rem. How wide is each side column?

    Type a number.

    Show the answer to question 5

    Answer: 180 px

    The content column grows to its 40rem limit, 640 px, before the fr columns share what is left: 1000 − 640 = 360 px, so 180 px each. At 360 px the content column gets only 328 px, and the side columns keep their 16 px minimum.

  6. Question 6 of 6 What does ... mean inside a string of grid-template-areas?

    Choose one answer.

    Show the answer to question 6

    Answer: One empty cell

    A run of one or more dots with no space inside it is one null cell token. Authors often use as many dots as the longest name has letters, so the columns of the template line up in the code.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.