CSS Container Queries: Basics

Introduction to Container Queries

CSS Container Queries represent a significant evolution in responsive design, allowing elements to adapt based on their parent container's size rather than just the viewport size.

The Problem with Media Queries

Traditional responsive design relies on media queries, which have limitations:

  • Viewport-centric: Media queries only respond to the overall viewport size
  • Component reusability: Components can't adapt to their immediate context
  • Layout flexibility: The same component might need different styles in different containers
Key Insight: Container queries enable truly modular, context-aware components that can adapt to their container's size, not just the viewport size. This makes components more reusable across different layouts and contexts.

Media Queries vs. Container Queries

Feature Media Queries Container Queries
Responds to Viewport dimensions Container dimensions
Scope Global (entire document) Local (specific containers)
Component reusability Limited (fixed breakpoints) High (adapts to container)
Use case Page-level layout changes Component-level adaptations

Basic Syntax and Usage

Defining Container Elements

Before using container queries, you need to define which elements should be considered containers:


/* Define a container */
.card-container {
  container-type: inline-size;  /* Enable container queries for inline axis */
  container-name: card;         /* Optional: Name the container for targeting */
}

/* Alternative container types */
.block-container {
  container-type: block-size;   /* Enable container queries for block axis */
}

.full-container {
  container-type: size;         /* Enable container queries for both axes */
}

/* Shorthand syntax */
.named-container {
  container: card / inline-size;  /* name / type */
}
                        

Container Query Syntax

Once you've defined containers, you can write queries that target elements inside them:


/* Basic container query */
@container (min-width: 400px) {
  .card-title {
    font-size: 1.5rem;
  }
  
  .card-image {
    float: left;
    width: 40%;
  }
}

/* Target a named container */
@container card (min-width: 600px) {
  .card-content {
    columns: 2;
  }
}

/* Multiple conditions */
@container (min-width: 300px) and (max-width: 500px) {
  .card-actions {
    flex-direction: column;
  }
}
                        
Important: Container queries only apply to descendants of the container element. The container itself cannot be styled based on its own size using container queries.

Container Query Units

Available Units

Container queries introduce new relative units that are based on the container's dimensions:

Unit Description Example
cqw 1% of container's width font-size: 2cqw;
cqh 1% of container's height margin-top: 3cqh;
cqi 1% of container's inline size padding-inline: 2cqi;
cqb 1% of container's block size margin-block: 1cqb;
cqmin 1% of container's smaller dimension border-radius: 1cqmin;
cqmax 1% of container's larger dimension max-width: 80cqmax;

Using Container Query Units


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

.card-title {
  /* Font size will be 4% of container width */
  font-size: clamp(1rem, 4cqw, 2rem);
}

.card-image {
  /* Image takes 50% of container width */
  width: 50cqw;
  /* Height maintains aspect ratio */
  height: auto;
}

.card-content {
  /* Padding scales with container size */
  padding: 2cqw;
}

/* These units work even without @container queries */
                        
Pro Tip: Container query units are perfect for creating fluid typography and spacing that scales with the container size, similar to how viewport units (vw, vh) scale with the viewport.

Practical Examples

Responsive Card Component


/* HTML */
<div class="card-container">
  <div class="card">
    <img class="card-image" src="image.jpg" alt="Card image">
    <div class="card-body">
      <h3 class="card-title">Card Title</h3>
      <p class="card-text">Card description text goes here...</p>
      <div class="card-actions">
        <button class="btn-primary">Action</button>
        <button class="btn-secondary">Cancel</button>
      </div>
    </div>
  </div>
</div>

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

.card {
  display: flex;
  flex-direction: column;
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
}

.card-image {
  width: 100%;
  height: auto;
  object-fit: cover;
}

.card-body {
  padding: 1rem;
}

.card-title {
  font-size: 1.25rem;
  margin-bottom: 0.5rem;
}

.card-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 1rem;
}

/* Container queries for responsive adaptations */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
  }
  
  .card-image {
    width: 40%;
  }
  
  .card-body {
    flex: 1;
  }
}

