Modern CSS 2026 — Container Queries, CSS Grid, Layers, and New Features
Advertisement
Introduction
Why This Matters
CSS in 2026 is unrecognizable compared to 2020. Container queries, cascade layers, native nesting, :has(), color-mix(), and subgrid are all baseline-supported across browsers. These features replace entire libraries and JavaScript workarounds that were standard practice just a few years ago.
Container Queries
Respond to the parent container size, not the viewport:
/* Mark an element as a container */
.card-grid {
container-type: inline-size;
container-name: card-grid;
}
/* Style children based on container width */
@container card-grid (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container card-grid (min-width: 900px) {
.card {
grid-template-columns: 280px 1fr;
}
.card-title {
font-size: 1.5rem;
}
}Container query units:
.responsive-text {
/* cqi = 1% of the container's inline size */
font-size: clamp(1rem, 4cqi, 2rem);
padding: 2cqi;
}CSS Cascade Layers
Layers give explicit control over specificity — no more !important wars:
/* Define layer order (lower = lower priority) */
@layer reset, base, components, utilities;
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
}
a { color: var(--color-link); }
}
@layer components {
.btn {
display: inline-flex;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
background: var(--color-primary);
color: white;
}
/* Overrides base layer a color inside .btn */
.btn a { color: inherit; }
}
@layer utilities {
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
}
/* Unlayered styles win over all layers regardless of order */
.override { color: red; }CSS Nesting
Native CSS nesting — no preprocessor needed:
.card {
background: white;
border-radius: 0.5rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
/* Nest selectors with & */
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.15);
transform: translateY(-2px);
}
& .card-title {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
& .card-body {
color: var(--color-text-secondary);
line-height: 1.7;
}
@media (min-width: 768px) {
padding: 2rem;
}
}The :has() Selector
Target parents based on their children — the "parent selector" CSS never had:
/* Card with an image gets different padding */
.card:has(img) {
padding: 0;
}
.card:has(img) .card-content {
padding: 1.5rem;
}
/* Form label styling based on required input */
.form-group:has(input:required) label::after {
content: ' *';
color: var(--color-danger);
}
/* Navigation item with active child */
.nav-item:has(> .active) {
background: var(--color-primary-light);
border-left: 3px solid var(--color-primary);
}
/* Grid changes when it contains fewer than 3 items */
.grid:not(:has(:nth-child(3))) {
grid-template-columns: repeat(2, 1fr);
}CSS Subgrid
Align nested elements to the parent grid:
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.product-card {
display: grid;
/* Inherit the parent's column tracks */
grid-template-rows: subgrid;
grid-row: span 4; /* image | title | description | price+button */
}
/* All cards now align their titles, descriptions, and buttons in the same row */
.product-card img { grid-row: 1; }
.product-card h2 { grid-row: 2; align-self: start; }
.product-card p { grid-row: 3; }
.product-card footer { grid-row: 4; align-self: end; }color-mix() and Modern Color Functions
:root {
--color-brand: oklch(55% 0.22 264);
/* Mix to create tints/shades */
--color-brand-light: color-mix(in oklch, var(--color-brand) 30%, white);
--color-brand-dark: color-mix(in oklch, var(--color-brand) 80%, black);
--color-brand-alpha: color-mix(in oklch, var(--color-brand) 20%, transparent);
}
/* oklch gives perceptually uniform colors — equal lightness = same perceived brightness */
.success { background: oklch(65% 0.18 145); }
.warning { background: oklch(65% 0.18 80); }
.danger { background: oklch(65% 0.18 25); }Common Mistakes
- Using
@mediaqueries for component-level responsive design instead of@container - Not defining a
@layerorder at the top of the stylesheet — layers defined out of order behave unpredictably - Nesting too deeply — CSS nesting is readable up to 3 levels; deeper nesting hurts maintainability
- Using
color: oklch(...)without a fallback for Safari 15 or older — use@supportsfor progressive enhancement - Using
!importantinstead of reorganizing cascade layers
Best Practices
- Use
@layerfor all CSS including third-party libraries to make specificity predictable - Use
container-type: inline-sizeon layout containers and@containerfor component breakpoints - Write colors in
oklchfor accessible, perceptually uniform palettes - Use
:has()to reduce JavaScript-driven class toggling for CSS-expressible logic - Use
subgridto align card content across different cards without JavaScript measurement
Key Takeaways
- Container queries (
@container) respond to parent size, enabling truly reusable components - Cascade layers (
@layer) give explicit precedence control — replacing!importanthacks - Native CSS nesting is baseline-supported in all major browsers since 2024
:has()is the long-awaited "parent selector" — enables selecting elements by their children- CSS Subgrid aligns nested items to an ancestor grid without extra wrappers
oklch()colors are perceptually uniform — equal L values have equal perceived lightnesscolor-mix()creates tints, shades, and alpha variants without preprocessorscqicontainer query units (1% of inline size) allow fluid typography inside components
Advertisement