CSS and Layout skill 3 of 3

Container Queries SKILL.md: Fix @container Rules That Never Match

The Container Queries skill makes Claude, Cursor or another AI agent find why an @container rule never matches and fix it. A read-only checker flags missing container types, self-styling queries, collapsing containers and unknown names, then the agent measures the component at two container widths before and after, editing in small approved batches.

container-queries/SKILL.md, version 1.0.0, 3,072 words, plus scripts/cq_check.py.

An agent works on container queries in this order: run a read-only checker over the stylesheets, find which ancestor is the container and how wide it is, decide whether a component should use a container query or stay on a media query, declare the container, write the queries, then measure the result at two container widths in a browser.

It works on plain CSS, bundler output and <style> blocks. Facts and measurements sit in references/appendix.md, component recipes in references/patterns.md.

When to use this skill, and when not to

Use it when an @container rule does nothing, when a component collapses to zero width or height after container-type was added, when cqi units give viewport-sized values, or when a card, widget or sidebar item should change layout with the space it is given.

RequestUse instead
Horizontal overflow, 1fr blowouts, truncationflexbox-grid-layout.md
A rule that loses to another rulecascade-layers.md
Page-level breakpoints, orientation, prefers-* queriesKeep @media. Step 3 explains the split
style() and scroll-state() container queriesOut of scope for this release. They were not tested
Fluid type and spacing scalesOut of scope for this release

Guardrails

  1. Propose, then change. Report and plan first. Edit only after the user approves a batch, on a branch under version control. Never push, merge or deploy.
  2. Never edit vendor files. Do not change files in node_modules, vendor folders or CDN copies. Override them from your own CSS.
  3. Local pages only, no page text. Measure only pages served from the user's machine, with fake data. The snippets read computed styles and sizes, never page text, cookies or storage. Do not paste url(...) values into reports.
  4. Batches stay small. At most 1 component or 20 rules per batch (defaults), each shown as a diff.
  5. Keep what works. Add the container query beside the old media query, then delete the media rule only after step 7 passes.
  6. Nothing is invented. Thresholds are labelled defaults. Source facts carry a date. Version-dependent facts carry verify before relying on this. A breakpoint number comes from the component's content or the design, never from the old viewport value.
  7. Refuses to: put container-type: size on an element with no height, make every wrapper a container, claim a fix without measuring two container widths, or guess a breakpoint.

Inputs the agent must have before starting

Ask for every missing item. Do not assume.

InputAsk exactly
Stylesheets and markup"List every stylesheet and the HTML or template that holds the component, in load order."
The component"Which component should respond to its container? Where is it placed, and how wide is each slot?"
The layout change"What should change, and at what width does the component stop fitting?"
Current behaviour"Which @media rules does it use today?"
Browsers"Which browsers must work? Container queries became Baseline newly available in 2023."
How to run the page"How do I open it locally?"
Approval"May I edit on a branch in batches, showing each diff first?"

Raw SKILL.md file

.md

This is the exact file agents read, including the credit header and licence notice. Save it as container-queries/SKILL.md.

---
name: container-queries
description: "Audits and fixes CSS container queries that never match, collapse or fall back to the viewport: container-name with no container-type, a query that targets the container itself, size containers with no height, inline-size containers in shrink-to-fit boxes, unusable display values and unknown names. Runs a read-only checker, then converts component breakpoints from media queries in approved batches and measures two container widths. Use for @container not working or component-level responsive design. Do NOT use for page breakpoints, layout overflow, cascade conflicts or fluid type."
license: CC-BY-4.0
metadata:
  author: Afeez Adebayo
  author_url: https://afeezadebayo.com
  author_linkedin: https://www.linkedin.com/in/afeez-adebayo/
  author_x: https://x.com/za_icon
  source: https://afeezadebayo.com/skills-md/frontend-development/css-layout/container-queries/
  category: frontend-development
  subcategory: css-layout
  version: "1.0.0"
  created: "2026-10-25"
  updated: "2026-10-25"
---

# Container Queries: Make a Component Respond to Its Container, Not the Viewport

An agent works on container queries in this order: run a read-only checker over the stylesheets, find which ancestor is the container and how wide it is, decide whether a component should use a container query or stay on a media query, declare the container, write the queries, then measure the result at two container widths in a browser.

It works on plain CSS, bundler output and `<style>` blocks. Facts and measurements sit in `references/appendix.md`, component recipes in `references/patterns.md`.

## When to use this skill, and when not to

Use it when an `@container` rule does nothing, when a component collapses to zero width or height after `container-type` was added, when `cqi` units give viewport-sized values, or when a card, widget or sidebar item should change layout with the space it is given.