@container card (min-width: 600px) {
  .card-title {
    font-size: 1.5rem;
  }
  
  .card-text {
    columns: 2;
    column-gap: 1.5rem;
  }
}
                        

Responsive Navigation


/* HTML */
<div class="nav-container">
  <nav class="main-nav">
    <div class="nav-brand">Logo</div>
    <ul class="nav-menu">
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Services</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
    <button class="nav-toggle">Menu</button>
  </nav>
</div>

/* CSS */
.nav-container {
  container-type: inline-size;
  container-name: nav;
}

.main-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  background-color: #f8f9fa;
}

.nav-brand {
  font-weight: bold;
  font-size: 1.25rem;
}

.nav-menu {
  display: none;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  margin-top: 1rem;
}

.nav-menu li {
  margin-bottom: 0.5rem;
}

.nav-toggle {
  display: block;
}

/* Container queries for responsive navigation */
@container nav (min-width: 600px) {
  .nav-menu {
    display: flex;
    width: auto;
    margin-top: 0;
    gap: 1.5rem;
  }
  
  .nav-menu li {
    margin-bottom: 0;
  }
  
  .nav-toggle {
    display: none;
  }
}
                        

Browser Support and Fallbacks

Current Browser Support

Container queries are a relatively new feature, but support is growing:

  • Chrome: Supported since version 105
  • Firefox: Supported since version 110
  • Safari: Supported since version 16
  • Edge: Supported since version 105

Feature Detection


/* CSS feature detection */
@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
    container-name: card;
  }
  
  /* Container queries here */
}

/* JavaScript feature detection */
if (CSS.supports('container-type', 'inline-size')) {
  console.log('Container queries are supported!');
} else {
  console.log('Container queries are not supported.');
  // Apply fallback behavior
}
                        

Fallback Strategies


/* Strategy 1: Start with mobile-first default styles */
.card {
  display: flex;
  flex-direction: column;
}

/* Add media queries as first-level fallback */
@media (min-width: 768px) {
  .card {
    flex-direction: row;
  }
}

/* Then enhance with container queries */
@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
  }
  
  /* Override media query styles with container queries */
  @container (min-width: 400px) {
    .card {
      flex-direction: row;
    }
  }
}

/* Strategy 2: Use JavaScript for older browsers */
if (!CSS.supports('container-type', 'inline-size')) {
  // Add resize observer to mimic container queries
  const containers = document.querySelectorAll('.card-container');
  
  containers.forEach(container => {
    const resizeObserver = new ResizeObserver(entries => {
      for (let entry of entries) {
        const width = entry.contentRect.width;
        const card = entry.target.querySelector('.card');
        
        if (width >= 400) {
          card.classList.add('card-wide');
        } else {
          card.classList.remove('card-wide');
        }
      }
    });
    
    resizeObserver.observe(container);
  });
}
                        

Best Practices

When to Use Container Queries

  • Reusable components that appear in different layout contexts
  • Components with variable width constraints
  • Card layouts, grids, and flexible content areas
  • Widgets and UI components that need to adapt to their container

When to Use Media Queries

  • Page-level layout changes that affect the overall structure
  • Navigation patterns that depend on screen size
  • Device-specific adaptations (e.g., touch vs. mouse interfaces)
  • Print styles and other media-specific styles

Performance Considerations

  • Limit container nesting: Avoid deeply nested containers when possible
  • Be mindful of reflows: Container queries can trigger layout recalculations
  • Use containment where appropriate: The contain property can improve performance
Pro Tip: Container queries and media queries complement each other. Use container queries for component-level adaptations and media queries for page-level layout changes.

Next Steps

Now that you understand the basics of CSS Container Queries, you can explore more advanced topics in our CSS Container Queries: Advanced guide, including:

  • Style queries and container query value types
  • Complex layout adaptations with container queries
  • Combining container queries with CSS Grid and Flexbox
  • Advanced component examples and patterns
  • Performance optimization techniques