Back to Blog
CSSContainer QueriesResponsiveLayout

CSS Container Queries Advanced

Deep dive into container queries. From size queries to style queries to container query units.

B
Bootspring Team
Engineering
January 28, 2021
6 min read

Container queries enable component-level responsive design. Here's how to master them.

Basic Container Setup#

1/* Define a containment context */ 2.card-container { 3 container-type: inline-size; 4 container-name: card; 5} 6 7/* Shorthand */ 8.sidebar { 9 container: sidebar / inline-size; 10} 11 12/* Query the container */ 13@container card (min-width: 400px) { 14 .card { 15 display: grid; 16 grid-template-columns: 200px 1fr; 17 } 18} 19 20/* Without name (queries nearest ancestor) */ 21@container (min-width: 300px) { 22 .content { 23 font-size: 1.2rem; 24 } 25}

Container Types#

1/* inline-size - query inline dimension (width in horizontal writing) */ 2.wrapper { 3 container-type: inline-size; 4} 5 6/* size - query both dimensions */ 7.box { 8 container-type: size; 9} 10 11@container (min-height: 200px) { 12 .tall-content { 13 display: block; 14 } 15} 16 17/* normal - no size containment, only style queries */ 18.style-only { 19 container-type: normal; 20 container-name: theme; 21}

Size Query Syntax#

1/* Width queries */ 2@container (width > 400px) { 3 .element { /* styles */ } 4} 5 6@container (width >= 400px) { 7 .element { /* styles */ } 8} 9 10@container (400px <= width <= 800px) { 11 .element { /* styles */ } 12} 13 14/* Combined conditions */ 15@container (min-width: 400px) and (max-width: 800px) { 16 .element { /* styles */ } 17} 18 19/* Or conditions */ 20@container (width < 300px) or (width > 800px) { 21 .element { /* extreme widths styles */ } 22} 23 24/* Not condition */ 25@container not (width < 400px) { 26 .element { /* styles when >= 400px */ } 27} 28 29/* Named container */ 30@container sidebar (min-width: 250px) { 31 .nav-item { 32 padding: 1rem; 33 } 34}

Container Query Units#

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

Style Queries#