| Request | Use instead |
|---|---|
| Horizontal overflow, `1fr` blowouts, truncation | `flexbox-grid-layout.md` |
| A rule that loses to another rule | `cascade-layers.md` |
| Page-level breakpoints, orientation, `prefers-*` queries | Keep `@media`. Step 3 explains the split |
| `style()` and `scroll-state()` container queries | Out of scope for this release. They were not tested |
| Fluid type and spacing scales | Out of scope for this release |

## Guardrails

1. **Propose, then change.** Report and plan first. Edit only after the user approves a batch, on a branch under version control. Never push, merge or deploy.
2. **Never edit vendor files.** Do not change files in `node_modules`, vendor folders or CDN copies. Override them from your own CSS.
3. **Local pages only, no page text.** Measure only pages served from the user's machine, with fake data. The snippets read computed styles and sizes, never page text, cookies or storage. Do not paste `url(...)` values into reports.
4. **Batches stay small.** At most 1 component or 20 rules per batch (defaults), each shown as a diff.
5. **Keep what works.** Add the container query beside the old media query, then delete the media rule only after step 7 passes.
6. **Nothing is invented.** Thresholds are labelled defaults. Source facts carry a date. Version-dependent facts carry **verify before relying on this**. A breakpoint number comes from the component's content or the design, never from the old viewport value.
7. **Refuses to:** put `container-type: size` on an element with no height, make every wrapper a container, claim a fix without measuring two container widths, or guess a breakpoint.

## Inputs the agent must have before starting

Ask for every missing item. Do not assume.

| Input | Ask exactly |
|---|---|
| Stylesheets and markup | "List every stylesheet and the HTML or template that holds the component, in load order." |
| The component | "Which component should respond to its container? Where is it placed, and how wide is each slot?" |
| The layout change | "What should change, and at what width does the component stop fitting?" |
| Current behaviour | "Which `@media` rules does it use today?" |
| Browsers | "Which browsers must work? Container queries became Baseline newly available in 2023." |
| How to run the page | "How do I open it locally?" |
| Approval | "May I edit on a branch in batches, showing each diff first?" |

## Workflow

### Step 0: Confirm scope

List the files and the one component. Minified input is fine for the checker, but edits go in source files. One component at a time.

### Step 1: Run the checker

```
python3 scripts/cq_check.py page.css | head -40
```

Read only its output. Do not read the script. Pass every stylesheet that declares or uses containers, because a query is matched against containers in all the files you pass. Add `--strict` to exit 1 on any warning. The last two lines give counts. Each code has one fix, in this table.

| Code | Level | Meaning | Fix |
|---|---|---|---|
| `name-no-type` | warn | A container name with no `container-type` never matches a size query | `container: name / inline-size` |
| `query-unknown-name` | warn | `@container name` where no scanned file declares that name | Declare it, or fix the spelling, or pass the missing file |
| `query-no-container` | warn | An unnamed `@container` and no container-type anywhere | Declare a container on an ancestor |
| `self-style` | warn | A query styles the element that is the container | Style a child, or wrap the element |
| `size-no-height` | warn | `container-type: size` with no height, block-size or aspect-ratio collapses to 0 high | Use `inline-size`, or give it a height |
| `shrink-to-fit` | warn | An inline-size container in a float, inline-block or fit-content box collapses to 0 wide | Give it a width, `flex-basis` or `width: 100%` |
| `bad-display` | warn | Container-type on `display: inline`, `contents`, `table` or `table-cell` does not make a container | Use a block, flex, grid or inline-block box |
| `height-query-inline-size` | warn | A height query on an inline-size container never matches | Use `container-type: size` and a height |
| `cq-unit-no-container` | info | `cqi`, `cqw` and similar units with no container fall back to small viewport units | Declare a container, or use another unit |

### Step 2: Find the container in the browser

Run the ancestors snippet (Templates) on the element that should change. It lists every ancestor with a container type or name, with its type and size. If the list is empty, no query can match. If the nearest entry is not the one you expect, an unnamed query is matching the wrong container: name the query.

A query is measured on the container's content box. Padding does not count (Chromium 153: a 400px box with 50px padding each side matched `min-width: 300px` and failed `min-width: 350px`).

### Step 3: Decide container query or media query

| Finding | Decision |
|---|---|
| The component sits in slots of different widths, such as a main column and a sidebar | Container query |
| The layout depends on the viewport, such as the page grid, the header or `prefers-*` | Keep `@media` |
| A page shell contains components | `@media` for the shell, container queries inside the components |
| The component appears in one slot only and that slot never changes | Keep `@media`. A container adds a wrapper for no gain |

### Step 4: Declare the container

```css
.card-wrap { container: card / inline-size; }
```

