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 10 – Flexbox

Flex alignment, auto margins and order

Align one flex item with align-self and wrapped lines with align-content, place items with auto margins, and reorder visually without breaking focus order.

  • 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 align-self for single items and align-content for wrapped lines
  • Use auto margins to place and separate items precisely
  • Change the visual order without breaking keyboard and reading order

Before you start

What you’ll learn · Before you start

What you will learn

  • Use align-self for single items and align-content for wrapped lines
  • Use auto margins to place and separate items precisely
  • Change the visual order without breaking keyboard and reading order

Before you start

On this page

Flexbox aligns along both of its axes. Across the line, align-items gives every item its default position, align-self changes it for one item, and align-content moves the lines themselves when a container has wrapped onto more than one. Along the line, auto margins take the free space before justify-content does, so a single margin-inline-start: auto pushes an item to the end, and auto margins on both sides centre it. The order property and the -reverse directions change where items are drawn, but not the order in which a keyboard or a screen reader reaches them, so a careless reorder can fail two WCAG 2.2 success criteria, 1.3.2 and 2.4.3. This lesson measures what Chromium 153, Firefox 155 and WebKit 26.6 did with each case, down to where the Tab key moved focus.

One item out of line: align-self

align-self takes the same values as align-items, but on a single item, where it overrides the container’s setting. Its default, auto, means “do what align-items says”. In a two-line container where every item stretches to its 100 px line, one item with align-self: flex-end kept its own 24 px height and sat at the bottom of its line, 76 px down, in all three engines. A common use is a button in a column layout: the items of a column stretch across its width by default, and align-self: flex-start keeps the button as wide as its text.

Lines, not items: align-content

align-content works one level up. It distributes the space between the lines of a multi-line container, with the same values as justify-content plus stretch, while align-items and align-self place each item inside its own line. A 300 by 200 px container with items 120 px wide and 24 px tall (as tall as their text); the last word in the first column is the align-content value:

