CSS Container Query in Practice: 5 Patterns for Component-Level Responsive Layouts

前端工程

Container Queries: A Paradigm Shift in Responsive Design

Media queries (@media) respond to viewport width—the same component looks identical in a sidebar and main content area, unable to adapt to its own container. CSS Container Queries (@container) let components respond to their parent container's size, enabling true "component-level responsiveness." In 2026, Container Queries are supported by all major browsers, Container Query Units (cqw/cqh) are the new responsive units, and component libraries no longer need breakpoint Props.

This article walks through 5 core patterns, covering the full pipeline from container definition → component queries → container units → nested containers → practical components.


Core Concepts

Concept Description
@container CSS query rule based on container size
container-type Defines container type (inline-size/size/normal)
container-name Names a container for precise querying
cqw/cqh Container query width/height units (1cqw = 1% of container width)
container Shorthand for container-type and container-name
inline-size Container type querying only inline direction (width)
size Container type querying both width and height
style() Query based on container CSS custom properties

Problem Analysis: 5 Challenges in Container Queries

  1. Container type selection: inline-size vs size performance impact
  2. Nested containers: Priority and performance of multi-level container queries
  3. Fallback strategy: Degradation for browsers without container query support
  4. Working with media queries: When to use container queries vs media queries
  5. Container query units: Mixing cqw/cqh with rem/vw

Step-by-Step: 5 Container Query Patterns

Pattern 1: Basic Container Queries

/* Define container */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Default: narrow container */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
}

.card-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.card-content {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Wide container: horizontal layout */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card-image {
    width: 200px;
    aspect-ratio: 1;
    flex-shrink: 0;
  }

  .card-content {
    flex: 1;
  }
}

/* Wider container: large card */
@container card (min-width: 600px) {
  .card {
    padding: 1.5rem;
    gap: 1.5rem;
  }

  .card-image {
    width: 280px;
  }

  .card-title {
    font-size: 1.5rem;
  }
}

Pattern 2: Container Query Units

/* Fluid typography with container query units */
.text-container {
  container-type: inline-size;
}

.fluid-heading {
  font-size: clamp(1.2rem, 5cqw, 3rem);
  line-height: 1.2;
}

.fluid-body {
  font-size: clamp(0.875rem, 2.5cqw, 1.125rem);
  line-height: 1.6;
}

.fluid-spacing {
  padding: clamp(0.75rem, 3cqw, 2rem);
  gap: clamp(0.5rem, 2cqw, 1.5rem);
}

/* Container query + clamp for breakpoint-free responsiveness */
.adaptive-grid {
  container-type: inline-size;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: clamp(0.75rem, 2cqw, 1.5rem);
}
/* Responsive typography system */
:root {
  --font-scale: 1;
}

.typography-container {
  container-type: inline-size;
}

@container (min-width: 300px) {
  .typography-container { --font-scale: 1.1; }
}

@container (min-width: 500px) {
  .typography-container { --font-scale: 1.25; }
}

@container (min-width: 700px) {
  .typography-container { --font-scale: 1.4; }
}

.responsive-text {
  font-size: calc(1rem * var(--font-scale));
}

Pattern 3: Named Containers & Nesting

/* Named containers: precise query targeting */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

.main-content {
  container-type: inline-size;
  container-name: main;
}

.widget {
  container-type: inline-size;
  container-name: widget;
}

/* Navigation inside sidebar */
@container sidebar (min-width: 250px) {
  .nav-item {
    flex-direction: row;
    gap: 0.75rem;
  }

  .nav-icon {
    width: 20px;
  }

  .nav-label {
    display: block;
    font-size: 0.875rem;
  }
}

@container sidebar (max-width: 249px) {
  .nav-item {
    flex-direction: column;
    align-items: center;
  }

  .nav-label {
    font-size: 0.75rem;
  }
}