| Rule | Reason |
|---|---|
| Put the container on a wrapper or a parent, never on the element you restyle | An element cannot be styled by a query on itself (measured, Chromium 153). Its `::before` and `::after` can |
| Always name it | An unnamed query matches the nearest container, which may be an inner one you did not mean (measured: a 200px inner container beat an 800px outer one) |
| Write the type | `container: card` alone sets a name and no type, so queries never match (measured) |
| Default to `inline-size` | `size` also applies size containment to height, so the box collapses unless it has a height (measured: 0px high) |
| Use a block, flex, grid or inline-block box | Containers on `display: inline`, `contents`, `table` and `table-cell` did not match (measured) |
| Give the container a width | In a float, an inline-block or an auto-width flex item, an inline-size container collapsed to 0px wide. `width: 100%`, `flex: 1` and `flex: 0 0 200px` all fixed it (measured) |

Use `size` only when you query height, and then give the container a `height`, `block-size` or `aspect-ratio`.

### Step 5: Write the queries

```css
.card { display: flex; flex-direction: column; gap: 1rem; }
@container card (width >= 400px) { .card { flex-direction: row; } }
```

| Rule | Reason |
|---|---|
| Base styles are the narrow layout, queries add the wide one | Mobile-first, fewer overrides |
| `(min-width: 400px)` and `(width >= 400px)` both work | Measured in Chromium 153. The bound is inclusive: 400px matched |
| Take the breakpoint from the content, not from the old viewport query | Ask the user, or measure the width at which the component stops fitting |
| Queries can sit inside `@media` and inside `@supports` | Measured |
| Keep every queried rule on a child of the container | Step 4 |

If browsers older than the Baseline 2023 versions must work, wrap the container declaration and the queries in `@supports (container-type: inline-size)` and keep the narrow layout as the base (measured: the support test is true in Chromium 153; **verify before relying on this** in older engines).

### Step 6: Container units

`cqi` is 1% of the container's inline size and `cqw` 1% of its width. Without a container they fall back to the small viewport units (MDN and web.dev notes; measured: `50cqw` gave 500px in a 1000px viewport, which cannot tell small from large viewport units). Use them for sizes that should follow the slot, wrapped in `clamp()` so a very narrow or very wide container cannot make them extreme.

### Step 7: Verify

1. Run the checker again. Warnings must be 0, or each is listed with a reason.
2. Run the measuring snippet (Templates) on the wrapper at one width below and one at the breakpoint. The computed value must change at the breakpoint and not before.
3. Check the component in every real slot, not only the test page.
4. Run the existing test suite if there is one.
5. List what you could not test: browsers older than your target, slots behind a login.

### Step 8: Report

Use the report template. A short list of file, line, issue and fix.

## Worked example

An invented page: a `.card` with a title that should grow at 400px, a sidebar with a nav that should become a row at 300px, a `.panel`, a `.tag` and a `.widget`. The agent runs the checker, which reports 6 warnings, 0 errors.

```
page.css:9  warn  bad-display  .tag: container-type on display: inline does not make a container ...
page.css:1  warn  name-no-type  .card: container name without container-type inline-size or size ...
page.css:6  warn  query-unknown-name  @container sidebar names a container that no scanned file declares ...
page.css:12  warn  self-style  .widget is a container and cannot be styled by its own query ...
page.css:5  warn  shrink-to-fit  .sidebar: inline-size container in a shrink-to-fit box collapses to 0 wide ...
page.css:8  warn  size-no-height  .panel: container-type size with no height, block-size or aspect-ratio collapses to 0 high ...
```

At 800px wide the browser agrees with the checker: the card title is 16px where 24px was meant, the nav is `block` where `flex` was meant, the sidebar is 0px wide, the panel is 0px high and the widget padding is 16px where 32px was meant.

Fixes in one batch (`page.css` only): a `.card-wrap` wrapper takes `container: card / inline-size`, the sidebar container is renamed to match its query and given `width: 100%`, the panel gets `height: 12rem`, the tag becomes `inline-block` with a width, and the widget gets a wrapper. The checker then reports 0 warnings. At 800px the browser gives a 24px title, a `flex` nav, a sidebar 800px wide, a panel 192px high and 32px widget padding. The title measured 16px at 300px and 399px and 24px at 400px and 600px, measured in Chromium 153 on 25 October 2026.

## Templates

### Ancestors that are containers (step 2)

```js
(sel) => { let el = document.querySelector(sel), out = [];
  for (let n = el.parentElement; n; n = n.parentElement) { const s = getComputedStyle(n);
    if (s.containerType !== 'normal' || s.containerName !== 'none') { const r = n.getBoundingClientRect();
      out.push(n.tagName.toLowerCase() + (n.className ? '.' + String(n.className).trim().split(/\s+/).join('.') : '') + ' | name ' + s.containerName + ' | type ' + s.containerType + ' | ' + Math.round(r.width) + 'x' + Math.round(r.height)); } }
  return out; }
```

