CSS Container Queries Layout System
A practical exploration of CSS Container Queries to build truly responsive components that adapt based on their container size rather than viewport dimensions. This experiment demonstrates modern CSS techniques with progressive enhancement for browser compatibility.
Problem
Traditional responsive design relies on viewport-based media queries, which creates limitations:
- Components can’t adapt to their actual container size
- Sidebar layouts break when components are moved between different width containers
- Reusable components require different CSS rules in different contexts
- Grid layouts struggle with dynamic content areas
Solution Architecture
Container Query Implementation
/* Base card component */
.card {
container-type: inline-size;
container-name: card;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 1rem;
background: white;
}
/* Responsive layouts based on container width */
@container card (min-width: 300px) {
.card__content {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
align-items: center;
}
.card__image {
width: 80px;
height: 80px;
}
}
@container card (min-width: 500px) {
.card {
padding: 1.5rem;
}
.card__content {
grid-template-columns: 120px 1fr auto;
}
.card__image {
width: 120px;
height: 120px;
}
.card__actions {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
@container card (min-width: 700px) {
.card__content {
grid-template-columns: 160px 1fr 200px;
}
.card__image {
width: 160px;
height: 160px;
}
.card__meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
}
Progressive Enhancement Strategy
/* Fallback for browsers without container query support */
.card__content {
display: block;
}
.card__image {
width: 60px;
height: 60px;
float: left;
margin-right: 1rem;
margin-bottom: 0.5rem;
}
/* Feature detection and enhancement */
@supports (container-type: inline-size) {
.card__image {
float: none;
margin: 0;
}
}
JavaScript Enhancement
// Feature detection and polyfill loading
if (!CSS.supports('container-type: inline-size')) {
// Load container query polyfill for older browsers
import('https://cdn.skypack.dev/css-container-query-polyfill');
// Add fallback class for different styling
document.documentElement.classList.add('no-container-queries');
}
// Demo: Dynamic container resizing
class ContainerResizer {
constructor(element) {
this.element = element;
this.isResizing = false;
this.setupResizer();
}
setupResizer() {
const resizer = document.createElement('div');
resizer.className = 'resizer';
resizer.innerHTML = '⟷';
resizer.addEventListener('mousedown', (e) => {
this.isResizing = true;
this.startX = e.clientX;
this.startWidth = this.element.offsetWidth;
document.addEventListener('mousemove', this.handleResize);
document.addEventListener('mouseup', this.stopResize);
});
this.element.appendChild(resizer);
}
handleResize = (e) => {
if (!this.isResizing) return;
const diff = e.clientX - this.startX;
const newWidth = Math.max(200, this.startWidth + diff);
this.element.style.width = `${newWidth}px`;
};
stopResize = () => {
this.isResizing = false;
document.removeEventListener('mousemove', this.handleResize);
document.removeEventListener('mouseup', this.stopResize);
};
}
// Initialize resizable containers
document.querySelectorAll('.resizable-container').forEach((container) => {
new ContainerResizer(container);
});
HTML Structure
<div class="demo-layout">
<div class="resizable-container" style="width: 400px;">
<div class="card">
<div class="card__content">
<img class="card__image" src="https://picsum.photos/160" alt="Demo" />
<div class="card__info">
<h3 class="card__title">Responsive Product Card</h3>
<p class="card__description">
This card adapts its layout based on container width, not viewport
size.
</p>
<div class="card__meta">
<span class="card__price">$99.99</span>
<span class="card__rating">★★★★☆</span>
</div>
</div>
<div class="card__actions">
<button class="btn btn-primary">Add to Cart</button>
<button class="btn btn-secondary">Save</button>
</div>
</div>
</div>
</div>
</div>
Key Technical Insights
Container Query Advantages
- True Component Isolation: Components respond to their actual space
- Reusable Across Contexts: Same component works in sidebar, main content, or modal
- Better Grid Layouts: Dynamic columns based on available space
- Reduced CSS Complexity: No need for contextual modifier classes
Browser Support Strategy
/* Progressive enhancement approach */
@supports not (container-type: inline-size) {
.card {
/* Simplified layout for older browsers */
display: block;
}
.card__content {
display: flex;
align-items: center;
gap: 1rem;
}
}
Performance Considerations
- Container queries recalculate on container size changes (not just viewport)
- Containment context improves rendering performance
- Layout containment prevents unnecessary reflows
Real-World Applications
Dashboard Widgets
Perfect for admin dashboards where widgets can be:
- Moved between different sized containers
- Resized dynamically by users
- Displayed in various grid layouts
E-commerce Product Cards
Automatically adapt from:
- Narrow sidebar: Minimal info, vertical layout
- Medium grid: Balanced horizontal layout
- Wide featured: Full details with actions
Content Management Systems
Components that work seamlessly in:
- Narrow content editor sidebars
- Main content areas
- Full-width featured sections
Browser Compatibility
Browser | Container Queries | Polyfill Needed |
---|---|---|
Chrome 105+ | ✅ Native | No |
Firefox 110+ | ✅ Native | No |
Safari 16+ | ✅ Native | No |
IE/Legacy | ❌ Not Supported | Yes |
Implementation Tips
Best Practices
- Always provide fallbacks for browsers without support
- Use meaningful container names for better debugging
- Test with dynamic content to ensure layouts don’t break
- Consider container query units (cqw, cqh) for typography scaling
Common Pitfalls
- Avoid circular dependencies (container size affecting its own size)
- Remember containment side effects (z-index, absolute positioning)
- Test resize performance with many containers
CodePen Demo Features
The live demo showcases:
- Interactive resizer to see container queries in action
- Multiple breakpoints demonstrating different layouts
- Progressive enhancement with fallback styles
- Smooth transitions between layout states
Future Enhancements
- Container query units for more fluid typography
- Style queries for theme-based adaptations
- Integration with CSS Grid subgrid for complex layouts
This experiment demonstrates how container queries solve real layout problems while maintaining backward compatibility through progressive enhancement.