Problem: Your Components Break in Tight Spaces
You built a card component that looks perfect at 1200px wide, but crams into a 300px sidebar and everything overlaps. Media queries can't help because they only see viewport size, not the card's actual container.
You'll learn:
- How container queries work vs media queries
- The
@containersyntax for responsive components - Using GitHub Copilot to generate container query code
- Common pitfalls and how to avoid them
Time: 12 min | Level: Intermediate
Why This Happens
Media queries (@media) respond to viewport width. Your sidebar card sees the 1920px screen and tries to use desktop styles, even though it's squeezed into 300px of space.
Common symptoms:
- Components look wrong in sidebars/grids
- Same component needs different layouts in different contexts
- Media query breakpoints don't match actual component needs
- Copy-pasting styles for every container size
Example that fails:
/* This checks viewport, not the card's container */
@media (min-width: 768px) {
.card {
display: grid;
grid-template-columns: 200px 1fr; /* Breaks in narrow sidebars */
}
}
Solution
Step 1: Set Up Container Context
First, tell the browser which element is the container to measure:
/* Define the container */
.sidebar,
.main-content,
.card-wrapper {
container-type: inline-size; /* Measures width */
container-name: card-container; /* Optional: name for targeting */
}
Why this works: container-type: inline-size makes the element a container context. Child elements can now query this container's width instead of the viewport.
If using Copilot: Type /* make this a container */ above your selector, then press Tab. Copilot suggests the container-type property.
Step 2: Write Container Queries
Now query the container, not the viewport:
/* Card adapts to its container */
.card {
display: block; /* Mobile-first: stack by default */
}
/* When container is 400px+ wide, switch to grid */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 150px 1fr;
gap: 1rem;
}
}
/* When container is 600px+ wide, more space */
@container (min-width: 600px) {
.card {
grid-template-columns: 200px 1fr;
gap: 1.5rem;
}
}
Copilot tip: After writing @container, Copilot auto-suggests breakpoints based on your existing styles. Press Tab to accept, then customize the width values.
Step 3: Use Named Containers (Advanced)
When you have nested containers, target specific ones:
/* Define named containers */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
.main-content {
container-name: main;
container-type: inline-size;
}
/* Target specific container */
@container sidebar (min-width: 300px) {
.card { padding: 0.5rem; }
}
@container main (min-width: 600px) {
.card { padding: 2rem; }
}
Why this matters: Same .card component behaves differently in sidebar vs main area, even if both containers are the same width.
Copilot workflow:
- Type
container-name:and Copilot suggests semantic names - In
@container, type the name and Copilot completes the query - Copilot learns your naming patterns after 2-3 examples
Step 4: Combine with Container Query Units
Use cqw, cqh for sizes relative to the container:
@container (min-width: 400px) {
.card-title {
font-size: 5cqw; /* 5% of container width */
max-font-size: 2rem; /* Cap it */
}
.card-image {
height: 30cqh; /* 30% of container height */
}
}
Container units:
cqw= 1% of container widthcqh= 1% of container heightcqi= 1% of container inline size (width in LTR)cqb= 1% of container block size (height)
Copilot behavior: Type font-size: 5cq and Copilot suggests the unit. It knows container context from your @container rules.
Step 5: Full Example with Copilot
Let's build a real product card:
<div class="product-grid">
<div class="product-card">
<img src="product.jpg" alt="Product" class="product-image">
<div class="product-info">
<h3 class="product-title">Wireless Headphones</h3>
<p class="product-price">$129.99</p>
<button class="product-cta">Add to Cart</button>
</div>
</div>
</div>
/* Container setup */
.product-grid {
container-type: inline-size;
display: grid;
gap: 1rem;
/* Grid adapts with regular media queries */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
/* Mobile-first card */
.product-card {
background: white;
border-radius: 8px;
overflow: hidden;
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-info {
padding: 1rem;
}
/* Container query: when card has space */
@container (min-width: 350px) {
.product-card {
display: grid;
grid-template-columns: 40% 1fr; /* Side-by-side */
}
.product-image {
height: 100%; /* Fill container height */
}
.product-cta {
width: auto; /* Shrink button */
padding: 0.5rem 1.5rem;
}
}
/* Container query: wide cards get more breathing room */
@container (min-width: 500px) {
.product-info {
padding: 2rem;
}
.product-title {
font-size: 4cqw; /* Scales with container */
max-font-size: 1.5rem;
}
}
How Copilot helps here:
- After typing
.product-card {, Copilot suggests display properties - Type
@containerand it suggests breakpoints from your container setup - For
font-size:, Copilot offers bothcqwandrem- pick what fits - It learns your spacing scale (1rem, 1.5rem, 2rem) and suggests consistently
If it fails:
- Container queries not working: Check browser support (Chrome 105+, Safari 16+, Firefox 110+)
- Styles not applying: Ensure parent has
container-typeset - Nested containers broken: Add
container-nameto target the right one - Copilot suggesting media queries: Comment
/* use container query */to guide it
Verification
Test it:
- Open browser DevTools
- Resize the container element (not the window)
- Watch styles change at container breakpoints
You should see: Card layout switches when the container hits 350px, regardless of viewport size.
DevTools tip (Chrome 105+):
- Open Elements panel → Styles
- Look for "Container Queries" section
- Shows active container and matching queries
Container Queries vs Media Queries
Use @container when:
- Component lives in multiple contexts (sidebar, main, grid)
- Layout depends on parent size, not screen size
- Building reusable components for design systems
Use @media when:
- Changing page-level layout (header, navigation)
- Adapting to device capabilities (touch, hover)
- Overall page structure that always matches viewport
Use both:
/* Page layout uses media queries */
@media (min-width: 1024px) {
.layout {
display: grid;
grid-template-columns: 300px 1fr; /* Sidebar + main */
}
}
/* Components inside use container queries */
@container (min-width: 400px) {
.card { /* Adapts to its space */ }
}
What You Learned
- Container queries measure the parent container, not viewport
container-type: inline-sizeenables container queries@containersyntax mirrors@mediabut checks container width- Container units (
cqw,cqh) size elements relative to containers - GitHub Copilot learns your container patterns and suggests matching queries
Limitations:
- Browser support: Chrome 105+, Safari 16+, Firefox 110+ (IE/old browsers need fallbacks)
- Can't query container's own properties (only descendants can query it)
container-typecreates a new containing block (affects absolute positioning)
Copilot Productivity Tips
Train Copilot on your patterns:
- Write 2-3 container queries manually with your preferred breakpoints
- Copilot learns your values (e.g., 400px, 600px, 800px)
- Future suggestions match your spacing scale
Use comments to guide:
/* small container layout */
@container (min-width: 300px) { /* Copilot fills this */ }
/* medium container, grid layout */
@container (min-width: 500px) { /* Copilot suggests grid */ }
Accept and modify:
- Copilot often suggests
min-width: 768px(common media query breakpoint) - Accept it, then change to your container's actual breakpoint
- Copilot adapts to your edits over time
Keyboard shortcuts:
Tab= Accept suggestionAlt + ]= Next suggestion (if Copilot offers alternatives)Esc= Dismiss suggestion
Tested on Chrome 121, Safari 17.2, Firefox 122, with GitHub Copilot v1.156+
Browser compatibility: All modern browsers as of 2026. For legacy support, see CSS Container Query Polyfill.