Modern CSS in 2026: Container Queries, Cascade Layers and More
The CSS you can use today has changed radically. Discover the features that are redefining how we write styles.
The Silent CSS Revolution
CSS has undergone the greatest evolution in its history over the past three years. Features that were only dreams for years or required JavaScript are now native parts of the language. Container queries, cascade layers, the :has() selector, CSS nesting, and view transitions are fundamentally transforming how we write styles.
What's most impressive is that all these features are supported across all modern browsers (Chrome, Firefox, Safari, Edge). They're no longer experiments: they're production-ready tools you can use today. In this article, we'll explore each one in depth with practical examples you can apply immediately to your projects.
The combined impact of these features is so significant that many developers are reconsidering whether they need CSS-in-JS, heavy CSS frameworks, or preprocessors like Sass. Native CSS in 2026 is more powerful, expressive, and maintainable than ever.
Container Queries: Container-Based Responsive Design
Container queries are arguably the most anticipated feature in CSS history. For decades, responsive design was based exclusively on media queries that respond to viewport size. This works well for full-page layouts, but fails when a component can appear in different-sized contexts.
Imagine a product card component that appears in a narrow sidebar, a 3-column grid, and a full-width detail view. With media queries, you'd need to know exactly where the card will be placed to adjust its styles. With container queries, the card automatically adapts to its parent container's size.
To use container queries, you first define a container context on the parent element using container-type: inline-size and optionally give it a name with container-name. Then you use @container to apply conditional styles based on the container's size, analogous to how @media responds to the viewport.
This enables truly reusable components that adapt to any context without needing props, variants, or specific classes. A well-designed component with container queries works correctly regardless of where it's placed in the layout.
Container queries also support style queries with style(), allowing you to condition styles based on the parent container's CSS properties. This opens up possibilities like changing a component's theme based on a container CSS variable.
Cascade Layers (@layer): Total Control Over the Cascade
The CSS cascade has historically been a source of frustration. When multiple rules apply to the same element, conflict resolution is based on specificity, order of appearance, and importance (!important). In large projects, this leads to specificity wars where developers add increasingly specific selectors or !important to override styles.
Cascade layers (@layer) introduce a new level of control over the cascade. You can organize your styles into layers with explicit priority: layers declared first have lower priority than those declared later. This allows you to establish a predictable order without relying on selector specificity.
A common pattern is to define layers for: reset/normalize, base/defaults, components, utilities, and overrides. Styles within a layer always have lower priority than those in the next layer, regardless of selector specificity. This means a utility in the "utilities" layer will always beat a complex selector in the "components" layer, without needing !important.
Cascade layers are especially valuable when integrating third-party CSS (libraries, frameworks, widgets) because you can place them in low-priority layers and cleanly override them from higher layers. They also facilitate gradual migration of legacy styles: you can encapsulate old CSS in one layer and new CSS in another, controlling exactly which takes priority.
The :has() Selector — The "Parent Selector" CSS Always Needed
For over 20 years, developers asked for a "parent selector" in CSS: the ability to style an element based on its children. The :has() selector finally makes this possible, and goes far beyond a simple parent selector.
:has() is a functional selector that accepts a relative selector list as an argument. The element matches if it contains at least one element matching the provided selectors. For example, article:has(img) selects all articles containing at least one image. form:has(input:invalid) selects forms containing at least one invalid input.
The possibilities are enormous and eliminate the need for JavaScript for many common patterns. You can style a label based on the state of its associated input, change a grid's layout based on how many children it has, or apply conditional styles to a container based on the type of content it holds.
:has() also works as an advanced "sibling selector." p:has(+ h2) selects paragraphs immediately followed by an h2, something impossible with traditional selectors. Combined with :not(), you can create complex negative selectors: div:not(:has(> img)) selects divs that don't have images as direct children.
The impact on JavaScript reduction is significant. Many patterns that required event listeners and DOM manipulation can now be expressed purely in CSS, resulting in more declarative, more performant, and easier-to-maintain code.
CSS Nesting: Goodbye to Selector Repetition
CSS nesting allows you to nest selectors within other selectors, eliminating parent selector repetition and making the relationship between styles visually clear. It's one of the most used features of preprocessors like Sass and Less, and is now natively available in CSS.
The native syntax is slightly different from Sass: nested selectors that don't start with a special character (. # : [ &) need the & operator to reference the parent selector. For example, inside .card { }, you need to write & .title { } for the title styles, but you can write :hover { } or .dark & { } directly without &.
CSS nesting significantly reduces CSS verbosity, especially for components with multiple states and variants. A component that previously required 30 lines of repeated selectors can be expressed in 15 lines with nesting, and the hierarchical relationship between styles is immediately visible.
An important consideration is that native nesting has different specificity behavior than Sass. Sass "flattens" nested selectors when compiling, while native nesting uses the new "most specific selector in the list" specificity. In practice, this rarely causes problems, but it's important to understand for debugging.
View Transitions: Animations Between Pages and States
The View Transitions API allows you to create smooth animations between different states of a page or between entirely different pages. Previously, achieving smooth transitions between pages required complex SPA frameworks or heavy animation libraries.
The API works by capturing a snapshot of the old page state and animating the transition to the new state. You can control which elements participate in the transition using view-transition-name, and customize animations with ::view-transition-old and ::view-transition-new pseudo-elements.
The most impactful use case is page transitions in multi-page applications (MPA). With the View Transitions API and the @view-transition CSS attribute, you can create smooth page transitions without converting your site into an SPA. This is especially valuable for content sites, e-commerce, and documentation where MPA has performance and SEO advantages.
For transitions within the same page (like expanding a card to a detail view), the Same-Document View Transitions API allows you to capture the state before and after the DOM change and animate the transition. The result is "shared element transition" type animations similar to native Android/iOS apps, but implemented with pure CSS.
Other Notable Modern Features
CSS Subgrid
Subgrid allows a child grid to inherit the tracks (rows or columns) of the parent grid. This solves the problem of aligning nested elements with the main grid, common in card layouts where you want titles, content, and footers of multiple cards to align perfectly with each other.
Scroll-driven animations
Scroll-driven animations allow you to link CSS animations directly to page or container scrolling, without JavaScript. You can create progress bars, parallax, reveal animations, and complex scroll effects using only CSS with animation-timeline: scroll() or animation-timeline: view().
@scope
The @scope rule allows you to limit the reach of CSS selectors to a specific DOM subtree, similar to how Shadow DOM styles are encapsulated. This is useful for creating component styles that don't affect elements outside the component, without needing Shadow DOM or CSS Modules.
Popover API and anchor positioning
The native Popover API allows you to create popovers, tooltips, menus, and modals without JavaScript, with automatic focus management, click-outside dismissal, and accessibility. Combined with the new Anchor Positioning API, you can position floating elements relative to an anchor element with full control over position and collision behavior.
Do We Still Need CSS Preprocessors and Frameworks?
With all these native features, the need for preprocessors like Sass has significantly decreased. CSS nesting replaces Sass's core functionality, custom properties replace variables, and @layer provides cascade control that previously required external tools. However, Sass remains useful for advanced mixins, functions, and complex mathematical operations that native CSS doesn't yet support.
As for CSS frameworks like Tailwind, they remain relevant for teams that value development speed and design consistency. However, new CSS features allow you to create robust design systems without depending on a framework, giving more control and generating less final CSS.
Conclusion
CSS in 2026 is a mature, powerful, and expressive language. The features we've explored (container queries, cascade layers, :has(), nesting, view transitions) represent a qualitative leap in what's achievable with pure CSS. If you've recently updated your CSS knowledge, you'll be surprised how much you can accomplish without resorting to JavaScript or external tools.
The recommendation is to adopt these features gradually in your projects. Start with CSS nesting and custom properties (the easiest to adopt), then incorporate container queries for reusable components, and finally explore cascade layers to organize your CSS at scale. The future of CSS is bright, and it's already here.