1/* Query custom properties */ 2.theme-container { 3 container-name: theme; 4 --theme: light; 5} 6 7@container theme style(--theme: dark) { 8 .card { 9 background: #1a1a1a; 10 color: white; 11 } 12} 13 14@container style(--theme: light) { 15 .card { 16 background: white; 17 color: #333; 18 } 19} 20 21/* Query computed styles */ 22@container style(--featured: true) { 23 .product { 24 border: 2px solid gold; 25 transform: scale(1.05); 26 } 27} 28 29/* Boolean-like custom properties */ 30.parent { 31 --expanded: true; 32} 33 34@container style(--expanded: true) { 35 .content { 36 display: block; 37 } 38} 39 40@container style(--expanded: false) { 41 .content { 42 display: none; 43 } 44}

Nested Containers#

1/* Multiple container contexts */ 2.page { 3 container: page / inline-size; 4} 5 6.sidebar { 7 container: sidebar / inline-size; 8} 9 10.widget { 11 container: widget / inline-size; 12} 13 14/* Query specific container */ 15@container page (min-width: 1200px) { 16 .sidebar { 17 width: 300px; 18 } 19} 20 21@container sidebar (min-width: 250px) { 22 .widget { 23 padding: 1.5rem; 24 } 25} 26 27@container widget (min-width: 200px) { 28 .widget-content { 29 display: grid; 30 grid-template-columns: 1fr 1fr; 31 } 32}

Card Component Patterns#

1/* Fully responsive card */ 2.card-wrapper { 3 container: card / inline-size; 4} 5 6.card { 7 display: flex; 8 flex-direction: column; 9 padding: clamp(1rem, 4cqi, 2rem); 10} 11 12.card-image { 13 aspect-ratio: 16 / 9; 14 width: 100%; 15} 16 17.card-title { 18 font-size: clamp(1rem, 4cqi, 1.5rem); 19} 20 21/* Small card (< 300px) */ 22@container card (max-width: 299px) { 23 .card { 24 text-align: center; 25 } 26 27 .card-meta { 28 display: none; 29 } 30} 31 32/* Medium card (300px - 500px) */ 33@container card (300px <= width < 500px) { 34 .card { 35 flex-direction: row; 36 } 37 38 .card-image { 39 width: 40%; 40 aspect-ratio: 1; 41 } 42 43 .card-content { 44 flex: 1; 45 padding-left: 1rem; 46 } 47} 48 49/* Large card (>= 500px) */ 50@container card (min-width: 500px) { 51 .card { 52 display: grid; 53 grid-template-columns: 1fr 1fr; 54 gap: 2rem; 55 } 56 57 .card-image { 58 aspect-ratio: 4 / 3; 59 } 60 61 .card-actions { 62 display: flex; 63 gap: 1rem; 64 } 65}
1/* Responsive navigation */ 2.nav-wrapper { 3 container: nav / inline-size; 4} 5 6.nav { 7 display: flex; 8 flex-wrap: wrap; 9 gap: 0.5rem; 10} 11 12.nav-link { 13 padding: 0.5rem; 14 font-size: clamp(0.875rem, 2cqi, 1rem); 15} 16 17/* Compact nav */ 18@container nav (max-width: 400px) { 19 .nav { 20 flex-direction: column; 21 } 22 23 .nav-link { 24 width: 100%; 25 text-align: center; 26 } 27 28 .nav-icon { 29 display: none; 30 } 31} 32 33/* Expanded nav */ 34@container nav (min-width: 600px) { 35 .nav { 36 justify-content: space-between; 37 } 38 39 .nav-link { 40 display: flex; 41 align-items: center; 42 gap: 0.5rem; 43 } 44 45 .nav-description { 46 display: block; 47 font-size: 0.75rem; 48 opacity: 0.7; 49 } 50}

Grid Layouts#

1/* Container-aware grid */ 2.grid-container { 3 container: grid / inline-size; 4} 5 6.grid { 7 display: grid; 8 gap: clamp(1rem, 3cqi, 2rem); 9} 10 11@container grid (max-width: 400px) { 12 .grid { 13 grid-template-columns: 1fr; 14 } 15} 16 17@container grid (400px <= width < 700px) { 18 .grid { 19 grid-template-columns: repeat(2, 1fr); 20 } 21} 22 23@container grid (700px <= width < 1000px) { 24 .grid { 25 grid-template-columns: repeat(3, 1fr); 26 } 27} 28 29@container grid (min-width: 1000px) { 30 .grid { 31 grid-template-columns: repeat(4, 1fr); 32 } 33}

Form Layouts#

1/* Responsive form */ 2.form-container { 3 container: form / inline-size; 4} 5 6.form-group { 7 display: flex; 8 flex-direction: column; 9 gap: 0.5rem; 10 margin-bottom: 1rem; 11} 12 13.form-label { 14 font-size: clamp(0.875rem, 2cqi, 1rem); 15} 16 17/* Inline labels on wider containers */ 18@container form (min-width: 500px) { 19 .form-group { 20 flex-direction: row; 21 align-items: center; 22 } 23 24 .form-label { 25 width: 30%; 26 text-align: right; 27 padding-right: 1rem; 28 } 29 30 .form-input { 31 flex: 1; 32 } 33} 34 35/* Side-by-side fields */ 36@container form (min-width: 700px) { 37 .form-row { 38 display: grid; 39 grid-template-columns: 1fr 1fr; 40 gap: 2rem; 41 } 42}

With CSS Variables#

1/* Dynamic theming with containers */ 2.theme-wrapper { 3 container: theme / inline-size; 4 --card-padding: 1rem; 5 --card-gap: 0.5rem; 6 --card-radius: 0.5rem; 7} 8 9@container theme (min-width: 400px) { 10 .theme-wrapper { 11 --card-padding: 1.5rem; 12 --card-gap: 1rem; 13 --card-radius: 0.75rem; 14 } 15} 16 17@container theme (min-width: 600px) { 18 .theme-wrapper { 19 --card-padding: 2rem; 20 --card-gap: 1.5rem; 21 --card-radius: 1rem; 22 } 23} 24 25.card { 26 padding: var(--card-padding); 27 gap: var(--card-gap); 28 border-radius: var(--card-radius); 29}

Fallback Strategies#

1/* Feature detection */ 2@supports (container-type: inline-size) { 3 .modern-card-container { 4 container-type: inline-size; 5 } 6} 7 8/* Fallback without container queries */ 9.card { 10 /* Default mobile styles */ 11 display: flex; 12 flex-direction: column; 13} 14 15/* Media query fallback */ 16@media (min-width: 600px) { 17 .card { 18 flex-direction: row; 19 } 20} 21 22/* Container query enhancement */ 23@container (min-width: 400px) { 24 .card { 25 flex-direction: row; 26 } 27}

Best Practices#

Container Setup: ✓ Use meaningful container names ✓ Prefer inline-size over size ✓ Apply to direct parent of queried elements ✓ Consider nesting carefully Queries: ✓ Use container query units for fluid sizing ✓ Combine with clamp() for bounds ✓ Test at various container sizes ✓ Provide sensible defaults Performance: ✓ Avoid excessive nesting ✓ Use containment appropriately ✓ Test with real content ✓ Profile rendering performance Compatibility: ✓ Provide fallbacks for older browsers ✓ Use @supports for feature detection ✓ Consider progressive enhancement ✓ Test across browsers

Conclusion#

Container queries enable truly component-based responsive design. Use size queries for layout changes, style queries for theming, and container query units for fluid sizing. Always provide fallbacks and test thoroughly across container sizes.

Share this article

Help spread the word about Bootspring