Modern CSS Features Worth Using in 2026 (and What to Skip)

The CSS worth adopting in 2026 is container queries, :has(), native nesting, cascade layers, and oklch color. Skip subgrid workarounds and most new at-rules you won't ship. The real win is deleting JavaScript and Sass you no longer need.
Part of the complete web design guide.
On this page
- Container queries changed how I structure components
- :has() is the parent selector, and it’s finally safe to lean on
- Native nesting is fine now, with one rule
- Color: oklch and relative color syntax
- View transitions for the multi-page feel
- What I’d still skip in 2026
- How this plays out on real projects
- FAQ
- Do I need a build step to use these features in 2026?
- What’s the biggest mistake when adopting :has()?
- Are view transitions worth it for a small site?
Most “CSS trends” posts are written by people who haven’t shipped a stylesheet in years. I’ve been building front ends for a long time, and the interesting story in 2026 isn’t a single new property. It’s that a pile of features we used to fake with JavaScript or a preprocessor are now just baseline CSS. Here’s what’s actually worth adopting, what still needs care, and what I’d leave alone for now.
Container queries changed how I structure components
Media queries ask about the viewport. Container queries ask about the parent. That sounds small until you build a card that lives in a wide hero on one page and a narrow sidebar on another. Before container queries, that card either needed two variants or a JS resize observer. Now it just responds to its own box.
.card-wrap {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 34rem) {
.card {
display: grid;
grid-template-columns: 12rem 1fr;
gap: 1.25rem;
}
}
The gotcha: container-type: inline-size creates containment, which means the container’s size no longer depends on its contents in the inline axis. If you slap it on everything, layouts that used to size to their children will collapse. Put it on the wrapper you actually want to query, not on the card itself.
:has() is the parent selector, and it’s finally safe to lean on
For years people asked for a parent selector and got told to use JavaScript. :has() shipped across the major engines and by 2026 you can use it without a polyfill. The practical wins are form states and layout shifts that used to need a class toggled from a script.
/* Style the field group only when its input is invalid and touched */
.field:has(input:user-invalid) {
border-color: oklch(0.62 0.19 25);
}
.field:has(input:user-invalid) .hint {
display: block;
}
/* Push a sidebar only when it actually contains a nav */
.layout:has(> aside nav) {
grid-template-columns: 1fr 18rem;
}
Two things to watch. Specificity of :has() is the specificity of its most specific argument, so it can surprise you in a cascade you thought you had under control. And keep selectors shallow. .a:has(.b .c .d) is a debugging nightmare waiting to happen.
Native nesting is fine now, with one rule
CSS nesting works in browsers and, unlike the early days, the parser doesn’t choke on the way most of us write it. You can nest without the & for element selectors, but I still use it for pseudo-classes and combinators because it reads clearer.
.nav {
display: flex;
gap: 0.75rem;
a {
color: inherit;
text-decoration: none;
}
&:hover a {
opacity: 0.85;
}
@media (width >= 48rem) {
gap: 1.5rem;
}
}
If you’re keeping a preprocessor around purely for nesting, you can probably drop it. I still reach for Sass on big codebases for mixins and functions, but for a typical marketing site or app shell, plain CSS with nesting and custom properties covers it.
Color: oklch and relative color syntax
Hex and rgb are fine, but oklch() gives you perceptually even lightness, which means building a tint and shade scale stops being guesswork. Same lightness value, different hues, actually looks balanced.
:root {
--brand: oklch(0.62 0.16 250);
--brand-hover: oklch(from var(--brand) calc(l - 0.08) c h);
--brand-muted: oklch(from var(--brand) l calc(c * 0.4) h);
}
Relative color syntax (oklch(from ...)) lets you derive colors from a single token instead of hardcoding five variants. If you maintain a design system, this alone removes a lot of copy-paste. Just confirm your target browsers, because the fallback story for older Safari is the usual “ship a static value first” routine.
View transitions for the multi-page feel
The View Transitions API started on single-page apps and now works for cross-document navigation in the browsers that matter. You get a smooth morph between pages with a few lines and no framework.
@view-transition {
navigation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 220ms;
}
Where it earns its keep: list-to-detail navigation, image galleries, and anything where the user’s eye needs to follow an element across a page change. Where it hurts: if you animate everything, the site feels slow even when it isn’t. Respect prefers-reduced-motion or you’ll make people motion-sick.
What I’d still skip in 2026
- Anchor positioning. Genuinely useful for tooltips and popovers, but support is uneven enough that you’ll want a fallback. Fine for progressive enhancement, risky as your only positioning strategy.
- Scroll-driven animations everywhere. Great for a hero or a progress bar. Cargo-culted onto every section, it’s a performance tax and a usability problem.
- CSS
if()and other experimental conditionals. Interesting, not something I’d put in production CSS yet.
None of this replaces knowing the cascade. You can adopt every feature above and still ship a mess if your specificity is a war zone. The features just remove the JavaScript tax we used to pay for layout logic.
How this plays out on real projects
On client work, the order I usually follow is: container queries for component layout, :has() for state, nesting for readability, oklch for the palette, and view transitions last, once navigation is stable. If you’re a business owner wondering why your site feels dated next to a competitor’s, it’s often not the colors. It’s that the layout was built for one screen width and everything else is a compromise. That’s the kind of thing I fix in my Long Island web design work, and it’s usually a smaller job than people expect.
FAQ
Do I need a build step to use these features in 2026?
No. Container queries, :has(), nesting, and oklch all run in current browsers without a preprocessor or bundler. A build step is still handy for minification and cache busting, but it’s no longer required to write modern CSS.
What’s the biggest mistake when adopting :has()?
Writing long, deeply nested selectors with it. Keep the argument short, expect its specificity to match the most specific part, and test the cascade. It’s a layout and state tool, not a place to prove how clever your selector is.
Are view transitions worth it for a small site?
If you have list-to-detail navigation or a gallery, yes, and it’s a few lines of CSS. If your site is a handful of static pages with no shared elements between them, the payoff is small. Add it where the user’s eye actually tracks something across a navigation.