Call it with a selector for the element, for example `('.card .title')`. It prints tag names, class names and sizes only.

### Measure a property at container widths (step 7)

```js
(wrap, sel, prop, widths) => widths.map(w => { const c = document.querySelector(wrap), o = c.style.width;
  c.style.width = w + 'px'; const v = getComputedStyle(document.querySelector(sel)).getPropertyValue(prop); c.style.width = o; return w + 'px: ' + v; })
```

Call it as `('.card-wrap', '.card .title', 'font-size', [300, 399, 400, 600])`. It restores the width after each read.

### Report

```
Container query report: <page>, <date>
Component: <name>. Container: <selector, name, type>. Breakpoint: <width, source>.
Checker before: warnings N. After: warnings N.
Changes: <file:line  issue  fix>   (one line each)
Measured: <property at widths, before and after>
Left as is: <warning  reason>
Not tested: <items>
```

### Starting prompt

```
Use the container-queries skill. Stylesheets and markup: <list>. Component: <name>, in slots of <widths>.
It should <change> when its container is narrower than <width, or "ask me">. Edit on a branch in batches of one component or 20 rules and show each diff first.
```

## Failure modes and anti-patterns

| Mistake | How to spot it | Do instead |
|---|---|---|
| Styling the container with its own query | `self-style`, and the rule never applies | Query a child, or add a wrapper |
| Naming a container and writing no type | `name-no-type` | `container: name / inline-size` |
| `container-type: size` without a height | `size-no-height`, and the component is 0px high | `inline-size`, or add a height |
| Inline-size container in a float or an auto-width flex item | `shrink-to-fit`, or the browser shows 0px width | `width: 100%`, `flex: 1` or a fixed width |
| Container on an inline, contents or table box | `bad-display` | A block, flex, grid or inline-block box |
| Copying the viewport breakpoint into the container query | A component that breaks at an arbitrary width | Measure where the content stops fitting |

## Quality bar

Run every item before handing over.

1. Checker warnings are 0, or each is listed with a reason.
2. Every query is named, and the container it names declares a type.
3. No rule restyles the container with its own query.
4. The measuring snippet shows the value changing at the breakpoint and not before.
5. The component was checked in each real slot.
6. No file under `node_modules` or a vendor folder was edited.
7. Every changed line appears in the report with file, line, issue and fix.
8. The report lists what was not tested.

## Cost

Figures are estimates from this skill's own count (words × 1.35 for prose, characters ÷ 3.5 for code and tables), not from a model tokeniser. Rival figures are estimates from the excerpts I read.

| Item | Tokens | Status | Read condition |
|---|---|---|---|
| A. Description (always loaded) | 113 | Estimate | Every session with the skill installed |
| B. `SKILL.md` body | 4,463 | Estimate | When the skill fires |
| C. `references/appendix.md` | 843 | Estimate | Only to check a claim or a measurement |
| C. `references/patterns.md` | 525 | Estimate | Only when writing or rebuilding a component's container and queries |
| Script output: `cq_check.py` (worked example) plus measuring snippets | 605 | Estimate | Every run. Checker output is capped with `head -40`, about 1,524 tokens |
| D. Typical run (A + B + script and snippet output) | 5,181 | Estimate | Common path: fix flagged lines, no references |
| D. Worst case (A + B + both references + 40 lines of output) | 8,068 | Estimate | Rebuilding a layout, many findings |
| E. Cheaper path | 4,956 | Estimate | Run the checker, fix only flagged lines, measure two container widths, skip the references |

Rival sizes, estimated from the excerpts I read (SKILL.md only, files they link were not sized): openskillindex container-query-architect about 350 to 400 words, roughly 475 to 540 tokens, no scripts; thedaviddias container-queries (tessl) about 230 words, roughly 310 tokens, tessl score 58; wshobson responsive-design (smithery) about 250 to 300 words, roughly 340 to 405 tokens, plus `references/details.md`. This skill costs more to load than all three. What it adds is a checker for the failures that silently stop a query matching, and a measuring step. On a stylesheet with no containers the checker printed 93 characters for a 235,601-character file, so the saving is the agent not reading the file. On the 12-line worked example its output is larger than the file, so on a stylesheet under about 100 lines read the file and use the ancestors snippet.

## Handoffs

`flexbox-grid-layout.md` covers overflow and shrinking inside the container. `cascade-layers.md` covers a container rule that loses to another rule.

## Appendix

Tool facts, measurements and sources are in `references/appendix.md`. Component recipes are in `references/patterns.md`.

## Sources

Last verified 25 October 2026 for every source, except where a date says otherwise.

