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 12 – Responsive design: media and container queries

Container queries and container units

Make components respond to their container with container-type and @container, size text with cqi, and avoid self-queries, zero widths and stray containers.

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

What you will learn

  • Build components that respond to their container with container-type and @container
  • Size content with container query units such as cqi and cqb
  • Avoid the common traps of querying yourself, missing sizes and unnamed nested containers

Before you start

What you’ll learn · Before you start

What you will learn

  • Build components that respond to their container with container-type and @container
  • Size content with container query units such as cqi and cqb
  • Avoid the common traps of querying yourself, missing sizes and unnamed nested containers

Before you start

On this page

A container query styles an element by the size of a box around it instead of the size of the window. You mark an ancestor as a container with container-type: inline-size, then write @container (width >= 30rem) { … }, and the rules inside apply wherever that container is at least 30rem wide. The same card can then be stacked in a 280 px sidebar and laid out side by side in a 640 px column on the same page, which no media query can do, because both cards share one viewport. Container size queries and the container units such as cqi are Baseline widely available. This lesson measures one card in three places in Chromium 153, Firefox 155 and WebKit 26.6, then the three traps that catch most people: a container that queries itself, a container with no width of its own, and a query that reaches the wrong container.

One card, three places

Container queries need two things: a container, and rules that ask about it. container: card / inline-size names the slot card and makes its width queryable. The card’s rules ask about the slot through @container card (…):

A card that follows its slot CSS · card.css
/* The slot a card sits in is its query container: the card follows the slot's width, not the window's. */
.slot {
  container: card / inline-size;
}

.card {
  display: grid;
  gap: 0.75rem;
  padding: 1rem;
  border: 1px solid #c3c9d1;
  border-radius: 8px;
}

