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}Navigation Patterns#
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.