- Anthropic, Skill authoring best practices: https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices
- MDN, Container queries: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries
- web.dev, Baseline 2023 (updated 12 December 2023): https://web.dev/blog/baseline2023
- csswg-drafts issue 7667: https://github.com/w3c/csswg-drafts/issues/7667
- openskillindex, container-query-architect. Rival implementation: https://openskillindex.com
- thedaviddias, container-queries (via tessl). Rival implementation: https://tessl.io/registry/thedaviddias
- wshobson, responsive-design (via smithery.ai). Rival implementation: https://smithery.ai/skills/wshobson/responsive-design

---
Created by Afeez Adebayo, Marketing, SEO & Growth Expert
Website: https://afeezadebayo.com
LinkedIn: https://www.linkedin.com/in/afeez-adebayo/
X: https://x.com/za_icon
Source: https://afeezadebayo.com/skills-md/frontend-development/css-layout/container-queries/

Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). You may copy, share and adapt this skill, including for commercial use, as long as you credit Afeez Adebayo and link to the source URL above. Keep this notice in any copy or adapted version.

Workflow

Step 0: Confirm scope

List the files and the one component. Minified input is fine for the checker, but edits go in source files. One component at a time.

Step 1: Run the checker

python3 scripts/cq_check.py page.css | head -40

Read only its output. Do not read the script. Pass every stylesheet that declares or uses containers, because a query is matched against containers in all the files you pass. Add --strict to exit 1 on any warning. The last two lines give counts. Each code has one fix, in this table.

CodeLevelMeaningFix
name-no-typewarnA container name with no container-type never matches a size querycontainer: name / inline-size
query-unknown-namewarn@container name where no scanned file declares that nameDeclare it, or fix the spelling, or pass the missing file
query-no-containerwarnAn unnamed @container and no container-type anywhereDeclare a container on an ancestor
self-stylewarnA query styles the element that is the containerStyle a child, or wrap the element
size-no-heightwarncontainer-type: size with no height, block-size or aspect-ratio collapses to 0 highUse inline-size, or give it a height
shrink-to-fitwarnAn inline-size container in a float, inline-block or fit-content box collapses to 0 wideGive it a width, flex-basis or width: 100%
bad-displaywarnContainer-type on display: inline, contents, table or table-cell does not make a containerUse a block, flex, grid or inline-block box
height-query-inline-sizewarnA height query on an inline-size container never matchesUse container-type: size and a height
cq-unit-no-containerinfocqi, cqw and similar units with no container fall back to small viewport unitsDeclare a container, or use another unit

Step 2: Find the container in the browser

Run the ancestors snippet (Templates) on the element that should change. It lists every ancestor with a container type or name, with its type and size. If the list is empty, no query can match. If the nearest entry is not the one you expect, an unnamed query is matching the wrong container: name the query.

A query is measured on the container's content box. Padding does not count (Chromium 153: a 400px box with 50px padding each side matched min-width: 300px and failed min-width: 350px).

Step 3: Decide container query or media query

FindingDecision
The component sits in slots of different widths, such as a main column and a sidebarContainer query
The layout depends on the viewport, such as the page grid, the header or prefers-*Keep @media
A page shell contains components@media for the shell, container queries inside the components
The component appears in one slot only and that slot never changesKeep @media. A container adds a wrapper for no gain

Step 4: Declare the container

.card-wrap { container: card / inline-size; }
RuleReason
Put the container on a wrapper or a parent, never on the element you restyleAn element cannot be styled by a query on itself (measured, Chromium 153). Its ::before and ::after can
Always name itAn unnamed query matches the nearest container, which may be an inner one you did not mean (measured: a 200px inner container beat an 800px outer one)
Write the typecontainer: card alone sets a name and no type, so queries never match (measured)
Default to inline-sizesize also applies size containment to height, so the box collapses unless it has a height (measured: 0px high)
Use a block, flex, grid or inline-block boxContainers on display: inline, contents, table and table-cell did not match (measured)
Give the container a widthIn a float, an inline-block or an auto-width flex item, an inline-size container collapsed to 0px wide. width: 100%, flex: 1 and flex: 0 0 200px all fixed it (measured)

Use size only when you query height, and then give the container a height, block-size or aspect-ratio.

Step 5: Write the queries

.card { display: flex; flex-direction: column; gap: 1rem; }
@container card (width >= 400px) { .card { flex-direction: row; } }
RuleReason
Base styles are the narrow layout, queries add the wide oneMobile-first, fewer overrides
(min-width: 400px) and (width >= 400px) both workMeasured in Chromium 153. The bound is inclusive: 400px matched
Take the breakpoint from the content, not from the old viewport queryAsk the user, or measure the width at which the component stops fitting
Queries can sit inside @media and inside @supportsMeasured
Keep every queried rule on a child of the containerStep 4

