CSS has undergone a remarkable transformation over the past few years. What once required JavaScript hacks, preprocessor gymnastics, or third-party libraries is now achievable with native CSS. Here’s a look at the features that have fundamentally changed how we write stylesheets.
Container Queries
For years, responsive design meant responding to the viewport. Container queries changed that paradigm entirely — now components can respond to their own container’s size.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
This is the single biggest shift in responsive design since media queries. Components are finally truly portable — drop them anywhere and they adapt to their context, not the page.
The :has() Selector
Often called the “parent selector,” :has() is far more powerful than that name suggests. It enables styling based on the presence or state of descendant elements.
/* Style a form group when its input is invalid */
.form-group:has(input:invalid) {
border-color: red;
}
/* A card with an image gets different layout */
.card:has(img) {
grid-template-rows: 200px 1fr;
}
This eliminates entire categories of JavaScript that existed solely to toggle parent classes based on child state.
Cascade Layers
@layer gives us explicit control over the cascade — something we’ve been fighting against (or working around with !important) for decades.
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; }
}
@layer components {
.button { padding: 8px 16px; }
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
}
No more specificity wars. Layers make it clear which styles win, regardless of selector complexity.
Subgrid
CSS Grid was revolutionary, but child elements couldn’t participate in a parent’s grid tracks — until subgrid.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.grid-item {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
This is essential for card layouts where you need content to align across siblings.
Native Nesting
No more Sass just for nesting. Native CSS nesting is here and it works exactly as you’d expect:
.card {
padding: 1rem;
& .title {
font-size: 1.5rem;
}
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.1);
}
@media (width >= 768px) {
padding: 2rem;
}
}
View Transitions API
Smooth page transitions without a framework:
@view-transition {
navigation: auto;
}
::view-transition-old(root) {
animation: fade-out 0.3s ease;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease;
}
This bridges the gap between MPAs and SPAs in terms of perceived smoothness.
What This Means
The gap between CSS and what we used preprocessors or JavaScript for has narrowed dramatically. The modern CSS developer needs fewer tools, fewer abstractions, and fewer workarounds. The platform is finally catching up with our ambitions.
The best part? All of these features are supported in every major browser today. There’s no reason not to use them in production.
Write CSS like it’s 2026, because it is.