Home / Snippets / Layout /

Center with container-query-aware technique

Use container queries to switch between block centering and flex centering depending on available space — a responsive centering pattern that adapts to its context.

container-type: inline-size — centering strategy switches at 400px
Centered card
Flex centering above 400px, block margin centering below

Resize the browser to see the centering strategy change.

Widely Supported
layoutno-js

Quick implementation

/* Establish the container query context */
.cq-center-wrap {
  container-type: inline-size;
}

/* Default: flex centering at wider container widths */
.cq-center-card {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 10rem;
}

/* Below 400px: switch to block + margin-based centering */
@container (max-width: 400px) {
  .cq-center-card {
    display: block;
    margin-inline: auto;
    text-align: center;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building container-query-aware
centering patterns using modern CSS.

Goal: Create a centering pattern that uses container queries to switch
the centering strategy based on the available container width — using
flexbox centering at wider containers and block margin centering at
narrower ones. The component should adapt automatically when placed in
any layout context (full page, sidebar, modal, grid card) without
per-placement CSS overrides.

Technical constraints:
- The wrapper element gets container-type: inline-size to establish
  the query context. This allows @container rules to respond to the
  wrapper's computed inline size rather than the viewport width.
- The default state (wider containers) uses display: flex with
  justify-content: center and align-items: center for reliable
  two-axis centering of any child element.
- The @container (max-width: 400px) block switches to display: block
  with margin-inline: auto and text-align: center for block-level
  centering — appropriate when the container is too narrow for flex
  centering to add value over simpler margin-based approaches.
- Use oklch() for any color values — no hex or rgba.
- No JavaScript required.

Framework variant (pick one):
A) Vanilla CSS utility classes .cq-center-wrap (establishes the
   container) and .cq-center-card (handles the centering with the
   @container switch). Both are composable on any element pair.
B) React component — ContainerAwareCenter accepts children,
   breakpoint (string, default "400px"), and className props; renders
   a wrapper div with container-type: inline-size and a content div
   that switches centering strategy via CSS module @container rule.

Edge cases to handle:
- container-type: inline-size prevents the container from sizing
  itself to its children's inline sizes. If the wrapper needs to
  shrink-wrap to content, add an explicit width or max-width, or
  use container-type: size (which queries both axes but also prevents
  sizing from both axes).
- Nested containers: if .cq-center-wrap is inside another element
  with container-type, @container queries inside it match against
  .cq-center-wrap itself — the nearest ancestor with a container-type.
  This is correct behavior; name containers with container-name if
  you need to query a specific ancestor.
- The @container breakpoint (400px here) uses px units. Container
  query breakpoints resolve against the container's computed size in
  px — em and rem units in @container resolve against the root font
  size, not the container's font size.
- When both centering strategies produce identical visual output
  (e.g., a single short text node centered in a narrow space),
  the switch is still correct — keeping the block fallback ensures
  the layout works even if JavaScript or CSS partially fails to load.

Return CSS only (or a React component if variant B is chosen).

Why container queries change the centering problem

Before container queries, every responsive centering decision was tied to the viewport. A media query like @media (max-width: 400px) fires at a specific viewport width, regardless of whether the component is 400px wide because the viewport is narrow or because it is sitting in a 400px sidebar column inside a 1440px viewport. The same component in different layout contexts required separate CSS overrides for each context — a maintenance problem that grew with every new placement.

Container queries decouple the breakpoint from the viewport. container-type: inline-size on a wrapper element tells the browser to track that element's inline size. The @container (max-width: 400px) block fires when that specific wrapper falls below 400px — independent of the viewport. The same component placed in a full-width page hero, a narrow sidebar, or a modal automatically selects the right centering strategy without any extra CSS.

For centering specifically, this means a card component can use flex centering at wide container widths (where the card has meaningful space to center within) and switch to block margin centering at narrow widths (where the card may fill the container width and flex centering adds no value). The switch is automatic, context-aware, and requires zero JavaScript.

The pattern explained

The pattern uses two elements. The outer wrapper gets container-type: inline-size, which establishes it as a query container. The inner card element gets the centering styles. In the default state — when the container is wider than 400px — the card uses display: flex; justify-content: center; align-items: center, which centers its content on both axes within the available flex space.

When the container width drops below 400px, the @container block overrides the card to display: block; margin-inline: auto; text-align: center. Block display with margin-inline: auto horizontally centers a block with a constrained width using the classic margin-auto technique. text-align: center handles inline content within the block. This is a simpler, more predictable centering approach for narrow spaces where flex centering would create awkward stretched layouts.

The 400px breakpoint is a guideline. Adjust it to the width at which your specific design benefits from switching strategy. For components with more complex internal structure, multiple breakpoints can progressively change the layout at different container sizes.

Combining with the :has() selector for contextual centering

Container queries and :has() can be combined for even more context-aware centering behavior. For example, a card that contains an image might center its text content differently than a card without an image. The :has(img) selector detects the presence of an image and applies different centering rules — without any JavaScript class toggling.

A pattern like .cq-center-card:has(img) { align-items: flex-start; } inside a @container (min-width: 400px) block overrides the default centered alignment for cards that contain images at wider widths, keeping images at the start of the flex axis while text wraps alongside. The :has() selector is evaluated at render time based on DOM content, and the container query breakpoint is evaluated based on layout size — together they enable centering decisions that respond to both content and context simultaneously.

Browser support for :has() is now broad across modern browsers. If combining both features, verify that your target browser set supports both container queries and :has() before shipping to production.

Browser support

CSS container queries (container-type and @container) are supported in all major browsers as of Chrome 105, Safari 16, Firefox 110, and Edge 105. Global browser support is above 90% as of early 2026. For most production projects, container queries can be used without a fallback.

In browsers without container query support, the @container block is silently ignored. The default state — flex centering at all widths — remains in effect. For the majority of use cases this is a perfectly acceptable fallback: the component stays visually centered, just without the narrow-width strategy switch. If the narrow behavior is critical, a @media (max-width: 400px) fallback targeting small viewport widths provides an approximate equivalent for those browsers, with the caveat that it fires on viewport width rather than container width.