Back to Blog
CSSContainer QueriesResponsiveLayout

CSS Container Queries Guide

Master CSS container queries. From basic usage to responsive components to practical patterns.

B
Bootspring Team
Engineering
September 9, 2021
6 min read

Container queries enable component-based responsive design. Here's how to use them effectively.

Basic Container Queries#

1/* Define a container */ 2.card-container { 3 container-type: inline-size; 4 container-name: card; 5} 6 7/* Query the container */ 8@container card (min-width: 400px) { 9 .card { 10 display: flex; 11 gap: 1rem; 12 } 13 14 .card-image { 15 flex: 0 0 150px; 16 } 17} 18 19/* Shorthand */ 20.card-container { 21 container: card / inline-size; 22} 23 24/* Anonymous container (no name) */ 25.wrapper { 26 container-type: inline-size; 27} 28 29@container (min-width: 300px) { 30 .content { 31 font-size: 1.25rem; 32 } 33}

Container Types#

1/* inline-size: Query width only (most common) */ 2.sidebar { 3 container-type: inline-size; 4} 5 6/* size: Query both width and height */ 7.modal { 8 container-type: size; 9} 10 11@container (min-height: 400px) { 12 .modal-content { 13 padding: 2rem; 14 } 15} 16 17/* normal: No size containment (for style queries) */ 18.theme-container { 19 container-type: normal; 20 container-name: theme; 21}

Responsive Card Component#

1.card-wrapper { 2 container: card / inline-size; 3} 4 5/* Default: vertical layout */ 6.card { 7 display: flex; 8 flex-direction: column; 9} 10 11.card-image { 12 width: 100%; 13 aspect-ratio: 16 / 9; 14 object-fit: cover; 15} 16 17.card-content { 18 padding: 1rem; 19} 20 21.card-title { 22 font-size: 1rem; 23} 24 25/* Medium: horizontal layout */ 26@container card (min-width: 400px) { 27 .card { 28 flex-direction: row; 29 } 30 31 .card-image { 32 width: 40%; 33 aspect-ratio: 1; 34 } 35 36 .card-content { 37 flex: 1; 38 display: flex; 39 flex-direction: column; 40 justify-content: center; 41 } 42 43 .card-title { 44 font-size: 1.25rem; 45 } 46} 47 48/* Large: enhanced layout */ 49@container card (min-width: 600px) { 50 .card { 51 gap: 2rem; 52 } 53 54 .card-image { 55 width: 50%; 56 } 57 58 .card-title { 59 font-size: 1.5rem; 60 } 61 62 .card-meta { 63 display: flex; 64 gap: 1rem; 65 } 66}
1.nav-container { 2 container: nav / inline-size; 3} 4 5.nav { 6 display: flex; 7 align-items: center; 8} 9 10.nav-links { 11 display: none; 12} 13 14.nav-menu-button { 15 display: block; 16} 17 18/* Show links when container is wide enough */ 19@container nav (min-width: 600px) { 20 .nav-links { 21 display: flex; 22 gap: 1rem; 23 } 24 25 .nav-menu-button { 26 display: none; 27 } 28} 29 30/* Enhanced layout for larger containers */ 31@container nav (min-width: 900px) { 32 .nav { 33 justify-content: space-between; 34 } 35 36 .nav-links { 37 gap: 2rem; 38 } 39 40 .nav-actions { 41 display: flex; 42 gap: 1rem; 43 } 44}

Grid Component#

1.grid-container { 2 container: grid / inline-size; 3} 4 5.grid { 6 display: grid; 7 gap: 1rem; 8 grid-template-columns: 1fr; 9} 10 11@container grid (min-width: 400px) { 12 .grid { 13 grid-template-columns: repeat(2, 1fr); 14 } 15} 16 17@container grid (min-width: 600px) { 18 .grid { 19 grid-template-columns: repeat(3, 1fr); 20 } 21} 22 23@container grid (min-width: 900px) { 24 .grid { 25 grid-template-columns: repeat(4, 1fr); 26 gap: 1.5rem; 27 } 28}

Container Query Units#

1.responsive-container { 2 container-type: inline-size; 3} 4 5.responsive-text { 6 /* Container query units */ 7 font-size: clamp(1rem, 3cqi, 2rem); /* cqi = 1% of container inline size */ 8 padding: 2cqi; 9 10 /* Available units: 11 cqw - container query width 12 cqh - container query height 13 cqi - container query inline size 14 cqb - container query block size 15 cqmin - smaller of cqi or cqb 16 cqmax - larger of cqi or cqb 17 */ 18} 19 20/* Fluid typography based on container */ 21.card-title { 22 font-size: max(1rem, 5cqi); 23} 24 25/* Responsive spacing */ 26.card-content { 27 padding: clamp(1rem, 4cqi, 2rem); 28 gap: clamp(0.5rem, 2cqi, 1rem); 29}

Style Queries (Experimental)#