If browsers older than the Baseline 2023 versions must work, wrap the container declaration and the queries in @supports (container-type: inline-size) and keep the narrow layout as the base (measured: the support test is true in Chromium 153; verify before relying on this in older engines).

Step 6: Container units

cqi is 1% of the container's inline size and cqw 1% of its width. Without a container they fall back to the small viewport units (MDN and web.dev notes; measured: 50cqw gave 500px in a 1000px viewport, which cannot tell small from large viewport units). Use them for sizes that should follow the slot, wrapped in clamp() so a very narrow or very wide container cannot make them extreme.

Step 7: Verify

  1. Run the checker again. Warnings must be 0, or each is listed with a reason.
  2. Run the measuring snippet (Templates) on the wrapper at one width below and one at the breakpoint. The computed value must change at the breakpoint and not before.
  3. Check the component in every real slot, not only the test page.
  4. Run the existing test suite if there is one.
  5. List what you could not test: browsers older than your target, slots behind a login.

Step 8: Report

Use the report template. A short list of file, line, issue and fix.

Worked example

An invented page: a .card with a title that should grow at 400px, a sidebar with a nav that should become a row at 300px, a .panel, a .tag and a .widget. The agent runs the checker, which reports 6 warnings, 0 errors.

page.css:9  warn  bad-display  .tag: container-type on display: inline does not make a container ...
page.css:1  warn  name-no-type  .card: container name without container-type inline-size or size ...
page.css:6  warn  query-unknown-name  @container sidebar names a container that no scanned file declares ...
page.css:12  warn  self-style  .widget is a container and cannot be styled by its own query ...
page.css:5  warn  shrink-to-fit  .sidebar: inline-size container in a shrink-to-fit box collapses to 0 wide ...
page.css:8  warn  size-no-height  .panel: container-type size with no height, block-size or aspect-ratio collapses to 0 high ...

At 800px wide the browser agrees with the checker: the card title is 16px where 24px was meant, the nav is block where flex was meant, the sidebar is 0px wide, the panel is 0px high and the widget padding is 16px where 32px was meant.

Fixes in one batch (page.css only): a .card-wrap wrapper takes container: card / inline-size, the sidebar container is renamed to match its query and given width: 100%, the panel gets height: 12rem, the tag becomes inline-block with a width, and the widget gets a wrapper. The checker then reports 0 warnings. At 800px the browser gives a 24px title, a flex nav, a sidebar 800px wide, a panel 192px high and 32px widget padding. The title measured 16px at 300px and 399px and 24px at 400px and 600px, measured in Chromium 153 on 25 October 2026.

Templates

Ancestors that are containers (step 2)

(sel) => { let el = document.querySelector(sel), out = [];
  for (let n = el.parentElement; n; n = n.parentElement) { const s = getComputedStyle(n);
    if (s.containerType !== 'normal' || s.containerName !== 'none') { const r = n.getBoundingClientRect();
      out.push(n.tagName.toLowerCase() + (n.className ? '.' + String(n.className).trim().split(/\s+/).join('.') : '') + ' | name ' + s.containerName + ' | type ' + s.containerType + ' | ' + Math.round(r.width) + 'x' + Math.round(r.height)); } }
  return out; }

Call it with a selector for the element, for example ('.card .title'). It prints tag names, class names and sizes only.

Measure a property at container widths (step 7)

(wrap, sel, prop, widths) => widths.map(w => { const c = document.querySelector(wrap), o = c.style.width;
  c.style.width = w + 'px'; const v = getComputedStyle(document.querySelector(sel)).getPropertyValue(prop); c.style.width = o; return w + 'px: ' + v; })

Call it as ('.card-wrap', '.card .title', 'font-size', [300, 399, 400, 600]). It restores the width after each read.

Report

Container query report: <page>, <date>
Component: <name>. Container: <selector, name, type>. Breakpoint: <width, source>.
Checker before: warnings N. After: warnings N.
Changes: <file:line  issue  fix>   (one line each)
Measured: <property at widths, before and after>
Left as is: <warning  reason>
Not tested: <items>

Starting prompt

Use the container-queries skill. Stylesheets and markup: <list>. Component: <name>, in slots of <widths>.
It should <change> when its container is narrower than <width, or "ask me">. Edit on a branch in batches of one component or 20 rules and show each diff first.

Failure modes and anti-patterns

MistakeHow to spot itDo instead
Styling the container with its own queryself-style, and the rule never appliesQuery a child, or add a wrapper
Naming a container and writing no typename-no-typecontainer: name / inline-size
container-type: size without a heightsize-no-height, and the component is 0px highinline-size, or add a height
Inline-size container in a float or an auto-width flex itemshrink-to-fit, or the browser shows 0px widthwidth: 100%, flex: 1 or a fixed width
Container on an inline, contents or table boxbad-displayA block, flex, grid or inline-block box
Copying the viewport breakpoint into the container queryA component that breaks at an arbitrary widthMeasure where the content stops fitting

