WhollySoftware
Back to blogWeb

CSS in 2026: Container Queries, :has(), and What We No Longer Reach For

Wholly Software TeamMarch 11, 20266 min read
CSS in 2026: Container Queries, :has(), and What We No Longer Reach For

Container queries have changed how we build reusable components more than any CSS feature since flexbox. A card component that needs to switch from a horizontal to vertical layout depending on the width of its parent container — not the viewport — used to require either duplicating the component with different props or reaching for a ResizeObserver in JavaScript. With @container queries, the card itself declares its own responsive behavior based on its container's width, and the same component now drops correctly into a wide dashboard panel or a narrow sidebar without any JavaScript or prop plumbing.

The :has() selector eliminated a specific category of JavaScript we used to write constantly: styling a parent based on a child's state. A form group that needs different border styling when it contains an invalid input used to require a JS class toggle on the parent. Now .form-group:has(:invalid) handles it directly in CSS. We removed a small internal utility library that existed mostly to handle this exact pattern across projects, because :has() support reached full coverage across evergreen browsers.

We've mostly stopped reaching for CSS-in-JS libraries on new projects. When we started most React work, styled-components was close to a default choice, giving us scoped styles and dynamic theming. With native CSS nesting now broadly supported and CSS custom properties handling dynamic theming without a runtime style-generation step, plain CSS Modules give us the same scoping without the runtime cost and the hydration-mismatch bugs that CSS-in-JS occasionally introduced in server-rendered apps.

Not every new CSS feature has changed our defaults. We've tried scroll-driven animations for a couple of client projects and pulled back — browser support gaps and jank on lower-end Android devices meant the JavaScript-based alternative, using Intersection Observer, was still the more reliable choice for anything beyond a simple fade-in. New CSS is worth adopting where it removes a whole category of JavaScript, not just because it's new.

The overall shift has been fewer lines of JavaScript per component and fewer runtime dependencies, which matters for bundle size independent of any specific feature. A component library we maintain across several client projects is roughly 15% smaller in JS terms than its equivalent from two years ago, almost entirely from retiring utilities that native CSS now handles on its own.

CSSFrontendWeb Standards
CSS in 2026: Container Queries, :has(), and What We No Longer Reach For — Wholly Software