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

Subgrid: aligning nested content

Line up titles, texts and buttons across a row of cards with CSS subgrid: share the parent grid's rows or columns, override gaps and pass line names down.

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

What you will learn

  • Use subgrid in grid-template-rows and grid-template-columns so nested grids reuse their parent's tracks
  • Arrange card titles, bodies and buttons in shared rows across a row of cards
  • Combine subgrid with gap overrides and named lines

Before you start

What you’ll learn · Before you start

What you will learn

  • Use subgrid in grid-template-rows and grid-template-columns so nested grids reuse their parent's tracks
  • Arrange card titles, bodies and buttons in shared rows across a row of cards
  • Combine subgrid with gap overrides and named lines

Before you start

On this page

A subgrid is a grid item that lays its own children out in its parent’s tracks instead of making tracks of its own. Set grid-template-rows: subgrid on a card that spans three rows of the page’s grid, and the card’s title, text and button sit in those three shared rows, so every card’s title row is as tall as the tallest title and the texts and buttons line up across the whole row of cards. grid-template-columns: subgrid does the same for columns, for example to line labels and inputs up across separate groups of a form. Before subgrid, that alignment needed fixed heights or JavaScript that measured the cards. Subgrid is Baseline widely available (Chrome 117, Firefox 71 and Safari 16 were the first versions to support it). The cards, the gaps, an overflow and a form all come with their numbers from Chromium 153, Firefox 155 and WebKit 26.6.

The problem, measured

Three product cards in a row, each its own small grid of three rows (auto 1fr auto), with titles and texts of different lengths:

