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

Use container queries for component-level responsiveness, :has() to replace parent-state JavaScript, and native nesting, cascade layers, and :is()/:where() for cleaner stylesheets. Drop float grids, flexbox fallbacks, vendor prefixes, and most IE-era hacks. Modern CSS handles what needed libraries or JavaScript a few years ago.
Part of the complete web design guide.
On this page
- Container queries changed how I build components
- :has() killed a lot of my JavaScript
- Native nesting is finally here, with a catch
- Custom properties and color-mix replaced my theme tooling
- What I’ve stopped doing
- How I decide what to ship
- FAQ
- Do I still need Sass in 2026?
- Are container queries safe for production now?
- Does :has() hurt performance?
Most of the CSS I wrote ten years ago is now dead weight. Flexbox fallbacks, float-based grids, vendor prefixes you don’t need, and a pile of JavaScript that existed only because CSS couldn’t do the job yet. In 2026 the platform caught up, and a lot of what used to require a build step or a library is now one or two lines. Here’s what I actually use on client projects, and what I’ve stopped bothering with.
Container queries changed how I build components
Media queries ask about the viewport. That was always the wrong question for a card or a sidebar, because the same component can sit in a wide column on one page and a narrow rail on another. Container queries ask about the parent, which is what you actually care about.
.card-wrap {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
gap: 1rem;
}
@container card (min-width: 30rem) {
.card {
grid-template-columns: 12rem 1fr;
align-items: center;
}
}
That card now adapts to wherever you drop it, no page-specific overrides. Browser support is solid across Chrome, Safari, Firefox, and Edge, so this is a default tool now, not an experiment. If you’re still writing three sets of media queries to make one component responsive, stop.
:has() killed a lot of my JavaScript
The relational pseudo-class lets you style a parent based on what’s inside it. This is the thing we begged for years, and it’s finally usable everywhere.
/* Add spacing only when the form has an error message */
.form-field:has(.error) {
border-color: #c0392b;
}
/* Style a card differently when it contains an image */
.post:has(img) {
padding-top: 0;
}
/* Highlight a nav item when its submenu is open */
.nav-item:has([aria-expanded="true"]) {
background: #f3f4f6;
}
Before :has(), that first example meant toggling a class in JS on every validation run. Now the state lives in the DOM and CSS reacts to it. Fewer classes, fewer bugs, less code to maintain.
Native nesting is finally here, with a catch
I used Sass for nesting for over a decade. Native nesting works now, but the syntax rules differ from Sass in ways that trip people up. The key one: element selectors need & when they start with a letter.
.nav {
display: flex;
gap: 1.5rem;
/* valid: starts with & */
& a {
color: inherit;
text-decoration: none;
}
/* valid: starts with a symbol, no & needed */
:hover {
color: #0a58ca;
}
/* INVALID: bare element selector */
/* a { color: red; } */
}
That last one silently breaks in some setups, so I keep the & on anything that could be ambiguous. It costs nothing and avoids a confusing afternoon. For most projects I’ve dropped Sass entirely and run plain CSS through Lightning CSS or esbuild, which handles nesting, autoprefixing, and minification in one pass.
Custom properties and color-mix replaced my theme tooling
Design tokens as CSS variables are old news. What’s newer and genuinely useful is color-mix(), which lets you derive shades at runtime instead of hardcoding twenty hex values.
:root {
--brand: #2563eb;
--brand-dark: color-mix(in oklab, var(--brand) 80%, black);
--brand-soft: color-mix(in oklab, var(--brand) 15%, white);
}
.button {
background: var(--brand);
border-bottom: 2px solid var(--brand-dark);
}
.button:hover {
background: var(--brand-soft);
}
Change --brand once and the whole palette follows, including hover states and borders. I’ve used this to do live theme switching without a rebuild. The oklab color space keeps the mixes perceptually even, which matters more than you’d think when you’re generating a full scale from one color.
What I’ve stopped doing
- Float layouts. Flexbox and grid cover everything. Floats are for wrapping text around images, full stop.
- Prefixing by hand. Autoprefixer or Lightning CSS reads your browser targets and handles it. Hand-written
-webkit-rules are noise. - Resetting with a huge framework. A short
box-sizingand margin reset does 95% of what the old resets did, without the bloat. - JS for layout. Equal-height columns, sticky footers, and equal-width grids are all CSS problems now.
- Pixel-perfect breakpoints. I design to content widths. Container queries made fixed device breakpoints mostly obsolete.
How I decide what to ship
The rule I use: if it’s in all current evergreen browsers and degrades gracefully, I use it. Container queries, :has(), nesting, custom properties, color-mix(), and clamp() all pass that test in 2026. I don’t ship things behind a flag on a client site unless there’s a clear fallback, because “it works in Chrome” is not a support plan.
If you’re maintaining an older codebase, the biggest win isn’t adding features. It’s deleting the workarounds. Pull out the float grids, the JS class-toggling, and the manual prefixes, and you’ll often cut your stylesheet size in half while making it easier to read. That kind of cleanup is a big part of what I do for Long Island web design clients, and it usually pays for itself in the first round of edits.
FAQ
Do I still need Sass in 2026?
Usually no. Native nesting, custom properties, and a modern bundler cover most of what Sass gave you. The main reasons to keep it are large existing codebases or heavy use of mixins and functions, which CSS still doesn’t have.
Are container queries safe for production now?
Yes. They’re supported in all current versions of Chrome, Safari, Firefox, and Edge. The only real concern is very old browsers, and those are already a small and shrinking slice of traffic.
Does :has() hurt performance?
For normal page markup, no. It can get expensive if you write very broad selectors against huge DOM trees, so scope it to a class or container rather than applying it to * or deeply nested universal selectors.