/* Nested container: widget inside sidebar */
@container widget (min-width: 200px) {
  .widget-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@container widget (max-width: 199px) {
  .widget-grid {
    grid-template-columns: 1fr;
  }
}

Pattern 4: Container Queries + Component Library

/* Component library: responsive components without breakpoint Props */
.btn-container {
  container-type: inline-size;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  font-size: 0.875rem;
  white-space: nowrap;
}

.btn-icon {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

.btn-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Narrow container: icon only */
@container (max-width: 100px) {
  .btn {
    padding: 0.5rem;
  }

  .btn-label {
    display: none;
  }
}

/* Medium container: icon + short text */
@container (min-width: 101px) and (max-width: 200px) {
  .btn {
    padding: 0.5rem 0.75rem;
  }

  .btn-label {
    max-width: 6ch;
  }
}

/* Wide container: full display */
@container (min-width: 201px) {
  .btn {
    padding: 0.5rem 1.25rem;
  }
}
/* Responsive form component */
.form-field-container {
  container-type: inline-size;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

@container (min-width: 400px) {
  .form-field {
    flex-direction: row;
    align-items: center;
  }

  .form-label {
    width: 120px;
    flex-shrink: 0;
    text-align: right;
  }

  .form-input {
    flex: 1;
  }
}

Pattern 5: Container Style Queries (@container style())

/* Style queries based on custom properties */
.theme-container {
  container-type: inline-size;
  --theme: light;
}

.theme-container[data-theme="dark"] {
  --theme: dark;
}

/* Switch styles based on theme variable */
@container style(--theme: dark) {
  .themed-card {
    background: #1f2937;
    color: #f9fafb;
    border-color: #374151;
  }

  .themed-card:hover {
    background: #111827;
  }
}

@container style(--theme: light) {
  .themed-card {
    background: #ffffff;
    color: #111827;
    border-color: #e5e7eb;
  }

  .themed-card:hover {
    background: #f9fafb;
  }
}

/* Switch based on layout variable */
.layout-container {
  container-type: inline-size;
  --layout: compact;
}

.layout-container[data-layout="comfortable"] {
  --layout: comfortable;
}

@container style(--layout: compact) {
  .list-item {
    padding: 0.5rem;
    gap: 0.5rem;
  }
}

@container style(--layout: comfortable) {
  .list-item {
    padding: 1rem;
    gap: 1rem;
  }
}

Pitfall Guide

Pitfall 1: Forgetting container-type

/* ❌ Wrong: No container type defined, @container won't work */
.parent { }
@container (min-width: 400px) { .child { ... } }

/* ✅ Correct: Define container type first */
.parent { container-type: inline-size; }
@container (min-width: 400px) { .child { ... } }

Pitfall 2: Performance Issues with size Type

/* ❌ Wrong: Using size when height query isn't needed */
.container { container-type: size; }

/* ✅ Correct: Use inline-size for width-only queries */
.container { container-type: inline-size; }

Pitfall 3: Container Query vs Media Query Conflicts

/* ❌ Wrong: Container query and media query overriding each other */
@media (min-width: 768px) { .card { flex-direction: row; } }
@container (min-width: 400px) { .card { flex-direction: column; } }

/* ✅ Correct: Media queries for layout framework, container queries for component internals */
@media (min-width: 768px) { .sidebar { width: 300px; } }
@container (min-width: 400px) { .card { flex-direction: row; } }

Pitfall 4: Container Query Unit Overflow

/* ❌ Wrong: cqw without clamp, overflow in extreme cases */
.title { font-size: 5cqw; }

/* ✅ Correct: Use clamp to limit range */
.title { font-size: clamp(1rem, 5cqw, 3rem); }

Pitfall 5: Nested Container Query Targeting Wrong Element

/* ❌ Wrong: Unnamed containers, query matches wrong ancestor */
.outer { container-type: inline-size; }
.inner { container-type: inline-size; }
@container (min-width: 400px) { .item { ... } }  /* Matches inner */

/* ✅ Correct: Use named containers for precise matching */
.outer { container-type: inline-size; container-name: outer; }
.inner { container-type: inline-size; container-name: inner; }
@container outer (min-width: 400px) { .item { ... } }

Error Troubleshooting

# Error Message Cause Solution
1 @container not working container-type not set Add container-type: inline-size
2 cqw unit not applied Browser doesn't support Check browser version or add fallback
3 Container query matches wrong element Unnamed nested containers Use container-name to distinguish
4 Layout shift on container change Container size change causes reflow Add transition for smooth change
5 Performance degradation container-type: size overhead Switch to inline-size
6 style() query not supported Browser doesn't support style queries Use data-attribute alternative
7 Container query ignored in Shadow DOM Shadow DOM boundary issue Define container inside Shadow DOM
8 Flex item container not queried Flex child width uncertain Set min-width: 0
9 Container query breaks grid Grid child container width uncertain Use minmax() constraint
10 Safari container query bug Safari-specific version bug Update Safari or add polyfill

Advanced Optimization

  1. Breakpoint-Free Design: Replace fixed breakpoints with cqw + clamp for fluid responsiveness
  2. Component Tokens: Container queries + CSS custom properties for component-level theming
  3. Container Query Polyfill: Use Container Query Polyfill for older browser support
  4. PostCSS Transforms: PostCSS plugins auto-generate media query fallbacks
  5. Design System Integration: Component libraries uniformly use container queries instead of breakpoint Props

Comparison

Dimension Container Queries Media Queries Element Queries JS Resize
Query Target Parent container Viewport Element itself Element itself
CSS Native
Performance ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐
Component-Level
Nesting Support
Browser Support Mainstream All Needs Polyfill All

Summary: CSS Container Queries evolve responsive design from "page-level" to "component-level"—components no longer depend on viewport width but adapt to their own container space. inline-size containers + named containers + cqw units form a trinity that lets component libraries completely eliminate breakpoint Props. In 2026, container queries are the new standard for responsive design, working alongside media queries: media queries for page layout, container queries for component internals.


Try these browser-local tools — no sign-up required →

#容器查询#响应式设计#CSS Container Query#组件级响应#2026#前端工程