Quality bar

Run every item before handing over.

  1. Checker warnings are 0, or each is listed with a reason.
  2. Every query is named, and the container it names declares a type.
  3. No rule restyles the container with its own query.
  4. The measuring snippet shows the value changing at the breakpoint and not before.
  5. The component was checked in each real slot.
  6. No file under node_modules or a vendor folder was edited.
  7. Every changed line appears in the report with file, line, issue and fix.
  8. The report lists what was not tested.

Cost

Figures are estimates from this skill's own count (words × 1.35 for prose, characters ÷ 3.5 for code and tables), not from a model tokeniser. Rival figures are estimates from the excerpts I read.

ItemTokensStatusRead condition
A. Description (always loaded)113EstimateEvery session with the skill installed
B. SKILL.md body4,463EstimateWhen the skill fires
C. references/appendix.md843EstimateOnly to check a claim or a measurement
C. references/patterns.md525EstimateOnly when writing or rebuilding a component's container and queries
Script output: cq_check.py (worked example) plus measuring snippets605EstimateEvery run. Checker output is capped with head -40, about 1,524 tokens
D. Typical run (A + B + script and snippet output)5,181EstimateCommon path: fix flagged lines, no references
D. Worst case (A + B + both references + 40 lines of output)8,068EstimateRebuilding a layout, many findings
E. Cheaper path4,956EstimateRun the checker, fix only flagged lines, measure two container widths, skip the references

Rival sizes, estimated from the excerpts I read (SKILL.md only, files they link were not sized): openskillindex container-query-architect about 350 to 400 words, roughly 475 to 540 tokens, no scripts; thedaviddias container-queries (tessl) about 230 words, roughly 310 tokens, tessl score 58; wshobson responsive-design (smithery) about 250 to 300 words, roughly 340 to 405 tokens, plus references/details.md. This skill costs more to load than all three. What it adds is a checker for the failures that silently stop a query matching, and a measuring step. On a stylesheet with no containers the checker printed 93 characters for a 235,601-character file, so the saving is the agent not reading the file. On the 12-line worked example its output is larger than the file, so on a stylesheet under about 100 lines read the file and use the ancestors snippet.

Handoffs

flexbox-grid-layout.md covers overflow and shrinking inside the container. cascade-layers.md covers a container rule that loses to another rule.

Appendix

Tool facts, measurements and sources are in references/appendix.md. Component recipes are in references/patterns.md.

Sources

Last verified 25 October 2026 for every source, except where a date says otherwise.

What this skill does

Container queries fail quietly. A container with a name and no type never matches. A query cannot style the element that is the container. A size container with no height is zero pixels high, and an inline-size container in a float or an auto-width flex item is zero pixels wide. None of these shows an error, so the component just keeps its narrow layout.

This skill gives the agent a fixed order. A read-only checker reads the stylesheets and reports nine kinds of problem. A browser snippet lists the ancestors that are containers, with their type and size. The agent then decides whether the component should use a container query or stay on a media query, declares a named container, writes the queries and measures the result at one width below the breakpoint and one at it. Every edit waits for your approval of the diff.

Who it is for

Use it if you build cards, widgets or sidebar items that sit in slots of different widths, if an @container rule does nothing, or if a component collapsed after you added container-type. It suits developers who use Claude Code, Cursor or similar agents and want the agent to measure the result instead of declaring it fixed.

It is not for page-level breakpoints, which stay on media queries, or for overflow inside the component, which the Flexbox and Grid Layout skill covers. It leaves style() and scroll-state() queries and fluid type out of this release.

What is inside

The file is 3,072 words (version 1.0.0) with one script and two reference files. It holds:

  • Seven guardrails, including measuring local pages only, no page text in the output and a breakpoint that comes from the content, never copied from the old viewport query.
  • Seven inputs the agent must have first, with the exact question for each.
  • A workflow of steps 0 to 8, a table that gives one fix per checker code and a table for choosing between container and media queries.
  • One standard-library script, scripts/cq_check.py, with nine problem codes.
  • A worked example with real output (6 warnings before, 0 after), measured in Chromium 153.
  • An ancestors snippet, a snippet that measures a property at several container widths, a report template and a starting prompt, six failure modes and an eight-item quality bar.
  • Two reference files: component recipes, and an appendix with sources and 26 browser measurements.

The token costs below are estimates from the skill’s own count: words times 1.35 for prose and characters divided by 3.5 for code and tables. They are not from a model tokeniser.

