CSS · 02 of 05

Responsive, wherever it lives

A component responds to its own space. The compact layout is the usable base.

@containerBaseline Widely availableMDN size container queries
Experience kept
The same compact and expanded card layouts
Machinery removed
Viewport coupling and parent-specific overrides
Decision
Let the component respond to its container
Live demo

Slide to change the container width.

THE REDUCTION

Same outcome. Less to coordinate.

A viewport breakpoint answers the wrong question for a component that moves between a page and a sidebar.

CURRENT PROJECT · CSS
/* Wrong in the sidebar: the viewport is wide, the card is not. */
@media (min-width: 640px) {
  .resource-card {
    grid-template-columns: 96px 1fr;
  }
}
WITH FEWER PARTS · CSS
.card-slot {
  container-type: inline-size;
}

@container (min-width: 320px) {
  .resource-card {
    grid-template-columns: 96px 1fr;
  }
}

The single-column card remains the base layout, so the component is still right in a slot that never reaches the threshold.

ABOUT THE PATTERN

Why this works

The card is portable because its layout depends on the space it receives, not the viewport around it.

Decisions that matter

  • Use inline-size containment on a wrapper, query descendants, and keep the single-column base usable.
  • The slider only demonstrates changing width.
  • Style/scroll-state queries need separate support checks.

Read from the skill's own reference files at build time.

COPY, ADAPT, SHIP

The dependency-free implementation

HTML, CSS, and the JavaScript the interaction actually needs

STANDALONE HTML
<!doctype html>
<html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Container-aware card</title>
<style id="recipe">
.card-container { container-type: inline-size; }
.card { display: grid; gap: 1rem; }
@container (min-width: 320px) {
  .card { grid-template-columns: 4rem 1fr; align-items: center; }
}
</style>
<style>
*{box-sizing:border-box}body{margin:0;padding:28px;font:14px/1.5 system-ui;color:#182334;background:#f2f5f9}label{display:block;margin-bottom:10px}input{width:100%;accent-color:#3866e8}.card-container{max-width:100%;margin:24px auto 0}.card{background:white;border:1px solid #dce2eb;border-radius:14px;padding:18px;box-shadow:0 10px 25px #1e32500a}.icon{display:grid;place-items:center;width:56px;height:56px;background:#e4edff;border-radius:12px;color:#2f5ccc;font-size:24px}.card h2{font-size:16px;margin:0}.card p{margin:4px 0 0;color:#647186}input:focus-visible{outline:3px solid #3866e8;outline-offset:4px}
</style>
<label for="width">Container width <output id="value">100%</output></label>
<input id="width" type="range" min="55" max="100" value="100">
<div class="card-container"><article class="card"><span class="icon" aria-hidden="true">↗</span><div><h2>Room to adapt</h2><p>Same component.<br>A different amount of space.</p></div></article></div>
<script>
// Demo control changes the container; CSS handles its layout.
const slider = document.querySelector('#width');
slider.addEventListener('input', () => {
  document.querySelector('.card-container').style.width = slider.value + '%';
  document.querySelector('#value').value = slider.value + '%';
});
</script></html>
Using Tailwind CSS v4 already? View the adapted example

Optional integration · adapt utilities to your project tokens

TAILWIND HTML
<!-- Tailwind CSS v4. Container queries are built in. -->
<label for="width" class="block text-sm font-medium text-slate-900">
  Container width <output id="value">100%</output>
</label>
<input
  id="width"
  type="range"
  min="55"
  max="100"
  value="100"
  class="mt-2 w-full accent-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
>

<div data-demo-container class="@container mt-6 max-w-full">
  <article class="grid min-w-0 gap-4 rounded-xl border border-slate-200 bg-white p-4 shadow-sm @[320px]:grid-cols-[4rem_minmax(0,1fr)] @[320px]:items-center">
    <span aria-hidden="true" class="grid size-14 place-items-center rounded-xl bg-blue-50 text-2xl text-blue-700">↗</span>
    <div class="min-w-0">
      <h2 class="font-semibold text-slate-900">Room to adapt</h2>
      <p class="mt-1 text-sm text-slate-600">Same component. A different amount of space.</p>
    </div>
  </article>
</div>

<script>
  // Demo control only; the Tailwind container variants handle the card layout.
  const slider = document.querySelector('#width');
  const container = document.querySelector('[data-demo-container]');
  const output = document.querySelector('#value');

  slider.addEventListener('input', () => {
    container.style.width = `${slider.value}%`;
    output.value = `${slider.value}%`;
  });
</script>