CSS Container Queries: Advanced
Style Queries
Beyond size-based container queries, CSS also supports style queries, allowing components to adapt based on the computed styles of their container.
Basic Style Query Syntax
/* Define a style container */
.theme-container {
container-name: theme;
container-type: style; /* Enable style queries */
}
/* Query based on custom property value */
@container theme style(--theme = dark) {
.card {
background-color: #333;
color: white;
}
.card-button {
background-color: #555;
border-color: #777;
}
}
/* Query based on multiple style conditions */
@container theme style(--theme = light) and style(--accent = blue) {
.card-accent {
border-color: var(--accent-color, blue);
}
}
Style Query Operators
Operator | Description | Example |
---|---|---|
= |
Equal to | style(--theme = dark) |
!= |
Not equal to | style(--theme != light) |
< , > |
Less than, Greater than | style(--spacing > 16px) |
<= , >= |
Less than or equal, Greater than or equal | style(--font-size >= 1.2rem) |
Practical Style Query Example
/* HTML */
<div class="theme-container" style="--theme: dark; --accent: blue;">
<div class="card">
<h3 class="card-title">Card Title</h3>
<p class="card-content">Card content goes here...</p>
<button class="card-button">Action</button>
</div>
</div>
/* CSS */
.theme-container {
container-name: theme;
container-type: style;
/* Default light theme values */
--theme: light;
--accent: blue;
--text-color: #333;
--bg-color: #fff;
--accent-color: #0066cc;
}
.card {
background-color: var(--bg-color);
color: var(--text-color);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid #ddd;
}
.card-button {
background-color: var(--accent-color);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
}
/* Style queries for theme variations */
@container theme style(--theme = dark) {
.card {
--bg-color: #222;
--text-color: #eee;
border-color: #444;
}
.card-button {
background-color: #555;
}
}
@container theme style(--accent = blue) {
.card-button {
--accent-color: #0066cc;
}
}
@container theme style(--accent = green) {
.card-button {
--accent-color: #00cc66;
}
}
Key Insight: Style queries enable theme switching and dynamic styling based on container properties, creating a powerful system for component adaptations beyond just size changes.
Complex Layout Adaptations
Multi-Level Container Queries
/* HTML */
<div class="page-container">
<div class="sidebar">
<div class="widget-container">
<div class="widget">
<h3 class="widget-title">Widget Title</h3>
<div class="widget-content">
<!-- Widget content -->
</div>
</div>
</div>
</div>
<div class="main-content">
<!-- Main content -->
</div>
</div>
/* CSS */
.page-container {
container-type: inline-size;
container-name: page;
display: flex;
flex-direction: column;
}
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.widget-container {
container-type: inline-size;
container-name: widget;
}
/* Page-level container query */
@container page (min-width: 768px) {
.page-container {
flex-direction: row;
}
.sidebar {
width: 300px;
}
.main-content {
flex: 1;
}
}
/* Sidebar-level container query */
@container sidebar (min-width: 250px) {
.widget-container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.widget {
flex: 1 0 200px;
}
}
/* Widget-level container query */
@container widget (min-width: 400px) {
.widget {
display: flex;
}
.widget-title {
width: 30%;
}
.widget-content {
width: 70%;
}
}
Combining Media and Container Queries
/* Base mobile styles */
.dashboard {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.card-container {
container-type: inline-size;
}
.card {
padding: 1rem;
}
/* Media query for overall layout */
@media (min-width: 768px) {
.dashboard {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1200px) {
.dashboard {
grid-template-columns: repeat(3, 1fr);
}
}
/* Container queries for individual cards */
@container (min-width: 300px) {
.card {
display: flex;
align-items: center;
}
.card-icon {
margin-right: 1rem;
}
}
@container (min-width: 400px) {
.card {
padding: 1.5rem;
}
.card-title {
font-size: 1.25rem;
}
}
Container Queries with CSS Grid and Flexbox
Adaptive Grid Layouts
/* HTML */
<div class="grid-container">
<div class="grid">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
<div class="grid-item">Item 4</div>
<div class="grid-item">Item 5</div>
<div class="grid-item">Item 6</div>
</div>
</div>
/* CSS */
.grid-container {
container-type: inline-size;
container-name: grid;
}
.grid {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
}
.grid-item {
background-color: #f0f0f0;
padding: 1rem;
border-radius: 4px;
}
/* Container queries for adaptive grid */
@container grid (min-width: 400px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@container grid (min-width: 700px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
@container grid (min-width: 1000px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}
Responsive Flexbox Components
/* HTML */
<div class="feature-container">
<div class="feature">
<div class="feature-icon">
<i class="icon"></i>
</div>
<div class="feature-content">
<h3 class="feature-title">Feature Title</h3>
<p class="feature-description">Feature description text goes here...</p>
</div>
</div>
</div>
/* CSS */
.feature-container {
container-type: inline-size;
container-name: feature;
}
.feature {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 1.5rem;
border: 1px solid #eee;
border-radius: 8px;
}
.feature-icon {
width: 60px;
height: 60px;
background-color: #f0f0f0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
/* Container queries for adaptive layout */
@container feature (min-width: 400px) {
.feature {
flex-direction: row;
text-align: left;
align-items: flex-start;
}
.feature-icon {
margin-right: 1.5rem;
margin-bottom: 0;
}
.feature-content {
flex: 1;
}
}
@container feature (min-width: 600px) {
.feature-title {
font-size: 1.5rem;
}
.feature-description {
font-size: 1.1rem;
}
}
Advanced Component Examples
Responsive Data Table
/* HTML */
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start Date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Name">John Doe</td>
<td data-label="Position">Developer</td>
<td data-label="Office">New York</td>
<td data-label="Age">35</td>
<td data-label="Start Date">2020/01/15</td>
<td data-label="Salary">$85,000</td>
</tr>
<!-- More rows... -->
</tbody>
</table>
</div>
/* CSS */
.table-container {
container-type: inline-size;
container-name: table;
overflow-x: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
padding: 0.75rem;
border-bottom: 1px solid #ddd;
}
.data-table th {
text-align: left;
background-color: #f8f9fa;
}
/* Container query for responsive table */
@container table (max-width: 700px) {
.data-table {
border: 0;
}
.data-table thead {
display: none;
}
.data-table tr {
display: block;
margin-bottom: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.data-table td {
display: block;
text-align: right;
border-bottom: 1px solid #eee;
position: relative;
padding-left: 50%;
}
.data-table td:last-child {
border-bottom: 0;
}
.data-table td::before {
content: attr(data-label);
position: absolute;
left: 0.75rem;
width: 45%;
text-align: left;
font-weight: bold;
}
}
Adaptive Dashboard Widget
/* HTML */
<div class="widget-container">
<div class="widget">
<div class="widget-header">
<h3 class="widget-title">Sales Overview</h3>
<div class="widget-actions">
<button class="widget-button">Daily</button>
<button class="widget-button">Weekly</button>
<button class="widget-button">Monthly</button>
<button class="widget-button">Export</button>
</div>
</div>
<div class="widget-body">
<div class="widget-chart">
<!-- Chart content -->
</div>
<div class="widget-stats">
<div class="stat-item">
<span class="stat-label">Total Sales</span>
<span class="stat-value">$24,500</span>
</div>
<div class="stat-item">
<span class="stat-label">Conversion</span>
<span class="stat-value">15.3%</span>
</div>
<div class="stat-item">
<span class="stat-label">Growth</span>
<span class="stat-value">+8.4%</span>
</div>
</div>
</div>
</div>
</div>
/* CSS */
.widget-container {
container-type: inline-size;
container-name: widget;
}
.widget {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
}
.widget-header {
display: flex;
flex-direction: column;
padding: 1rem;
background-color: #f8f9fa;
border-bottom: 1px solid #ddd;
}
.widget-title {
margin: 0 0 1rem 0;
}
.widget-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.widget-button {
padding: 0.5rem;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
}
.widget-body {
padding: 1rem;
}
.widget-chart {
height: 200px;
background-color: #f0f0f0;
margin-bottom: 1rem;
}
.widget-stats {
display: flex;
flex-direction: column;
gap: 1rem;
}
.stat-item {
display: flex;
flex-direction: column;
padding: 0.75rem;
background-color: #f8f9fa;
border-radius: 4px;
}
.stat-label {
font-size: 0.875rem;
color: #666;
}
.stat-value {
font-size: 1.25rem;
font-weight: bold;
}
/* Container queries for adaptive widget */
@container widget (min-width: 500px) {
.widget-header {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.widget-title {
margin: 0;
}
.widget-stats {
flex-direction: row;
}
.stat-item {
flex: 1;
}
}
@container widget (min-width: 700px) {
.widget-body {
display: flex;
gap: 1.5rem;
}
.widget-chart {
flex: 2;
margin-bottom: 0;
}
.widget-stats {
flex: 1;
flex-direction: column;
}
}
@container widget (min-width: 900px) {
.widget-chart {
height: 300px;
}
.stat-value {
font-size: 1.5rem;
}
}
Performance Optimization
Using CSS Containment
/* Combining container queries with containment */
.card-container {
container-type: inline-size;
container-name: card;
/* Add containment for better performance */
contain: layout style paint;
}
/* Alternative: use content-visibility */
.offscreen-container {
container-type: inline-size;
content-visibility: auto;
contain-intrinsic-size: 300px;
}
Limiting Container Nesting
Deeply nested containers can impact performance. Consider these strategies:
- Use named containers to target specific containers without nesting
- Limit container depth to only what's necessary
- Apply container queries strategically at key breakpoints
Reducing Layout Shifts
/* Set minimum dimensions to reduce layout shifts */
.card-container {
container-type: inline-size;
min-height: 200px;
min-width: 200px;
}
/* Use aspect-ratio to maintain proportions */
.image-container {
container-type: inline-size;
aspect-ratio: 16 / 9;
}
/* Set explicit sizes for container query units */
.element {
font-size: clamp(1rem, 3cqi, 2rem);
}
Performance Tip: Container queries can trigger layout recalculations when container dimensions change. Use the
contain
property and limit the number of container queries to optimize performance.
Future Developments
Upcoming Container Query Features
- Container Query Length Units: More powerful relative units
- Container Query Value Types: New value types for more complex queries
- State Queries: Querying based on element states
- Scoped Styles: Better encapsulation for component styles
Integration with Web Components
/* Example of container queries with web components */
class ResponsiveCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
container-type: inline-size;
display: block;
}
.card {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 8px;
}
@container (min-width: 400px) {
.card {
display: flex;
}
.card-image {
width: 40%;
}
.card-content {
width: 60%;
padding-left: 1rem;
}
}
</style>
<div class="card">
<div class="card-image">
<slot name="image"></slot>
</div>
<div class="card-content">
<slot></slot>
</div>
</div>
`;
}
}
customElements.define('responsive-card', ResponsiveCard);
Related Topics
To further enhance your understanding of modern CSS layout and responsive design, explore these related topics:
Final Thought: Container queries represent a paradigm shift in responsive design, enabling truly modular, context-aware components. As browser support continues to improve, they will become an essential tool in every front-end developer's toolkit.