.card img {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.card h3 {
  margin: 0;
  font-size: clamp(1.125rem, 0.75rem + 3cqi, 2rem); /* 3 % of the slot's width, between 18 and 32 px */
}

/* The photo beside the text once the slot is 30rem wide */
@container card (width >= 30rem) {
  .card { grid-template-columns: 12rem 1fr; }
}

/* Photo, text and actions side by side from 60rem */
@container card (width >= 60rem) {
  .card { grid-template-columns: 16rem 1fr auto; }
}

The page places the same card in a 280 px sidebar, a 640 px main column and a 1100 px band, in one 1280 px window:

One card in three slots HTML · card.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>One card in three places</title>
  <link rel="stylesheet" href="card.css">
  <style>
    body { margin: 0; font: 16px/1.5 Arial, sans-serif; }
    .sidebar { inline-size: 280px; }
    .main { inline-size: 640px; }
    .band { inline-size: 1100px; }
  </style>
</head>
<body>
  <template id="card">
    <article class="card">
      <img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 3'%3E%3Crect width='4' height='3' fill='%23d9c7a7'/%3E%3C/svg%3E">
      <div><h3>Handloom cotton saree</h3><p>Soft, breathable and woven by hand.</p></div>
      <div><button type="button">Add to bag</button></div>
    </article>
  </template>
  <aside class="slot sidebar"></aside>
  <main class="slot main"></main>
  <section class="slot band"></section>
  <script>
    for (const slot of document.querySelectorAll('.slot')) {
      slot.append(document.getElementById('card').content.cloneNode(true));
      const card = slot.querySelector('.card');
      const columns = getComputedStyle(card).gridTemplateColumns.split(' ').length;
      const title = getComputedStyle(card.querySelector('h3')).fontSize;
      console.log(`${slot.classList[1]}: slot ${slot.getBoundingClientRect().width} px · ${columns} column(s) · title ${title}`);
    }
  </script>
</body>
</html>
Slot Layout Title size
sidebar, 280 px stacked, one column 20.4 px
main column, 640 px photo beside the text, two columns 31.2 px
band, 1100 px photo, text and actions, three columns 32 px

All three engines chose the same layouts; Firefox gave the title sizes as 20.4063 and 31.1875 px, because it keeps font sizes in steps of 1/64 px. A media query would have given all three cards the layout for a 1280 px window. This program works out what the stylesheet gives any slot, including the boundary at 30rem:

The card's layout and title size for a slot width JavaScript · programs/card_at.mjs
// What card.css gives a card in a slot of a given width: the layout its two container queries choose, and the title
// size from clamp(1.125rem, 0.75rem + 3cqi, 2rem), where 1cqi is 1 % of the slot's inline size.
const REM = 16; // px: rem in the clamp() uses the page's root font size

function cardAt(slotWidth) {
  const layout = slotWidth >= 60 * REM ? 'photo, text, actions' : slotWidth >= 30 * REM ? 'photo beside text' : 'stacked';
  const preferred = 0.75 * REM + 3 * (slotWidth / 100);
  const title = Math.min(Math.max(1.125 * REM, preferred), 2 * REM);
  return { layout, title };
}

console.log('slot     layout                 title');
for (const width of [280, 479, 480, 640, 960, 1100]) {
  const { layout, title } = cardAt(width);
  console.log(`${String(width).padStart(4)} px  ${layout.padEnd(22)} ${Number(title.toFixed(2))} px`);
}

Output

slot     layout                 title
 280 px  stacked                20.4 px
 479 px  stacked                26.37 px
 480 px  photo beside text      26.4 px
 640 px  photo beside text      31.2 px
 960 px  photo, text, actions   32 px
1100 px  photo, text, actions   32 px

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

Use media queries for the page’s own layout, which depends on the window, and container queries for components that can be placed anywhere: cards, media objects, form rows, widgets in a dashboard.

What container-type does, and what it costs

For size queries, container-type takes one of three values. normal, the default, makes no size container. inline-size lets descendants query the box’s width (its inline size, which is the width in horizontal text). size lets them query both width and height. Each container applies containment: style containment, plus inline-size containment for inline-size or size containment for size, and it starts an independent formatting context.

Inline-size containment means the box is sized as if it had nothing inside along the inline axis. That rule breaks a loop, since the container’s width can’t depend on the content that asks about the width, but it also means a box whose width normally comes from its content collapses. This page gives a filter bar container-type: inline-size in three such places, then fixes each:

Containers that take their width from their content HTML · zero-width.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Containers that take their width from their content</title>
  <style>
    body { margin: 0; padding: 1rem; font: 16px/1.5 Arial, sans-serif; }
    .case { margin-block-end: 1rem; }
    .filters { container-type: inline-size; } /* a query container for the buttons inside it */

    /* Three boxes whose width would come from their content */
    .inline .filters { display: inline-block; }
    .flex { display: flex; gap: 1rem; }
    .flex .filters { flex: none; }
    .grid { display: grid; grid-template-columns: auto 1fr; gap: 1rem; }

    /* The fixes: a width that comes from outside the box */
    .fixed.inline .filters { display: block; }
    .fixed.flex .filters { flex: 0 0 16rem; }
    .fixed.grid { grid-template-columns: 16rem 1fr; }
  </style>
</head>
<body>
  <template id="filters"><div class="filters"><button type="button">Size</button> <button type="button">Colour</button> <button type="button">Price</button></div><p>Results</p></template>
  <div class="case inline"></div>
  <div class="case flex"></div>
  <div class="case grid"></div>
  <div class="case inline fixed"></div>
  <div class="case flex fixed"></div>
  <div class="case grid fixed"></div>
  <script>
    for (const box of document.querySelectorAll('.case')) {
      box.append(document.getElementById('filters').content.cloneNode(true));
      const r = box.querySelector('.filters').getBoundingClientRect();
      console.log(`${box.className.replace('case ', '')}: the container is ${r.width} px wide and ${Math.round(r.height)} px tall`);
    }
  </script>
</body>
</html>
The container is Its width The fix Width after
inline-block 0 px display: block 768 px
a flex: none item 0 px flex: 0 0 16rem 256 px
in an auto grid column 0 px a 16rem column 256 px

All three engines measured the same numbers. At 0 px wide, the three buttons inside each broken container sat one above the other, 72 px tall, spilling out of a box with no width. A container must get its width from outside: a block that fills its parent, a flex basis, or a grid track. A size container also needs a height from outside, because its content no longer stretches it, so use size only for boxes with a known height, such as a full-screen panel. The independent formatting context also stops margins from collapsing through the container’s edge, which the margins lesson of this track measured.

Writing @container rules

A container query looks like a media query without the media type: conditions in parentheses, and, or and not, and the range syntax. It can’t ask about hover or user preferences, which are media questions. Each condition is answered by a container chosen this way: among the element’s ancestors, the nearest one that is a container for the features asked about and, when the query names one, has that name. With no such ancestor, the query is unknown and never applies.

A container can’t query itself

Because the answer comes from an ancestor, rules about the container’s own layout must wait for a container above it. This card is 600 px wide and is a container itself:

A container can't query itself HTML · self-query.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>A container can't query itself</title>
  <style>
    body { margin: 0; font: 16px/1.5 Arial, sans-serif; }
    .card { container-type: inline-size; display: flex; flex-direction: column; inline-size: 600px; }
    .card-body { display: flex; flex-direction: column; gap: 1rem; }

    /* Looks for a container above .card: there is none, so it never applies */
    @container (width >= 30rem) {
      .card { flex-direction: row; }
    }
    /* Asks .card, the nearest container above .card-body */
    @container (width >= 30rem) {
      .card-body { flex-direction: row; }
    }
  </style>
</head>
<body>
  <div class="card"><div class="card-body"><img alt="" width="160" height="120" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 3'/%3E"><p>Text beside the photo once the card is 30rem wide.</p></div></div>
  <script>
    const style = (s) => getComputedStyle(document.querySelector(s)).flexDirection;
    console.log(`.card is 600 px wide · .card flex-direction: ${style('.card')} · .card-body flex-direction: ${style('.card-body')}`);
  </script>
</body>
</html>

In all three engines, .card stayed a column, since no container sits above it, while .card-body, whose container is .card, became a row. Style the children, or wrap the component in a slot that is the container, as card.css does.

Name the containers you mean

Containers nest, and an unnamed query takes the nearest. Here a sidebar is a container named sidebar, and later someone makes its toolbar a container too, for the toolbar’s own buttons:

Which container a query reaches HTML · nested-names.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Which container a query reaches</title>
  <style>
    body { margin: 0; font: 16px/1.5 Arial, sans-serif; }
    .sidebar { container: sidebar / inline-size; inline-size: 320px; }
    .toolbar { container-type: inline-size; inline-size: 180px; } /* added later, for its own buttons */

    /* Unnamed: the nearest container, which is now the 180 px toolbar */
    @container (width >= 15rem) {
      .promo { outline: 2px solid; }
    }
    /* Named: the nearest container called sidebar, 320 px wide */
    @container sidebar (width >= 15rem) {
      .promo { text-decoration: underline; }
    }
  </style>
</head>
<body>
  <aside class="sidebar"><div class="toolbar"><p class="promo">Free returns for 30 days</p></div></aside>
  <script>
    const s = getComputedStyle(document.querySelector('.promo'));
    console.log(`unnamed query (toolbar, 180 px): ${s.outlineStyle === 'solid' ? 'matches' : 'does not match'} · sidebar query (320 px): ${s.textDecorationLine === 'underline' ? 'matches' : 'does not match'}`);
  </script>
</body>
</html>
A paragraph in a 180 px toolbar in a 320 px sidebar: the unnamed query reaches the toolbar, the query named sidebar reaches the sidebar.Which container a query reachesaside.sidebar320 pxcontainer: sidebar / inline-sizediv.toolbar180 pxcontainer-type: inline-sizep.promothe element both queries style@container (width >= 15rem)No name: it asks the nearest container,the dashed toolbar.180 px is less than 240 px: no match@container sidebar (width >= 15rem)Named: it asks the nearest containercalled sidebar, the solid blue box.320 px is at least 240 px: match

An unnamed query asks the nearest container; a named one asks the nearest with that name

Text description of the diagram

The top half shows three nested boxes. The outer box, drawn solid blue, is aside.sidebar, a container named sidebar (container: sidebar / inline-size), 320 px wide. Inside it, drawn dashed, is div.toolbar, an unnamed container (container-type: inline-size), 180 px wide. Inside the toolbar is p.promo, the element both queries style.

The bottom half shows the two queries:

  1. @container (width >= 15rem), drawn dashed like the toolbar. It has no name, so it asks the nearest container, the toolbar. 180 px is less than 240 px, so it does not match.
  2. @container sidebar (width >= 15rem), drawn solid blue like the sidebar. It asks the nearest container called sidebar. 320 px is at least 240 px, so it matches.

The unnamed query now asks the 180 px toolbar and stops matching, which no one meant; the query named sidebar still asks the 320 px sidebar. All three engines agreed. Name every container a component depends on, with container-name or the container shorthand, and name queries by what they mean (card, sidebar, page). A query with a name and no condition, such as @container sidebar { … }, applies whenever such a container exists; it is newly available in Chrome 148, Firefox 149 and Safari 26.4. Container style queries, which ask about a container’s custom properties, are newly available too and belong to the conditional CSS module of this track.

em and rem mean something else here

In a media query, em and rem are the browser’s initial font size. In a container query, lengths are worked out with the container’s own computed values: em is the container’s font size, and rem the root element’s font size as the page sets it. This page sets the root to 20 px and fills a 280 px window with a 16 px container:

The same lengths in a media query and in container queries HTML · query-lengths.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>em and rem in container and media queries</title>
  <style>
    html { font-size: 125%; } /* the page's 1rem is 20 px */
    body { margin: 0; }
    .box { container-type: inline-size; font-size: 16px; } /* fills the 280 px window */

    /* rem: the browser's initial 16 px in a media query, the root's 20 px in a container query */
    @media (width >= 15rem) { .box p { --media-rem: yes; } }
    @container (width >= 15rem) { .box p { --container-rem: yes; } }
    /* em: the container's own 16 px font size */
    @container (width >= 15em) { .box p { --container-em: yes; } }
  </style>
</head>
<body>
  <div class="box"><p>Delivery in 2 days</p></div>
  <script>
    const p = getComputedStyle(document.querySelector('.box p'));
    const width = document.querySelector('.box').getBoundingClientRect().width;
    for (const [name, prop] of [['@media (width >= 15rem)', '--media-rem'], ['@container (width >= 15rem)', '--container-rem'], ['@container (width >= 15em)', '--container-em']]) {
      console.log(`${width} px wide · ${name}: ${p.getPropertyValue(prop).trim() === 'yes' ? 'matches' : 'does not match'}`);
    }
  </script>
</body>
</html>
Query Length Matches at 280 px
@media (width >= 15rem) 15 × 16 = 240 px yes
@container (width >= 15rem) 15 × 20 = 300 px no
@container (width >= 15em) 15 × 16 = 240 px yes

The same 15rem meant 240 px in the media query and 300 px in the container query, in all three engines. When you move a breakpoint from @media to @container, check the containers’ font sizes, or write it in rem with a root size you know.

Container query units

Container query units are percentages of a container: cqw and cqh are 1 % of its width and height, cqi and cqb 1 % of its inline and block size, and cqmin and cqmax the smaller and larger of cqi and cqb. Each unit looks for the nearest container that can answer for its axis, and with none it falls back to the small viewport size on that axis:

Container units and their fallback HTML · cq-units.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Container query units and their fallback</title>
  <style>
    body { margin: 0; }
    .card { container-type: inline-size; inline-size: 400px; }
    .wide { font-size: 5cqi; }   /* 5 % of the nearest inline-size container */
    .tall { font-size: 10cqb; }  /* block axis: an inline-size container can't answer, so the small viewport does */
  </style>
</head>
<body>
  <div class="card"><p class="wide in">Inside a 400 px container</p><p class="tall in">Inside, using cqb</p></div>
  <p class="wide out">Outside any container</p>
  <script>
    for (const el of document.querySelectorAll('p')) console.log(`${el.textContent}: ${getComputedStyle(el).fontSize}`);
  </script>
</body>
</html>

In an 800 by 600 px window, in all three engines, 5cqi was 20 px inside the 400 px container and 40 px outside any container, 5 % of the 800 px viewport. 10cqb inside the same container was 60 px: an inline-size container can’t answer for the block axis, so the unit used the 600 px viewport height. Use cqi for sizes that follow a component’s width, like the card’s title, and avoid cqb unless a size container is there to answer it.

Media query or container query?

Media query or container query?
Criterion Media queryContainer query
Asks about The viewport, the input and the reader's settingsThe size of an ancestor container
Needs Nothingcontainer-type on an ancestor, which applies containment
em and rem The browser's initial font sizeThe container's and the root's font sizes
Units vw, vh, svw and the other viewport unitscqi, cqb, cqw, cqh, cqmin and cqmax
When to choose Page layout, hover and pointer, preferencesComponents placed in slots of different widths
HTML, CSS & JS Playground (Online Editor) Paste card.css and card.html, give the slots widths of your own, and compare the Full, Tablet and Phone previews.

Practise: which container answers

Exercise · Medium · JavaScript

Find the container a query asks, and resolve its lengths

Two questions decide whether a container query matches: which container answers it, and how long its lengths are. Write two functions in container.mjs and export them.

queryContainer(ancestors, { name = '', axis = 'inline' }) takes the ancestors of the element being styled, from its parent outwards, each as { name, type }: name is the container's name ('' when it has none) and type is 'normal', 'inline-size' or 'size'. It returns the index of the container the query asks, or -1 when there is none:

- a query about the inline axis (width or inline-size) needs a container of type inline-size or size; - a query about the block axis (height or block-size) needs type size; - a query with a name only accepts a container with exactly that name; a query without one takes the nearest container that fits.

queryLength(value, { containerFont, rootFont }) turns a length of a container query, such as '15rem', '15em' or '240px', into px. In a container query, em is the container's own font size and rem the root element's, both in px.

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

Starter code · container.mjs

/** The index of the ancestor a container query asks, or -1 when no ancestor can answer it. */
export function queryContainer(ancestors, { name = '', axis = 'inline' } = {}) {
  // Replace this line with your code.
  return -1;
}

/** A length of a container query in px: em is the container's font size, rem the root's. */
export function queryLength(value, { containerFont, rootFont }) {
  // Replace this line with your code.
  return 0;
}
The sample tests · container.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { queryContainer, queryLength } from './container.mjs';

const sidebar = { name: 'sidebar', type: 'inline-size' };
const toolbar = { name: '', type: 'inline-size' };
const plain = { name: '', type: 'normal' };
const panel = { name: 'panel', type: 'size' };

test('an unnamed query asks the nearest container', () => {
  assert.equal(queryContainer([toolbar, sidebar]), 0);
  assert.equal(queryContainer([plain, plain, sidebar]), 2);
});

test('a named query skips to the nearest container with that name', () => {
  assert.equal(queryContainer([toolbar, plain, sidebar], { name: 'sidebar' }), 2);
  assert.equal(queryContainer([toolbar, sidebar], { name: 'panel' }), -1);
});

test('a block-axis query needs a size container', () => {
  assert.equal(queryContainer([sidebar, toolbar, panel], { axis: 'block' }), 2);
  assert.equal(queryContainer([sidebar, toolbar], { axis: 'block' }), -1);
  assert.equal(queryContainer([panel, sidebar]), 0);
});

test('with no container at all, nothing answers', () => {
  assert.equal(queryContainer([plain, plain]), -1);
  assert.equal(queryContainer([]), -1);
});

test('px lengths stay as they are', () => {
  assert.equal(queryLength('240px', { containerFont: 20, rootFont: 16 }), 240);
});

test('em is the container\'s font size, rem the root\'s', () => {
  assert.equal(queryLength('15em', { containerFont: 16, rootFont: 20 }), 240);
  assert.equal(queryLength('15rem', { containerFont: 16, rootFont: 20 }), 300);
  assert.equal(queryLength('2.5rem', { containerFont: 14, rootFont: 16 }), 40);
  assert.equal(queryLength('30em', { containerFont: 14, rootFont: 16 }), 420);
});
A hint

Walk the array from index 0, the parent. Skip an ancestor whose type can't answer the axis, or whose name differs when the query has one, and return the first index that is left. For lengths, read the number and the unit with a regular expression such as /^(\d*\.?\d+)(px|em|rem)$/.

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

5 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 5 A card appears in a 280 px sidebar and in a 640 px main column on the same page. Why can't one media query give it two layouts?

    Choose one answer.

    Show the answer to question 1

    Answer: Both cards share one viewport, so a media query gives both the same answer

    A media query asks about the viewport, and both cards are in the same one. A container query asks about each card's own slot, so in the lesson's run the 280 px slot stacked the card and the 640 px slot put the photo beside the text, on one page.

  2. Question 2 of 5 .card { container-type: inline-size; } is followed by @container (width >= 30rem) { .card { flex-direction: row; } }. The card is 600 px wide and has no container above it. What happens?

    Choose one answer.

    Show the answer to question 2

    Answer: Nothing, because a container query looks for a container above the element it styles

    An element can't query itself: the query is answered by an ancestor container, and there is none, so the rule never applies. Styling .card-body inside the card works, because .card is its container. All three engines kept the card in a column and turned the body into a row.

  3. Question 3 of 5 Which of these containers end up 0 px wide once they get container-type: inline-size?

    Choose every answer that is right.

    Show the answer to question 3

    Answer:

    • A grid item in an auto column
    • An inline-block box with buttons inside
    • A flex item with flex: none

    Inline-size containment sizes the box as if it had no content along the inline axis, so a box whose width comes from its content collapses. All three engines measured 0 px for the first three. A fixed track, a flex basis or a block that fills its parent gives the container a width from outside, which is what it needs.

  4. Question 4 of 5 A title has font-size: clamp(1.125rem, 0.75rem + 3cqi, 2rem) with a 16 px root, in a container 640 px wide. What is its size in px?

    Type a number.

    Show the answer to question 4

    Answer: 31.2 px (anything from 31.15 to 31.25 counts)

    3cqi is 3 % of 640 px, 19.2 px, and 0.75rem is 12 px, so the preferred size is 31.2 px, between the 18 px minimum and the 32 px maximum. Chromium and WebKit computed 31.2 px; Firefox, which keeps font sizes in steps of 1/64 px, 31.1875 px.

  5. Question 5 of 5 The root font size is 20 px and a container is 280 px wide with a font size of 16 px. Which query matches?

    Choose one answer.

    Show the answer to question 5

    Answer: @container (width >= 15em)

    In a container query, em is the container's font size and rem the root's: 15em is 240 px and matches, 15rem is 300 px and doesn't. In a media query, 15rem would be 240 px, because media queries use the initial font size. All three engines gave these answers in the lesson's run.

Interview questions

Fresher · What is the difference between a media query and a container query, and when do you use each?

A media query asks about the viewport, the input device and the reader’s settings, so every element on the page gets the same answer. A container query asks about the size of an ancestor that was made a container with container-type: inline-size or size, so the same component can get different layouts in a sidebar and in a main column. Use media queries for the page’s own layout and for hover, pointer and preference questions, and container queries for components that appear in slots of different widths. Containers have a cost: inline-size containment means their width can’t come from their content, so they need a width from their parent, a flex basis or a grid track.

Key takeaways

  • container: card / inline-size names a container; @container card (width >= 30rem) asks about it.
  • The answer comes from an ancestor: an element can’t query itself, so style its children or add a wrapper.
  • An inline-size container is sized as if empty: inline-blocks, flex: none items and auto grid columns collapse to 0 px.
  • Name the containers a component depends on; an unnamed query takes the nearest container, whatever it is.
  • In container queries, em is the container’s font size and rem the root’s, unlike in media queries.
  • 1cqi is 1 % of the container’s inline size; with no container, container units use the small viewport.

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.