Three product cards, with and without subgrid HTML · cards.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Three product cards, with and without subgrid</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    h3, p { margin: 0; }
    .cards {
      display: grid;
      inline-size: 960px;
      grid-template-columns: repeat(3, 1fr);
      gap: 16px;
      margin-block-end: 32px;
    }
    .card { padding: 16px; background: #eef; }
    .card a { display: block; padding: 8px; background: #dde; text-align: center; }

    /* Each card is its own grid: its rows know nothing about the other cards. */
    #nested .card { display: grid; grid-template-rows: auto 1fr auto; row-gap: 8px; }

    /* Each card spans three rows of the parent and uses them for its own children. */
    #subgrid .card {
      grid-row: span 3;
      display: grid;
      grid-template-rows: subgrid;
      row-gap: 8px;                 /* the subgrid's own gap; without it, it would use the parent's 16px */
    }
  </style>
</head>
<body>
  <div class="cards" id="nested">
    <article class="card"><h3>Cotton kurta</h3><p>Hand-stitched, in five colours.</p><a href="#a">Add to cart</a></article>
    <article class="card"><h3>Handloom silk saree with a zari border</h3><p>Woven in Kanchipuram. Comes with a matching blouse piece and a gift box.</p><a href="#b">Add to cart</a></article>
    <article class="card"><h3>Block-printed dupatta</h3><p>Natural dyes.</p><a href="#c">Add to cart</a></article>
  </div>
  <div class="cards" id="subgrid">
    <article class="card"><h3>Cotton kurta</h3><p>Hand-stitched, in five colours.</p><a href="#a">Add to cart</a></article>
    <article class="card"><h3>Handloom silk saree with a zari border</h3><p>Woven in Kanchipuram. Comes with a matching blouse piece and a gift box.</p><a href="#b">Add to cart</a></article>
    <article class="card"><h3>Block-printed dupatta</h3><p>Natural dyes.</p><a href="#c">Add to cart</a></article>
  </div>
  <script>
    // For each card: how far below the card's top edge its title, text and button start, and how tall each is.
    const round = (n) => Math.round(n * 100) / 100;
    for (const grid of document.querySelectorAll('.cards')) {
      console.log(`${grid.id}: rows ${getComputedStyle(grid).gridTemplateRows}`);
      for (const card of grid.children) {
        const top = card.getBoundingClientRect().top;
        const parts = [...card.children].map((el) => {
          const r = el.getBoundingClientRect();
          return `${el.tagName.toLowerCase()} at ${round(r.top - top)} (${round(r.height)} tall)`;
        });
        console.log(`  ${card.querySelector('h3').textContent.split(' ')[0]}…: ${parts.join(', ')}; card ${round(card.getBoundingClientRect().height)}px`);
      }
    }
  </script>
</body>
</html>

The first grid of the example is the usual approach. How far below each card’s top edge its parts started, in all three engines:

Card Title Text Button
Kurta 16 px 48 px 128 px
Saree 16 px 72 px 128 px
Dupatta 16 px 48 px 128 px

The buttons line up, because the cards stretch to the height of the tallest one and the 1fr row takes the extra space. The texts do not: the saree’s two-line title pushes its text 24 px lower than its neighbours’, because each card sizes its rows only from its own content. A design that lines prices or ratings up across cards has the same problem with every part except the last.

Sharing the parent’s rows with subgrid

In the second grid of the example, each card does three things: it spans three rows of the parent (grid-row: span 3), it is a grid (display: grid), and it takes its rows from the parent (grid-template-rows: subgrid) instead of auto 1fr auto. It also sets its own row-gap: 8px, because a subgrid otherwise uses its parent’s gaps, 16 px here.

Three cards as separate grids, whose texts start at different heights, then as subgrids on shared rows, where they all line up.Three product cards, drawn to scaleEach card its own gridtitletextbuttontitletextbuttontitletextbuttonSubgrid: the cards share the parent rowstitletextbuttontitletextbuttontitletextbuttonThe dashed lines mark where the texts and buttonsstart. Alone, the texts start at 48, 72 and 48 px;on shared rows, all three start at 72 px.

Three product cards as separate grids and as subgrids

Text description of the diagram

Two rows of three product cards, drawn to scale from the lesson's measurements. Each card has a title, a text and a button.

  1. In the first row each card is its own grid. The first and third cards have one-line titles, so their texts start 48 px below the top of the card, while the middle card's two-line title pushes its text down to 72 px. A dashed line at 72 px shows that the texts do not line up. The buttons do line up, at 128 px, because the cards stretch to the same height and the text row takes the extra space.
  2. In the second row the cards use subgrid and share the parent's rows. Every title row is as tall as the tallest title, so all three texts start at 72 px and all three buttons at 128 px, along two dashed lines.
  3. A note under the cards repeats the numbers: alone, the texts start at 48, 72 and 48 px; on shared rows, all three start at 72 px.

All three engines then started every title at 16 px, every text at 72 px and every button at 128 px, and reported the parent’s rows as 60px 40px 52px. Those numbers come from three rules: each shared row is as tall as the tallest section in it; the card’s padding counts as extra margin on the sections at its top and bottom edges; and half the difference between the card’s gap and the parent’s, (8 − 16) ÷ 2 = −4 px, is added as margin on the sides that face another section. A program that applies them:

The rows and offsets, without and with subgrid JavaScript · shared_rows.mjs
// Where the title, text and button of each product card start, without and with subgrid (CSS Grid Level 2,
// "Subgrids"). The heights are the content heights the browsers measured: 24px per line of text, 40px buttons.
// Each card has 16px of padding; the parent's gap is 16px and the card's own row-gap 8px.

const cards = [
  { name: 'Cotton kurta', title: 24, text: 24, button: 40 },
  { name: 'Handloom silk saree', title: 48, text: 48, button: 40 },
  { name: 'Block-printed dupatta', title: 24, text: 24, button: 40 },
];
const padding = 16, parentGap = 16, cardGap = 8;

// Without subgrid: each card is a grid of auto, 1fr and auto rows, stretched to the height of the tallest card.
const natural = (c) => padding + c.title + cardGap + c.text + cardGap + c.button + padding;
const height = Math.max(...cards.map(natural));
console.log(`each card its own grid: every card stretched to ${height}px`);
for (const c of cards) {
  console.log(`  ${c.name.padEnd(22)} title at ${padding}, text at ${padding + c.title + cardGap}, button at ${height - padding - c.button}`);
}

// With subgrid: the parent's rows hold every card's sections. The card's padding counts as extra margin on the
// sections at its edges, and half the difference between the two gaps (8 - 16) / 2 = -4px as margin on the inside.
const half = (cardGap - parentGap) / 2;
const rows = [
  Math.max(...cards.map((c) => c.title)) + padding + half,
  Math.max(...cards.map((c) => c.text)) + 2 * half,
  Math.max(...cards.map((c) => c.button)) + half + padding,
];
const textTop = rows[0] + parentGap + half;
const buttonTop = rows[0] + parentGap + rows[1] + parentGap + half;
console.log(`subgrid: the parent's rows are ${rows.join(', ')}px, shared by every card`);
console.log(`  every card              title at ${padding}, text at ${textTop}, button at ${buttonTop}`);

Output

each card its own grid: every card stretched to 184px
  Cotton kurta           title at 16, text at 48, button at 128
  Handloom silk saree    title at 16, text at 72, button at 128
  Block-printed dupatta  title at 16, text at 48, button at 128
subgrid: the parent's rows are 60, 40, 52px, shared by every card
  every card              title at 16, text at 72, button at 128

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

The parent has no grid-template-rows at all: the browser creates three rows for each card it places, sized by the cards’ contents. With more cards than columns, the next row of cards gets three new rows of its own, so each row of cards lines up inside itself.

HTML, CSS & JS Playground (Online Editor) Paste the card example, then lengthen one title and watch every card's text move down with it.

How a subgrid behaves

A few rules from the specification decide what a subgrid can and cannot do:

  • It has no implicit tracks in the subgridded axis. If it has more children than the tracks it spans, the extra ones are placed in its last track, on top of whatever is there.
  • It uses its parent’s gaps unless it sets its own, and an override changes only the spacing of its own items.
  • Line names pass down. Named lines of the parent, and the lines its named areas create, can be used inside the subgrid, so a child can still be placed with grid-column: content. A subgrid can add names of its own after the keyword, as in grid-template-columns: subgrid [label] [field].
  • It has no track sizes of its own in that axis: the tracks, and their sizes, belong to the parent. The other axis can be an ordinary grid, or a subgrid too (grid-template: subgrid / subgrid).

The first rule is the one that breaks layouts later. The cards above were written with three parts; then a content editor adds a badge to each card:

A fourth child in a three-row subgrid HTML · extra-child.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>A fourth child in a three-row subgrid</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    h3, p { margin: 0; }
    .cards { display: grid; inline-size: 640px; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-block-end: 32px; }
    .card { display: grid; grid-template-rows: subgrid; row-gap: 8px; padding: 16px; background: #eef; }
    .card a { display: block; padding: 8px; background: #dde; text-align: center; }
    .badge { background: #fd8; }

    #three-rows .card { grid-row: span 3; }   /* written when the cards had three parts */
    #four-rows .card  { grid-row: span 4; }   /* one row for each part */
  </style>
</head>
<body>
  <div class="cards" id="three-rows">
    <article class="card"><h3>Cotton kurta</h3><p>Hand-stitched.</p><a href="#a">Add to cart</a><span class="badge">Bestseller</span></article>
    <article class="card"><h3>Block-printed dupatta</h3><p>Natural dyes.</p><a href="#b">Add to cart</a><span class="badge">New</span></article>
  </div>
  <div class="cards" id="four-rows">
    <article class="card"><h3>Cotton kurta</h3><p>Hand-stitched.</p><a href="#a">Add to cart</a><span class="badge">Bestseller</span></article>
    <article class="card"><h3>Block-printed dupatta</h3><p>Natural dyes.</p><a href="#b">Add to cart</a><span class="badge">New</span></article>
  </div>
  <script>
    const round = (n) => Math.round(n * 100) / 100;
    for (const grid of document.querySelectorAll('.cards')) {
      const card = grid.firstElementChild;
      const top = card.getBoundingClientRect().top;
      const parts = [...card.children].map((el) => {
        const r = el.getBoundingClientRect();
        return `${el.className || el.tagName.toLowerCase()} ${round(r.top - top)} to ${round(r.bottom - top)}`;
      });
      console.log(`${grid.id}: parent rows ${getComputedStyle(grid).gridTemplateRows}; first card: ${parts.join(', ')}`);
    }
  </script>
</body>
</html>
Card spans Button Badge
3 rows 80 to 120 px 80 to 120 px: on top of the button
4 rows 80 to 120 px 128 to 152 px: its own row

All three engines drew the badge exactly over the button in a three-row subgrid. The fix is to make the span match the number of parts, and to keep that number in one place. Setting it with a custom property, such as grid-row: span var(--parts, 3), lets a card with an extra part say so without editing the grid rules.

Columns: one label column across groups

Forms group their fields, and each group is usually its own grid, so each group’s label column fits only its own labels. Sharing the form’s columns lines every input up. Three versions of one form, each 560 px wide, with a max-content label column and a 1fr field column:

Labels and inputs aligned across groups HTML · form.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Labels and inputs aligned across groups</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    form { display: grid; inline-size: 560px; grid-template-columns: max-content 1fr; gap: 12px 16px; margin-block-end: 32px; }
    .group { grid-column: 1 / -1; display: grid; gap: inherit; margin: 0; }
    .group h3 { grid-column: 1 / -1; margin: 0; font-size: 1em; }
    input { font: inherit; }

    #separate .group  { grid-template-columns: max-content 1fr; }   /* each group its own two columns */
    #fieldsets .group { grid-template-columns: subgrid; }           /* fieldsets on the form's columns */
    #divs .group      { grid-template-columns: subgrid; }           /* role="group" divs on the form's columns */
  </style>
</head>
<body>
  <form id="separate">
    <fieldset class="group"><legend>You</legend>
      <label for="s-name">Name</label><input id="s-name" autocomplete="name">
      <label for="s-email">Email</label><input id="s-email" type="email" autocomplete="email">
    </fieldset>
    <fieldset class="group"><legend>Delivery</legend>
      <label for="s-address">Delivery address</label><input id="s-address" autocomplete="street-address">
      <label for="s-pin">PIN code</label><input id="s-pin" inputmode="numeric" autocomplete="postal-code">
    </fieldset>
  </form>
  <form id="fieldsets">
    <fieldset class="group"><legend>You</legend>
      <label for="f-name">Name</label><input id="f-name" autocomplete="name">
      <label for="f-email">Email</label><input id="f-email" type="email" autocomplete="email">
    </fieldset>
    <fieldset class="group"><legend>Delivery</legend>
      <label for="f-address">Delivery address</label><input id="f-address" autocomplete="street-address">
      <label for="f-pin">PIN code</label><input id="f-pin" inputmode="numeric" autocomplete="postal-code">
    </fieldset>
  </form>
  <form id="divs">
    <div class="group" role="group" aria-labelledby="d-you"><h3 id="d-you">You</h3>
      <label for="d-name">Name</label><input id="d-name" autocomplete="name">
      <label for="d-email">Email</label><input id="d-email" type="email" autocomplete="email">
    </div>
    <div class="group" role="group" aria-labelledby="d-delivery"><h3 id="d-delivery">Delivery</h3>
      <label for="d-address">Delivery address</label><input id="d-address" autocomplete="street-address">
      <label for="d-pin">PIN code</label><input id="d-pin" inputmode="numeric" autocomplete="postal-code">
    </div>
  </form>
  <script>
    // The left edge of every input, from the form's left edge, and whether each input sits beside its label.
    const round = (n) => Math.round(n * 100) / 100;
    for (const form of document.querySelectorAll('form')) {
      const left = form.getBoundingClientRect().left;
      const inputs = [...form.querySelectorAll('input')];
      const beside = inputs.every((i) => Math.abs(i.getBoundingClientRect().top - i.previousElementSibling.getBoundingClientRect().top) < 4);
      const edges = inputs.map((i) => `${i.id.slice(2)} ${round(i.getBoundingClientRect().left - left)}`);
      console.log(`${form.id}: inputs start at ${edges.join(', ')}; ${beside ? 'each beside its label' : 'stacked under their labels'}`);
    }
  </script>
</body>
</html>

Where the inputs started, measured from the form’s left edge:

Version Chromium 153 Firefox and WebKit
Fieldsets, own grids 72.69 and 149.16 px the same, to 0.01 px
Fieldsets, subgrid stacked in one column all at 149.16 px
Groups, subgrid all at 135.16 px the same, to 0.01 px

The groups in the last row are div elements with role="group".

Separate grids left the first group’s inputs 76 px to the left of the second group’s. As subgrids, the fieldsets lined every input up in Firefox and WebKit, but Chromium 153 did not treat the fieldset as a subgrid: its labels and inputs fell into a single column. Browsers lay a fieldset out with an anonymous inner box, and that is the kind of special case where engines still differ. The div version worked in all three: a div with role="group" and aria-labelledby pointing to its heading gives screen readers the group and its name, as a fieldset and its legend would. If you keep fieldsets, a fixed label column on each one, such as grid-template-columns: 10rem 1fr, aligns the inputs without a subgrid.

Support and fallbacks

Subgrid works in every current engine, so most sites can use it without a fallback. A site that must also serve older browsers can test for it with a feature query, @supports (grid-template-rows: subgrid), and keep a simpler layout for the others: each card a flex column with margin-block-start: auto on its button. That fallback lines up the buttons but not the texts, which is acceptable as a lesser layout. Fixed heights are the fallback to avoid: they break as soon as a text is longer than expected, after a translation or at a larger font size, and the text spills out of its box.

Practise: predict the shared rows

Exercise · Medium · JavaScript

Predict the shared rows of subgrid cards

A row of product cards sits in a grid. Each card spans one parent row per section (title, price, details, button) and sets grid-template-rows: subgrid, so its sections sit in the parent's rows. Each card has padding px of padding, the parent has a row gap of parentGap px and each card sets its own row-gap of cardGap px. Write three functions in cards.mjs and export them. All lengths are in px.

The first, sharedRows(cards, { padding, parentGap, cardGap }), returns the height of each parent row. cards is a list of cards, each a list of its sections' content heights, in order. A row is as tall as the tallest section in it, plus the extra margins the subgrid rules add: the card's padding on the side of a section that touches the card's top or bottom edge, and half the difference between the gaps, (cardGap - parentGap) / 2, on every side that faces another section. That half may be negative.

The second, sectionTops(rows, { padding, parentGap, cardGap }), returns how far below a card's top edge each section starts. The first section starts after the padding; every other section starts where its row starts, plus the same half difference.

The third, landingRows(children, span), returns the row (1, 2, 3 …) that each of a card's children auto-placed children lands in when the card spans span rows. A subgrid adds no rows of its own, so children that do not fit are all placed in its last row.

The sample tests import the three functions from cards.mjs and run in your browser.

Starter code · cards.mjs

/** The height of each parent row that the cards' sections share. */
export function sharedRows(cards, { padding, parentGap, cardGap }) {
  // Replace this line with your code.
  return cards[0];
}

/** How far below a card's top edge each section starts. */
export function sectionTops(rows, { padding, parentGap, cardGap }) {
  // Replace this line with your code.
  return rows.map(() => padding);
}

/** The row each of a card's children lands in when the card spans `span` rows. */
export function landingRows(children, span) {
  // Replace this line with your code.
  return Array.from({ length: children }, (_, i) => i + 1);
}
The sample tests · cards.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { sharedRows, sectionTops, landingRows } from './cards.mjs';

const lesson = { padding: 16, parentGap: 16, cardGap: 8 };

test('the lesson cards: rows of 60, 40 and 52px', () => {
  const cards = [[24, 24, 40], [48, 48, 40], [24, 24, 40]];
  assert.deepEqual(sharedRows(cards, lesson), [60, 40, 52]);
  assert.deepEqual(sectionTops([60, 40, 52], lesson), [16, 72, 128]);
});

test('four sections in four cards', () => {
  const cards = [[24, 32, 48, 40], [48, 32, 96, 40], [24, 32, 72, 40], [72, 32, 24, 40]];
  const rows = sharedRows(cards, lesson);
  assert.deepEqual(rows, [84, 24, 88, 52]);
  assert.deepEqual(sectionTops(rows, lesson), [16, 96, 136, 240]);
});

test('the same gap inside and outside adds nothing', () => {
  const same = { padding: 12, parentGap: 16, cardGap: 16 };
  const rows = sharedRows([[24, 24, 40], [48, 24, 40]], same);
  assert.deepEqual(rows, [60, 24, 52]);
  assert.deepEqual(sectionTops(rows, same), [12, 76, 116]);
});

test('a wider gap inside the card than outside', () => {
  const wide = { padding: 16, parentGap: 8, cardGap: 24 };
  assert.deepEqual(sharedRows([[24, 24, 40]], wide), [48, 40, 64]);
  assert.deepEqual(sectionTops([48, 40, 64], wide), [16, 64, 112]);
});

test('a card of one section gets both paddings', () => {
  assert.deepEqual(sharedRows([[30], [50]], lesson), [82]);
  assert.deepEqual(sectionTops([82], lesson), [16]);
});

test('children beyond the span land in the last row', () => {
  assert.deepEqual(landingRows(4, 3), [1, 2, 3, 3]);
  assert.deepEqual(landingRows(3, 3), [1, 2, 3]);
  assert.deepEqual(landingRows(5, 2), [1, 2, 2, 2, 2]);
  assert.deepEqual(landingRows(2, 4), [1, 2]);
});
A hint

With n sections, row i gets padding on its top side when i is 0 and half otherwise, and padding on its bottom side when i is n - 1 and half otherwise. Row i starts at the sum of the rows before it plus i parent gaps.

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 is a subgrid, and how is it different from a nested grid?

A nested grid is a grid item that is also a grid container with tracks of its own, sized only by its own children, so two cards side by side cannot line their insides up. A subgrid is a grid item that sets grid-template-rows: subgrid or grid-template-columns: subgrid, or both: it has no tracks of its own in that axis and lays its children out in the parent tracks it spans. Its children then take part in sizing those parent tracks, so a long title in one card makes the title row taller for every card. A subgrid keeps its own box, padding and background, inherits the parent’s gaps unless it overrides them, sees the parent’s line names, and adds no implicit tracks, so it must span as many tracks as it has children in that axis.

Key takeaways

  • grid-template-rows: subgrid on an item that spans parent rows puts its children in those rows, and their content sizes them, so sections line up across cards.
  • Span exactly as many tracks as the subgrid has children: extra children land in the last track, on top of others.
  • A subgrid uses its parent’s gaps unless it sets its own; half the difference is applied as margin inside it.
  • The subgrid’s padding counts as extra margin on its edge items, which is why the measured rows were 60, 40 and 52 px.
  • Line names of the parent pass down, and a subgrid can add its own after the subgrid keyword.
  • In the lesson’s run, Chromium 153 did not make a fieldset a subgrid; a div with role="group" worked in all three engines.
  • Subgrid is Baseline widely available; a flex-column fallback inside @supports loses only the alignment.

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 A card is a grid item that spans three rows of its parent. What does grid-template-rows: subgrid on the card do?

    Choose one answer.

    Show the answer to question 1

    Answer: The card's children are placed in the three parent rows it spans, and those rows are sized by every card's content

    A subgrid has no track sizes of its own in that axis: it lays its children out in the parent's tracks, and their content takes part in sizing those tracks. That is why a long title in one card makes the title row taller for every card in the row. The children stay inside the card, which keeps its own padding and background.

  2. Question 2 of 6 A card spans three parent rows with a subgrid. Someone adds a fourth child, a badge. Where does it go?

    Choose one answer.

    Show the answer to question 2

    Answer: Into the third row, on top of the button that is already there

    A subgrid has no implicit tracks in its subgridded axis, so items that do not fit are clamped into its last track. In the lesson's run the badge sat from 80 to 120 px, exactly over the button, in all three engines. Making the card span four rows fixed it.

  3. Question 3 of 6 Cards with padding: 16px and row-gap: 8px are subgrids of a parent with row-gap: 16px. The tallest title is 48px. How tall is the parent's first row?

    Type a number.

    Show the answer to question 3

    Answer: 60 px

    The title touches the card's top edge, so the card's 16 px padding counts as extra margin above it. Below it, the subgrid adds half the difference between the gaps, (8 − 16) ÷ 2 = −4 px. 48 + 16 − 4 = 60 px, which is what all three engines reported. The texts then start 72 px below the top of every card.

  4. Question 4 of 6 What happens to the gaps when a subgrid sets its own row-gap?

    Choose one answer.

    Show the answer to question 4

    Answer: Its children are spaced by its own gap, while the parent's tracks and gaps stay where they are

    A subgrid uses its parent's gaps unless it sets its own. When it does, half the difference between the two gaps is applied as extra (here negative) margin on the inner sides of its items, so they sit 8 px apart, centred on the parent's 16 px gaps.

  5. Question 5 of 6 In the lesson's run, which group of form fields could share the form's two columns as a subgrid in Chromium 153, Firefox 155 and WebKit 26.6?

    Choose one answer.

    Show the answer to question 5

    Answer: A div with role="group" and aria-labelledby

    Firefox and WebKit treated the fieldset as a subgrid and lined all inputs up at 149 px. Chromium 153 did not: its labels and inputs stacked in one column. A div with role="group", labelled by a heading, worked in all three engines and keeps the group's name for screen readers.

  6. Question 6 of 6 Which approach lines up the titles, texts and buttons across cards without fixed heights or JavaScript?

    Choose one answer.

    Show the answer to question 6

    Answer: Each card spans one parent row per section and uses grid-template-rows subgrid

    A flex column with an auto margin, or a grid of auto, 1fr and auto inside each card, lines up the buttons but not the texts, because each card sizes its own title. In the lesson's run the texts started at 48, 72 and 48 px that way, and at 72 px in every card with subgrid. display contents removes the card's box, its padding and its background along with it.

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.