align-content on one line and on two HTML · align-content.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>align-content moves lines, align-items moves items</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .box { display: flex; inline-size: 300px; block-size: 200px; margin-block-end: 8px; background: #eee; }
    .box > div { inline-size: 120px; background: #e6edfc; }   /* no height: 24px of text, unless stretched */
    .wrap { flex-wrap: wrap; }

    #nowrap-center { align-content: center; }           /* one line that can't wrap: align-content does nothing */
    #one-line-center { align-content: center; }         /* can wrap, has one line: the line is centred */
    #two-lines-center { align-content: center; }
    #two-lines-between { align-content: space-between; }
    #two-lines-self .last { align-self: flex-end; }     /* one item, at the bottom of its own line */
  </style>
</head>
<body>
  <div class="box" id="nowrap-center"><div>1</div><div>2</div></div>
  <div class="box wrap" id="one-line-normal"><div>1</div><div>2</div></div>
  <div class="box wrap" id="one-line-center"><div>1</div><div>2</div></div>
  <div class="box wrap" id="two-lines-normal"><div>1</div><div>2</div><div>3</div><div>4</div></div>
  <div class="box wrap" id="two-lines-center"><div>1</div><div>2</div><div>3</div><div>4</div></div>
  <div class="box wrap" id="two-lines-between"><div>1</div><div>2</div><div>3</div><div>4</div></div>
  <div class="box wrap" id="two-lines-self"><div>1</div><div class="last">2</div><div>3</div><div>4</div></div>
  <script>
    for (const box of document.querySelectorAll('.box')) {
      const top = box.getBoundingClientRect().y;
      const items = [...box.children].map((d) => {
        const r = d.getBoundingClientRect();
        return `${d.textContent}: y ${r.y - top}, ${r.height} tall`;
      });
      console.log(`${box.id} | ${items.join(' | ')}`);
    }
  </script>
</body>
</html>
Container Lines Items
nowrap, center one of 200 px 200 px tall
wrap, one line, normal one of 200 px 200 px tall
wrap, one line, center one of 24 px, at 88 24 px tall
wrap, two lines, normal two of 100 px 100 px tall
wrap, two lines, center two of 24 px, at 76 and 100 24 px tall
wrap, two lines, space-between at 0 and 176 24 px tall

All three engines gave these numbers. The first two rows hold the classic trap: align-content has no effect on a container that cannot wrap, because its only line is always as tall as the container. Once flex-wrap: wrap is set, it works even while everything still fits on one line. The default, normal, behaves like stretch: the lines share the spare height and stretchy items grow with them, which is why two wrapped lines of short items suddenly look 100 px tall.

With align-content: normal two lines share 200px and items stretch; with center, two 24px lines sit in the middle.Two lines in a 300 × 200 containeralign-content: normal1234each line is100px tall;the itemsstretch to filltheir linealign-content: center123476px freetwo 24pxlines, packedin the middle76px freealign-content moves the lines; align-items andalign-self place each item inside its own line.

align-content moves lines, align-items moves items

Text description of the diagram

The diagram shows a wrapping flex container 300px wide and 200px tall with four items, 120px wide and 24px tall when nothing stretches them, on two lines. It is drawn at 80 % scale.

  1. With align-content: normal, which behaves as stretch, the two lines share the container's height, so each line is 100px tall, and the items, which have align-items: stretch by default, fill their line and become 100px tall.
  2. With align-content: center, each line is only as tall as its items, 24px, and the two lines are packed together in the middle, with 76px of free space above them and 76px below.

align-content moves and sizes the lines; align-items and align-self place each item inside its own line.

Text on one baseline

align-items: baseline lines up the first line of text in each item, whatever the font sizes. A section heading in 32 px type with a link in 16 px type beside it, first centred and then on the baseline:

A heading and a link on one baseline HTML · baseline.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>A heading and a link on one baseline</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .section-head { display: flex; gap: 1rem; margin-block-end: 16px; }
    .section-head h2 { margin: 0; font-size: 2rem; line-height: 1.25; }
    .section-head a { margin-inline-start: auto; }
    #centred { align-items: center; }
    #baseline { align-items: baseline; }
    .mark { display: inline-block; inline-size: 0; block-size: 0; }   /* sits on the text's baseline */
  </style>
</head>
<body>
  <div class="section-head" id="centred"><h2><span class="mark"></span>Plans</h2><a href="#compare"><span class="mark"></span>Compare all plans</a></div>
  <div class="section-head" id="baseline"><h2><span class="mark"></span>Plans</h2><a href="#compare"><span class="mark"></span>Compare all plans</a></div>
  <script>
    // An empty inline-block's bottom edge is the baseline of its line, so it shows where the text sits.
    for (const head of document.querySelectorAll('.section-head')) {
      const top = head.getBoundingClientRect().y;
      const [h2, link] = [...head.querySelectorAll('.mark')].map((m) => +(m.getBoundingClientRect().bottom - top).toFixed(2));
      console.log(`${head.id}: heading baseline at ${h2}px, link baseline at ${link}px, ${+(h2 - link).toFixed(2)}px apart`);
    }
  </script>
</body>
</html>

Centred, the heading’s text sat on a baseline 31 px down and the link’s 25 px down in Chromium, 6 px apart (5.5 px in Firefox and WebKit), so the link looked as if it were floating. With align-items: baseline both baselines were at 31 px (31.09 px in WebKit). last baseline aligns the last lines of text instead, for example a button that should sit on the last line of the paragraph beside it; it is Baseline widely available, from Chrome 108, Firefox 52 and Safari 16.2. Items aligned on a baseline are not stretched.

Auto margins: the free space goes to them first

An auto margin on a flex item absorbs free space along its axis, and it does so before justify-content distributes anything. If one auto margin on a line takes all the free space, justify-content has nothing left to share. Three 80 px items in a 400 px row, 160 px of free space. “Start of c” means margin-inline-start: auto on c, and the second row adds justify-content: center:

Auto margins, and what happens when an item overflows HTML · auto-margins.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Auto margins and overflow</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .box { display: flex; inline-size: 400px; block-size: 120px; margin-block-end: 8px; background: #eee; }
    .box > div { flex: none; inline-size: 80px; block-size: 40px; background: #e6edfc; }

    #push-last .c { margin-inline-start: auto; }
    #push-last-centre { justify-content: center; }      /* has nothing left to share */
    #push-last-centre .c { margin-inline-start: auto; }
    #both-sides .b { margin-inline: auto; }
    #split .a { margin-inline-end: auto; }
    #split .c { margin-inline-start: auto; }
    #centre-both-axes > div { margin: auto; }

    /* An item wider than its 200px container, in a container that scrolls. */
    .narrow { inline-size: 200px; overflow-x: auto; }
    .narrow > div { inline-size: 300px; }
    #overflow-center { justify-content: center; }
    #overflow-safe { justify-content: safe center; }
    #overflow-margins > div { margin-inline: auto; }
    #overflow-around { justify-content: space-around; }
  </style>
</head>
<body>
  <div class="box" id="push-last"><div class="a">a</div><div class="b">b</div><div class="c">c</div></div>
  <div class="box" id="push-last-centre"><div class="a">a</div><div class="b">b</div><div class="c">c</div></div>
  <div class="box" id="both-sides"><div class="a">a</div><div class="b">b</div><div class="c">c</div></div>
  <div class="box" id="split"><div class="a">a</div><div class="b">b</div><div class="c">c</div></div>
  <div class="box" id="centre-both-axes"><div>a</div></div>
  <div class="box narrow" id="overflow-center"><div>wide</div></div>
  <div class="box narrow" id="overflow-safe"><div>wide</div></div>
  <div class="box narrow" id="overflow-margins"><div>wide</div></div>
  <div class="box narrow" id="overflow-around"><div>wide</div></div>
  <script>
    for (const box of document.querySelectorAll('.box')) {
      const b = box.getBoundingClientRect();
      const items = [...box.children].map((d) => {
        const r = d.getBoundingClientRect();
        return `${d.textContent} at x ${+(r.x - b.x).toFixed(2)}, y ${+(r.y - b.y).toFixed(2)}`;
      });
      const scroll = box.classList.contains('narrow') ? `; scrolls over ${box.scrollWidth}px` : '';
      console.log(`${box.id}: ${items.join(', ')}${scroll}`);
    }
  </script>
</body>
</html>
Auto margins a b c
start of c 0 80 320
start of c, center 0 80 320
both sides of b 0 160 320
end of a, start of c 0 160 320

The x positions were identical in all three engines. Two auto margins share the free space equally, 80 px each, whether they belong to one item or to two. They work on the cross axis too: a single item with margin: auto in a 400 by 120 px container sat at x 160 and y 40, centred in both directions without any alignment property.

Pinning a button to the bottom of a card

Cards in a row stretch to the height of the tallest, but their contents do not: a short description leaves its button halfway up the card. Make each card a column flex container, and give the button an auto margin on top:

Buttons on the bottom edge CSS · card-footer.css
/* Cards of equal height in a row, each with its button on the bottom edge however long its text is. */
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.card {
  display: flex;
  flex-direction: column;     /* the card's own main axis is vertical */
  flex: 1 1 12rem;
  padding: 1rem;
}

.card p {
  margin-block: 0 1rem;
}

.card .button {
  margin-block-start: auto;   /* takes all the free space above the button */
  align-self: flex-start;     /* as wide as its text, not the whole card */
}
Three cards with and without the auto margin HTML · card-footer.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Buttons on the bottom edge of every card</title>
  <link rel="stylesheet" href="card-footer.css">
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .card { background: #e6edfc; }
    .cards { margin-block-end: 16px; }
    #no-auto .button { margin-block-start: 0; }   /* the same cards without the auto margin */
  </style>
</head>
<body>
  <div class="cards" id="auto">
    <article class="card"><h3>Starter</h3><p>For one person.</p><a class="button" href="#starter">Choose Starter</a></article>
    <article class="card"><h3>Team</h3><p>For up to ten people, with shared folders, comments on every file and a history of each change.</p><a class="button" href="#team">Choose Team</a></article>
    <article class="card"><h3>School</h3><p>For classes, with a teacher's view.</p><a class="button" href="#school">Choose School</a></article>
  </div>
  <div class="cards" id="no-auto">
    <article class="card"><h3>Starter</h3><p>For one person.</p><a class="button" href="#starter">Choose Starter</a></article>
    <article class="card"><h3>Team</h3><p>For up to ten people, with shared folders, comments on every file and a history of each change.</p><a class="button" href="#team">Choose Team</a></article>
    <article class="card"><h3>School</h3><p>For classes, with a teacher's view.</p><a class="button" href="#school">Choose School</a></article>
  </div>
  <script>
    for (const cards of document.querySelectorAll('.cards')) {
      const top = cards.getBoundingClientRect().y;
      const rows = [...cards.children].map((card) => {
        const c = card.getBoundingClientRect();
        const b = card.querySelector('.button').getBoundingClientRect();
        return `${card.querySelector('h3').textContent}: card ${+c.height.toFixed(2)}px tall, button bottom ${+(b.bottom - top).toFixed(2)}`;
      });
      console.log(`${cards.id} at ${innerWidth}px | ${rows.join(' | ')}`);
    }
  </script>
</body>
</html>

At 1024 px the three cards were all 205.44 px tall in Chromium and WebKit (205.43 px in Firefox), and with the auto margin every button’s bottom edge was at 189.44 px, the card’s 16 px of padding above its bottom edge. Without it, the buttons of the two short cards ended at 141.44 px, 48 px higher than the Team card’s. At 360 px the cards stack, one per line, each as tall as its own content, so the two versions look the same; the auto margin only does work when there is spare height.

When items overflow: safe and unsafe centring

Centring has a dangerous side. When an item is larger than its container, center keeps it centred, so it hangs over both edges, and the part over the start edge may be impossible to reach. A 300 px item in a 200 px container that scrolls, aligned four ways (three values of justify-content, and auto margins on the item):

Alignment Starts at Start reachable
center −50 px no (WebKit: yes)
safe center 0 yes
margin-inline: auto 0 yes
space-around 0 yes

With plain center, Chromium and Firefox could not scroll to the first 50 px of the item at all; WebKit let the scroll position go to −50 px and showed them.

The safe keyword means “centre, unless that would push content past the start edge; then start”. It is Baseline newly available for flexbox: Chrome 115, Firefox 63 and Safari 17.6 support it. Auto margins have always behaved this way, because they only take positive free space, so they are the safe centring that every browser handles. The last row shows the specification’s newer rule: when items overflow, space-between falls back to safe flex-start, and space-around and space-evenly to safe center, and all three engines followed it. The real-world version of this bug is a dialog centred in a full-screen flex container: on a short phone screen in landscape, its top, often with the title and the close button, ends up above the screen.

Visual order and keyboard order

The order property takes an integer, 0 by default, and the container draws its items sorted by it, keeping the HTML order between items with equal values. flex-direction: row-reverse draws them from the end of the line instead. Neither changes the order in which the Tab key moves focus, or in which a screen reader reads, and that is easy to measure:

Three toolbars, three kinds of reordering HTML · order.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Visual order and keyboard order</title>
  <style>
    body { margin: 0; font: 16px/24px Arial, sans-serif; }
    .toolbar { display: flex; gap: 8px; margin-block-end: 8px; }
    #moved .help { order: -1; }                 /* drawn first, still last in the HTML */
    #reversed { flex-direction: row-reverse; }  /* drawn from right to left */
    #reading-flow { reading-flow: flex-visual; } /* limited availability: Chromium only */
    #reading-flow .help { order: -1; }
  </style>
</head>
<body>
  <div class="toolbar" id="moved"><button type="button">New</button><button type="button">Open</button><button type="button">Save</button><button type="button" class="help">Help</button></div>
  <div class="toolbar" id="reversed"><button type="button">New</button><button type="button">Open</button><button type="button">Save</button><button type="button" class="help">Help</button></div>
  <div class="toolbar" id="reading-flow"><button type="button">New</button><button type="button">Open</button><button type="button">Save</button><button type="button" class="help">Help</button></div>
  <script>
    // The order a sighted reader sees: left to right on the screen.
    for (const bar of document.querySelectorAll('.toolbar')) {
      const seen = [...bar.children].sort((a, b) => a.getBoundingClientRect().x - b.getBoundingClientRect().x);
      console.log(`${bar.id}: on screen ${seen.map((b) => b.textContent).join(', ')}`);
    }
  </script>
</body>
</html>

Pressing Tab through the page, one button at a time (Option+Tab in WebKit: in Safari, plain Tab reaches only fields and pop-up menus until a setting is changed, as Apple’s guide to its keyboard shortcuts says):

Toolbar On screen Keyboard
order: -1 on Help Help, New, Open, Save New, Open, Save, Help
row-reverse Help, Save, Open, New New, Open, Save, Help
order: -1 and reading-flow Help, New, Open, Save Help first only in Chromium

The first two keyboard orders were the same in all three engines. With reading-flow: flex-visual, Chromium followed the drawn order, Help, New, Open, Save, while Firefox and WebKit kept the HTML order. In the first two toolbars a keyboard user meets Help last although it is drawn first, and in the reversed toolbar the focus ring travels from right to left. WCAG 2.2 has two Level A success criteria for this. 1.3.2, Meaningful Sequence, asks that when the order of content matters for its meaning, the correct reading order can be determined from the code, and 2.4.3, Focus Order, asks that keyboard focus moves in an order that keeps the page’s meaning and operation. The flexbox specification itself says authors must not use order or the -reverse values in place of a correct source order.

The reading-flow property of CSS Display Level 4 can make focus follow the drawn order, as the third toolbar shows, but it has limited availability: only Chrome and Edge 137 and later support it, and in Firefox and WebKit the toolbar behaved like the first one. The check is simple enough to automate:

Drawn order against keyboard order JavaScript · order_check.mjs
// The order a flex container draws its items in is the "order-modified document order" of CSS Display Level 3: the
// items sorted by their order value, with ties kept in HTML order. row-reverse then lays that list out from the end
// of the line. Focus and screen readers keep to the HTML order, so this program flags every toolbar whose drawn order
// differs. The toolbars are the first two of order.html; Chromium 153, Firefox 155 and WebKit 26.6 drew them the same.

function drawnOrder(names, { order = {}, reverse = false } = {}) {
  const sorted = names
    .map((name, html) => ({ name, html, order: order[name] ?? 0 }))
    .sort((a, b) => a.order - b.order || a.html - b.html)
    .map((item) => item.name);
  return reverse ? sorted.reverse() : sorted;   // left to right on screen
}

const html = ['New', 'Open', 'Save', 'Help'];
const toolbars = [
  { id: 'moved', css: '.help { order: -1 }', order: { Help: -1 } },
  { id: 'reversed', css: 'flex-direction: row-reverse', reverse: true },
  { id: 'fixed', css: 'no order: Help moved first in the HTML', html: ['Help', 'New', 'Open', 'Save'] },
];

for (const bar of toolbars) {
  const source = bar.html ?? html;
  const screen = drawnOrder(source, bar);
  const same = screen.join() === source.join();
  console.log(`${bar.id} (${bar.css})`);
  console.log(`  on screen: ${screen.join(', ')}`);
  console.log(`  keyboard:  ${source.join(', ')}`);
  console.log(`  ${same ? 'the same order: nothing to fix' : `differs: ${screen[0]} is drawn first but reached ${source.indexOf(screen[0]) + 1}${['st', 'nd', 'rd'][source.indexOf(screen[0])] ?? 'th'} by the keyboard`}`);
}

Output

moved (.help { order: -1 })
  on screen: Help, New, Open, Save
  keyboard:  New, Open, Save, Help
  differs: Help is drawn first but reached 4th by the keyboard
reversed (flex-direction: row-reverse)
  on screen: Help, Save, Open, New
  keyboard:  New, Open, Save, Help
  differs: Help is drawn first but reached 4th by the keyboard
fixed (no order: Help moved first in the HTML)
  on screen: Help, New, Open, Save
  keyboard:  Help, New, Open, Save
  the same order: nothing to fix

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

The fix that works everywhere is the one in the third case of the program: put the items in the HTML in the order people should meet them, and keep order and -reverse for changes that carry no meaning, such as which side of a decorative image a caption sits. WCAG’s technique C27 describes exactly this: make the order of the DOM match the visual order.

CSS Flexbox Generator & Playground Try align-self, align-content, order and auto margins on live items, with wrapping switched on and off.

Practise: place items with auto margins and justify-content

Exercise · Medium · JavaScript

Place items on a line with auto margins and justify-content

Write place(lineSize, items, justify) in place.mjs and export it. It returns the start position (x, in px) of each item on one flex line lineSize px long, the way a browser places flex items along the main axis once their sizes are known. Each item is { size }, and may have autoStart: true or autoEnd: true for an auto margin on that side; every other margin is 0 and there is no gap. justify is one of flex-start (the default), center, flex-end, space-between, space-around, space-evenly and safe center.

1. The free space is lineSize minus the sizes of the items. 2. If the free space is positive and at least one auto margin exists, every auto margin gets an equal share of it, and justify has nothing left to distribute. 3. Otherwise auto margins are 0 and justify places the items: center puts half the free space before the first item, flex-end all of it; space-between shares it between the items, space-around gives each item half a share on both sides, and space-evenly makes every space, including the two ends, equal. 4. When the free space is negative (the items overflow), space-between, space-around and space-evenly start the items at 0, and so does safe center, while plain center lets them hang over both edges. With a single item, space-between starts it at 0.

The sample tests import place from place.mjs and run in your browser. Their numbers are the positions Chromium 153, Firefox 155 and WebKit 26.6 drew for the lesson's examples.

Starter code · place.mjs

/** The start position of each item on a flex line `lineSize` px long. */
export function place(lineSize, items, justify = 'flex-start') {
  // Replace this line with your code.
  return items.map(() => 0);
}
The sample tests · place.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { place } from './place.mjs';

const near = (actual, expected) => {
  assert.equal(actual.length, expected.length, `expected ${expected.length} positions, got ${actual.length}`);
  actual.forEach((x, i) => assert.ok(Math.abs(x - expected[i]) < 0.01, `item ${i + 1}: expected ${expected[i]}, got ${x}`));
};
const three = [{ size: 80 }, { size: 80 }, { size: 80 }];

test('justify-content shares the free space', () => {
  near(place(400, three), [0, 80, 160]);
  near(place(400, three, 'center'), [80, 160, 240]);
  near(place(400, three, 'flex-end'), [160, 240, 320]);
  near(place(400, three, 'space-between'), [0, 160, 320]);
  near(place(400, three, 'space-around'), [26.67, 160, 293.33]);
  near(place(400, three, 'space-evenly'), [40, 160, 280]);
});

test('an auto margin takes all the free space first', () => {
  const pushed = [{ size: 80 }, { size: 80 }, { size: 80, autoStart: true }];
  near(place(400, pushed), [0, 80, 320]);
  near(place(400, pushed, 'center'), [0, 80, 320]);
});

test('auto margins share the free space equally', () => {
  near(place(400, [{ size: 80 }, { size: 80, autoStart: true, autoEnd: true }, { size: 80 }]), [0, 160, 320]);
  near(place(400, [{ size: 80, autoEnd: true }, { size: 80 }, { size: 80, autoStart: true }]), [0, 160, 320]);
  near(place(400, [{ size: 80, autoStart: true, autoEnd: true }]), [160]);
});

test('an overflowing item: center hangs over the start, the safe forms do not', () => {
  const wide = [{ size: 300 }];
  near(place(200, wide, 'center'), [-50]);
  near(place(200, wide, 'safe center'), [0]);
  near(place(200, [{ size: 300, autoStart: true, autoEnd: true }]), [0]);
  near(place(200, wide, 'space-around'), [0]);
});

test('one item: space-between starts it, space-around centres it', () => {
  near(place(400, [{ size: 80 }], 'space-between'), [0]);
  near(place(400, [{ size: 80 }], 'space-around'), [160]);
});
A hint

Count the auto margins first. If there are any and the free space is positive, walk the items with a running x: add a share before an item that has autoStart, record x, add the item's size, then add a share after it if it has autoEnd. Otherwise work out two numbers from justify, the space before the first item and the extra space between items, and walk the items the same way.

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.

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 300px tall flex container has flex-wrap: nowrap and align-content: center. Its items have no height of their own. What happens?

    Choose one answer.

    Show the answer to question 1

    Answer: Nothing changes; the items stay stretched to 300px

    align-content moves lines, and a container that cannot wrap has a single line that always fills it, so the property has no effect and the items keep stretching. Add flex-wrap: wrap and the same declaration centres a 24px line, as the lesson measured.

  2. Question 2 of 6 Three 100px items sit in a 500px row with justify-content: center. The last item has margin-inline-start: auto. Where does the first item start?

    Type a number.

    Show the answer to question 2

    Answer: 0 px

    The 200px of free space go to the auto margin before justify-content distributes anything, so there is nothing left to centre: the items sit at 0, 100 and 400px.

  3. Question 3 of 6 A single 120px item with margin-inline: auto sits in a 360px flex row. Where does it start?

    Type a number.

    Show the answer to question 3

    Answer: 120 px

    The two auto margins share the 240px of free space equally, 120px each, so the item starts at 120px: it is centred without justify-content.

  4. Question 4 of 6 A 400px wide image that cannot shrink is centred with justify-content: center inside a 300px container that scrolls horizontally. What happens in Chromium?

    Choose one answer.

    Show the answer to question 4

    Answer: 50px of the image hang over the start edge and cannot be scrolled into view

    Unsafe centring hangs the extra 100px over both edges, 50px each. A scroll container can only scroll to the overflow at its end, so in Chromium and Firefox the first 50px are out of reach (WebKit lets you scroll there). justify-content: safe center or margin-inline: auto starts the image at the edge instead.

  5. Question 5 of 6 A "Help" button should be drawn first in a toolbar and be the first button keyboard users reach, in every browser. What do you do?

    Choose one answer.

    Show the answer to question 5

    Answer: Move the button to the start of the toolbar in the HTML

    order and row-reverse change only the drawing: in all three engines the keyboard still reached Help last. reading-flow fixes the keyboard order only in Chromium-based browsers. Changing the HTML order fixes the drawing, the keyboard and screen readers everywhere.

  6. Question 6 of 6 Which WCAG 2.2 success criterion asks that keyboard focus moves through a page in an order that keeps its meaning and operation?

    Choose one answer.

    Show the answer to question 6

    Answer: 2.4.3 Focus Order

    2.4.3 Focus Order is about the sequence of keyboard focus. 1.3.2 Meaningful Sequence is about the reading order that assistive technology can determine from the code; a reorder with order or -reverse can fail both.

Interview questions

Fresher · What is the difference between align-items and align-content?

Both work on the cross axis, at different levels. align-items places each item inside its own flex line: at the start, in the centre, at the end, on the text baseline, or stretched to the line’s height, which is the default; an item can override it with align-self. align-content moves and sizes the lines themselves, so it only matters when a container can wrap: on a nowrap container it does nothing, because the single line always fills the container. In a 200 px tall wrapping container with two lines of 24 px items, align-content: center packed both lines in the middle, 76 px from the top, while the default, normal, stretched each line to 100 px and the items with it.

Key takeaways

  • align-self overrides align-items for one item; align-content moves whole lines and does nothing unless the container has flex-wrap: wrap.
  • align-items: baseline puts text of different sizes on one baseline; centring left a 16 px link 6 px off a 32 px heading’s baseline.
  • Auto margins take free space before justify-content, so margin-inline-start: auto pushes an item to the end and margin-block-start: auto pins a card’s button to its bottom.
  • Plain center can hide the start of an overflowing item; safe center or auto margins keep it reachable.
  • order and -reverse change only the drawing: Tab order stayed in HTML order in all three engines. Reorder the HTML instead; reading-flow works only in Chromium-based browsers.

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.