1/* Query custom properties */ 2.theme-container { 3 container-name: theme; 4 --theme: light; 5} 6 7@container theme style(--theme: dark) { 8 .content { 9 background: #1a1a1a; 10 color: white; 11 } 12} 13 14/* Query computed styles */ 15@container style(display: flex) { 16 .child { 17 flex: 1; 18 } 19}

Nested Containers#

1.page { 2 container: page / inline-size; 3} 4 5.sidebar { 6 container: sidebar / inline-size; 7} 8 9.main { 10 container: main / inline-size; 11} 12 13/* Query parent container */ 14@container page (min-width: 1200px) { 15 .layout { 16 display: grid; 17 grid-template-columns: 300px 1fr; 18 } 19} 20 21/* Query sidebar container */ 22@container sidebar (min-width: 250px) { 23 .sidebar-nav { 24 flex-direction: column; 25 } 26} 27 28/* Query main container */ 29@container main (min-width: 600px) { 30 .article-grid { 31 grid-template-columns: repeat(2, 1fr); 32 } 33}

Combining with Media Queries#

1.widget-container { 2 container: widget / inline-size; 3} 4 5/* Container query for component layout */ 6@container widget (min-width: 400px) { 7 .widget { 8 flex-direction: row; 9 } 10} 11 12/* Media query for global adjustments */ 13@media (prefers-color-scheme: dark) { 14 .widget { 15 --widget-bg: #2a2a2a; 16 --widget-text: #ffffff; 17 } 18} 19 20/* Media query for device-specific features */ 21@media (hover: hover) { 22 .widget:hover { 23 transform: translateY(-2px); 24 } 25} 26 27/* Combine both */ 28@media (min-width: 768px) { 29 .widget-container { 30 max-width: 50%; 31 } 32} 33 34@container widget (min-width: 300px) { 35 .widget-title { 36 font-size: 1.5rem; 37 } 38}

Form Component#

1.form-container { 2 container: form / inline-size; 3} 4 5.form-group { 6 display: flex; 7 flex-direction: column; 8 gap: 0.5rem; 9} 10 11.form-row { 12 display: flex; 13 flex-direction: column; 14 gap: 1rem; 15} 16 17/* Side-by-side labels when container allows */ 18@container form (min-width: 500px) { 19 .form-group { 20 flex-direction: row; 21 align-items: center; 22 } 23 24 .form-group label { 25 flex: 0 0 150px; 26 text-align: right; 27 } 28 29 .form-group input { 30 flex: 1; 31 } 32 33 .form-row { 34 flex-direction: row; 35 } 36 37 .form-row .form-group { 38 flex: 1; 39 } 40} 41 42/* Actions layout */ 43@container form (min-width: 400px) { 44 .form-actions { 45 display: flex; 46 justify-content: flex-end; 47 gap: 1rem; 48 } 49}

Table Component#

1.table-container { 2 container: table / inline-size; 3} 4 5/* Mobile: stack rows */ 6.responsive-table { 7 display: block; 8} 9 10.responsive-table thead { 11 display: none; 12} 13 14.responsive-table tr { 15 display: block; 16 margin-bottom: 1rem; 17 border: 1px solid #ddd; 18 padding: 1rem; 19} 20 21.responsive-table td { 22 display: flex; 23 justify-content: space-between; 24} 25 26.responsive-table td::before { 27 content: attr(data-label); 28 font-weight: bold; 29} 30 31/* Desktop: normal table */ 32@container table (min-width: 600px) { 33 .responsive-table { 34 display: table; 35 width: 100%; 36 } 37 38 .responsive-table thead { 39 display: table-header-group; 40 } 41 42 .responsive-table tr { 43 display: table-row; 44 margin-bottom: 0; 45 border: none; 46 padding: 0; 47 } 48 49 .responsive-table td { 50 display: table-cell; 51 } 52 53 .responsive-table td::before { 54 display: none; 55 } 56}

Browser Support#

1/* Feature detection */ 2@supports (container-type: inline-size) { 3 .modern-container { 4 container-type: inline-size; 5 } 6} 7 8/* Fallback for older browsers */ 9.card-wrapper { 10 /* Fallback styles */ 11} 12 13@supports (container-type: inline-size) { 14 .card-wrapper { 15 container: card / inline-size; 16 } 17 18 @container card (min-width: 400px) { 19 .card { 20 flex-direction: row; 21 } 22 } 23}

Best Practices#

Usage: ✓ Use for component-level responsiveness ✓ Combine with media queries appropriately ✓ Name containers for clarity ✓ Use cqi units for fluid sizing Performance: ✓ Apply containment intentionally ✓ Avoid unnecessary nesting ✓ Test with various container sizes ✓ Monitor layout shifts Design: ✓ Design components container-first ✓ Define clear breakpoints ✓ Document container dependencies ✓ Consider content, not just size

Conclusion#

Container queries enable truly responsive components that adapt to their container rather than the viewport. Use them for reusable components like cards, navigation, and forms. Combine with media queries for device-specific features and use container units for fluid sizing.

Share this article

Help spread the word about Bootspring