Home / Snippets / Layout /

Stat card with container queries

A stats card that adapts from a compact vertical stack to a horizontal row using container queries — no media queries needed.

Narrow container — vertical stack
📦
1,284
Total orders
Wide container — horizontal row
📦
1,284
Total orders
Widely Supported
layoutcontainer-queriesno-js

Quick implementation

/* 1. Make the wrapper a container */
.stat-card-wrap {
  container-type: inline-size;
}

/* 2. Default: compact vertical stack */
.stat-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: oklch(0.19 0.02 260);
  border-radius: 0.5rem;
  padding: 1.25rem;
}

.stat-card__icon {
  font-size: 1.5rem;
  line-height: 1;
}

.stat-card__number {
  font-size: 2rem;
  font-weight: 800;
  color: oklch(0.72 0.19 265);
  line-height: 1;
}

.stat-card__label {
  font-size: 0.875rem;
  color: oklch(0.63 0.02 260);
}

/* 3. Wide container: switch to horizontal row */
@container (min-width: 300px) {
  .stat-card {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
  }

  .stat-card__icon {
    font-size: 2rem;
    flex-shrink: 0;
  }

  .stat-card__number {
    font-size: 1.75rem;
  }
}

Prompt this to your LLM

Includes role, constraints, layout variants, and edge cases to handle.

You are a senior frontend engineer building a responsive stat card
component using CSS container queries.

Goal: Create a stat card that displays an icon, a large number, and a
label. In a narrow container it stacks vertically; in a container
wider than 300px it switches to a horizontal row layout — all without
media queries.

Technical constraints:
- The wrapper element gets container-type: inline-size so its
  children can query the wrapper's own width.
- Do NOT use media queries — use @container queries only.
- Default (mobile-first) layout is flex-direction: column.
- At @container (min-width: 300px) switch to flex-direction: row
  with align-items: center.
- Use oklch() for all color values — no hex or rgba.
- No JavaScript required.

Design requirements:
- The stat number should be visually prominent (large font, accent color).
- The icon should shrink-proof (flex-shrink: 0) in the row layout.
- Maintain readable padding in both layouts.
- The card background should use a slightly lighter tone than the
  page background so it reads as an elevated surface in dark mode.

Edge cases to handle:
- Very long numbers (e.g. "1,000,000") must not overflow — use
  overflow: hidden or text-overflow: ellipsis on the number element.
- If multiple stat cards are placed in a CSS grid, each wrapper
  becomes its own container context so each card responds to its
  own column width independently.
- container-name is optional but useful when nested containers exist
  — add a comment showing how to use named containers.

Return CSS only.

Container query stat cards explained

A stat card is a UI element that surfaces a single key metric — an icon, a prominent number, and a short label. The challenge is that stat cards live in wildly different contexts: a full-width hero section, a two-column grid, a sidebar widget, or a narrow mobile drawer. With media queries, you'd write breakpoints tied to the viewport width, which breaks down when the same card component is reused across different layout regions.

Container queries solve this by letting the card respond to the width of its own container element rather than the viewport. Wrap the card in a container-type: inline-size element and write @container (min-width: 300px) rules. Now the card's layout adapts to the space it actually has — not the space the page has. Drop the same component into a sidebar or a wide dashboard panel and it just works.

The pattern

The key is the wrapper element. It carries container-type: inline-size which establishes a containment context. The card itself is just a flex container defaulting to flex-direction: column for the compact stacked layout. Inside @container (min-width: 300px), the flex direction switches to row and alignment adjusts to center the icon with the text block.

Using flex-shrink: 0 on the icon in the row layout prevents it from squishing when the number text is long. The number element also benefits from line-height: 1 so the card height stays compact and the vertical rhythm is consistent between the column and row variants.

Because the wrapper is the container context, you can nest stat cards inside any grid or flex layout and each card will independently respond to its own column width. No specificity tricks or modifier classes needed.

Stacking multiple stat cards

A common pattern is a dashboard row of four stat cards in a CSS grid. Give the grid a grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)) rule so cards wrap at smaller viewport widths. Each card's wrapper becomes its own container context, so as columns narrow the cards automatically flip from row layout back to vertical stack — no additional CSS needed.

If you want named container contexts (useful when containers are nested), add a container-name alongside container-type and reference it in the query: @container stat (min-width: 300px). This prevents inner containers from accidentally matching outer @container rules in deeply nested component trees.

Browser support

Container queries (container-type and @container) landed in all major browsers in 2023 and are now widely supported. Chrome 105+, Firefox 110+, and Safari 16+ all ship full support. For older browsers that don't support container queries, the component gracefully falls back to the default column layout — which is the compact stat card design, a perfectly usable fallback.

If you need to support older browsers with a polyfill, the container-query-polyfill package from Google Chrome Labs provides a JavaScript-based fallback. For most production sites today, container queries can be used without a polyfill given the high browser coverage.