Modern CSS: Container Queries, :has(), Nesting & Subgrid
Master modern CSS capabilities: Component-scoped @container queries, the relational :has() parent selector, native CSS Nesting, Subgrid alignment, and fluid math functions.
1. Real-World Analogy: Modular Smart Furniture vs Room Architecture
Modern CSS transitions web design from rigid page-level blueprints to modular, self-aware UI components:
📦 Container Queries (`@container`)
Like expandable modular furniture. If you put a sofa in a narrow studio apartment room (sidebar), it folds into an armchair. If you place the exact same sofa in a spacious lounge (main feed), it expands into a sectional — regardless of the total building footprint (viewport)!
🔍 The Relational Selector (`:has()`)
Like a smart airport luggage scanner. Instead of inspecting each bag separately, the conveyor belt machine triggers a red warning light on the outer suitcase if and only if it detects battery electronics inside.
🌿 Native CSS Nesting (`&`)
Like nested organizational folders in your OS. Child styles reside right inside their parent block without requiring third-party build tools (Sass/Less) or duplicated class prefixes.
2. Container Queries (`@container` & Units)
While Media Queries respond to the entire viewport width, Container Queries allow a component to style itself dynamically based on the width of its immediate container element.
/* Step 1: Designate the parent element as a queryable container context */
.card-wrapper {
container-type: inline-size; /* Measures container's inline (width) axis */
container-name: card-container; /* Optional named container */
}
/* Step 2: Query the container's width, NOT the window viewport */
@container card-container (min-width: 480px) {
.product-card {
display: grid;
grid-template-columns: 180px 1fr;
gap: 1.5rem;
}
.product-card__title {
font-size: 1.5rem;
}
}
/* Step 3: Container Query Units (cqw = 1% of container width) */
.product-card__badge {
font-size: clamp(0.75rem, 3cqw, 1.25rem);
}
Container Query Units Overview
| Unit | Description | Viewport Equivalent |
|---|---|---|
cqw |
1% of query container's width (inline size) | vw (1% viewport width) |
cqh |
1% of query container's height (block size) | vh (1% viewport height) |
cqi |
1% of query container's inline size | vi |
cqmin / cqmax |
Smaller or larger value between cqi and cqb |
vmin / vmax |
3. The Relational Parent Selector: :has()
The :has() pseudo-class represents an element if any of the relative selectors passed into it match at least one element. It unlocks the long-awaited "parent selector" and conditional sibling styling in pure CSS:
/* 1. Style parent card if it contains a featured badge child */
.article-card:has(.badge--featured) {
border: 2px solid #6C4CE8;
background: #F5F3FF;
}
/* 2. Highlight form wrapper when ANY child input is invalid & touched */
form:has(input:invalid:user-valid) {
border-left: 4px solid #EF4444;
}
/* 3. Darken body and lock scrolling when modal dialog is open */
body:has(dialog[open]) {
overflow: hidden;
}
/* 4. Sibling hover focus effect: dim siblings when one is hovered */
.menu-list:has(li:hover) li:not(:hover) {
opacity: 0.4;
filter: blur(1px);
transition: opacity 0.2s ease;
}
4. Native CSS Nesting
CSS now natively supports selector nesting without needing preprocessors like Sass or Less. The nesting selector & explicitly references the parent selector:
.card {
background: #FFFFFF;
padding: 1.5rem;
border-radius: 8px;
/* Direct element nesting */
h3 {
color: #0F172A;
font-size: 1.25rem;
}
/* Pseudo-class targeting parent with & */
&:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
/* Class modifier on parent */
&.card--dark {
background: #1E293B;
color: #FFFFFF;
}
/* Nested Media Query inside the component */
@media (max-width: 640px) {
padding: 1rem;
}
}
5. CSS Subgrid & Fluid Math Functions
Subgrid allows nested child grids to participate directly in the parent grid's row and column tracks, ensuring cards in different containers have identical header and footer heights.
/* 1. Subgrid alignment across sibling cards */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: auto 1fr auto; /* header, body, footer */
gap: 1.5rem;
}
.grid-container > .card {
display: grid;
grid-row: span 3; /* Spans 3 tracks of the parent */
grid-template-rows: subgrid; /* Inherits parent's row alignments! */
}
/* 2. Fluid Typography with clamp() */
h1 {
/* clamp(MIN, PREFERRED, MAX) */
font-size: clamp(1.75rem, 4vw + 1rem, 3.5rem);
}
6. Interactive Workbench: Live Modern CSS Playground
Resize the simulated parent container using the range slider and toggle the checkbox to see @container queries and the :has() parent selector react in real-time!
/* Live Container & :has() Engine */
.demo-card-wrapper {
container-type: inline-size;
container-name: demoBox;
}
/* When container >= 380px -> Horizontal flex */
@container demoBox (min-width: 380px) {
.modern-widget {
flex-direction: row;
align-items: center;
}
}
/* :has() parent styling */
.modern-widget:has(input[type="checkbox"]:checked) {
border: 2px solid #10B981;
background: #ECFDF5;
}
Container-Aware Card
Layout flexes automatically based on container width!
🎯 Placement Interview Key Takeaways & Common Traps
- Trap 1: Forgetting
container-type— An element cannot respond to@containerrules unless an ancestor hascontainer-type: inline-sizeorcontainer-type: sizedeclared. - Trap 2: Can a container query itself? — No! An element cannot query its own dimensions with
@container. Container queries can only style descendants of the container to prevent infinite layout circularity. - Trap 3: Specificity of Nesting with
&— In CSS Nesting,&is parsed similarly to:is(). The specificity of:is()is determined by the most specific selector in its arguments list. - Trap 4: Disallowed
:has()Nesting — You cannot nest:has()inside another:has()(e.g.div:has(:has(p))is explicitly forbidden by the CSS specification to avoid pathological performance trees).