ItemTokensStatus
Description (always loaded)113Estimate
SKILL.md body (when the skill fires)4,463Estimate
references/appendix.md, read only to check a claim843Estimate
references/patterns.md, read only when writing or rebuilding a component525Estimate
Typical run (description, body, checker and snippet output)5,181Estimate
Cheaper path (checker, fix flagged lines, two container widths, no references)4,956Estimate
Worst case (both references, 40 lines of checker output)8,068Estimate

This skill costs more to load than the three rivals I could size, which are roughly 310 to 540 tokens for SKILL.md alone. What it adds is the checker and the measuring step. On a 235,601-character stylesheet with no containers the checker printed 93 characters in 0.61 seconds. On a stylesheet under about 100 lines its output is larger than the file, and the skill says to read the file instead.

How to install and use it in Claude, Cursor and other agents

Claude. Keep the folder as container-queries/ with SKILL.md, scripts/ and references/ inside, and zip it. In Claude, open Customize, then Skills, then Upload a skill. In Claude Code, put the folder in ~/.claude/skills/ or .claude/skills/ in a project.

Cursor and other agents. Put the folder where your tool reads skills, or paste the body into its rules and keep the script beside your code. Then send the starting prompt from the Templates section.

Before you install. Read the script first. It reads CSS files and writes nothing. The skill tells the agent to measure only local pages with invented data, to read sizes and computed styles and never page text, and to edit only on a branch in batches you approve. Install skills only from sources you trust.

A worked example

An invented page has a card whose title should grow at 400px, a sidebar nav that should become a row at 300px, a panel, a tag and a widget. The checker reports 6 warnings: a name with no type, a query naming a container nothing declares, a widget styled by its own query, a sidebar that collapses, a panel with no height and a tag on display: inline.

At 800px the browser agrees. The title is 16px where 24px was meant, the sidebar is 0px wide and the panel is 0px high. One batch adds a wrapper container for the card and the widget, renames the sidebar container to match its query, gives it width: 100% and gives the panel a height. The checker then reports no warnings, the title is 24px, the sidebar is 800px wide and the panel is 192px high. The title measures 16px at 399px and 24px at 400px.

Common mistakes

The skill is built around five ways this goes wrong:

  • Styling the container with its own query. The rule never applies. The skill queries a child or adds a wrapper.
  • Naming a container and writing no type. A name alone never matches. The skill writes container: name / inline-size.
  • Using container-type: size without a height. The box collapses to zero. The skill defaults to inline-size.
  • Putting the container in a float or an auto-width flex item. The width collapses to zero. The skill adds a width, flex: 1 or width: 100%.
  • Copying the viewport breakpoint. The component then breaks at an arbitrary width. The skill takes the breakpoint from the content.

Flexbox and Grid Layout covers overflow and shrinking inside the container, and Cascade Layers covers a container rule that loses to another rule. Browse the CSS and Layout set, the full Frontend Development category or the Skills-md library.

Frequently asked questions

Usually the container is not set up. A container needs a type as well as a name, and the element you restyle must be inside it, not the container itself. In Chromium 153 a name with no type never matched. The skill’s checker flags the missing type, unknown names, self-styling queries and containers on display values that cannot be containers.

A query measures the container, so it can only restyle descendants. In Chromium 153 a rule on the container did not apply, while the same rule on a child did, and so did a rule on the container’s ::before. Put the container on a wrapper or a parent and restyle the element inside. The checker flags this as self-style.

A size container ignores its content for height, so with no height it measured 0px high in Chromium 153. An inline-size container in a float, an inline-block or an auto-width flex item measured 0px wide. Give it a width, flex: 1 or width: 100%, or use inline-size and let height follow the content.

Use inline-size unless you query height. It contains the width only, so height still follows the content. Choose size when a rule depends on height, and then give the container a height, block-size or aspect-ratio. A height query on an inline-size container did not match in Chromium 153, which the checker reports as height-query-inline-size.

Use container queries for components that sit in slots of different widths, such as a card in a main column and a sidebar. Keep media queries for the page grid, the header and preference queries. A page shell can use media queries while the components inside it use container queries. The skill’s step 3 holds the decision table.

They fall back to the small viewport units. In Chromium 153, 50cqw gave 500px in a 1000px viewport and 200px inside a 400px container. The checker reports container units as info when no container is declared in the files you pass. Wrap them in clamp() so a very narrow or wide container cannot make them extreme.

The description is about 113 tokens and loads in every session. The SKILL.md body is about 4,500 tokens when the skill fires. A typical run is about 5,200 tokens with checker output, the cheapest path is about 5,000 and the worst case is about 8,100. All figures are estimates, not from a model tokeniser.

Changelog

  • 1.0.0, 25 October 2026: first release.

Use, adapt and share this skill under CC BY 4.0: credit Afeez Adebayo and link to this page.