If you learned CSS a few years ago and haven't looked closely since, you might be surprised by how much has changed. A lot of the JavaScript we used to write for layouts and interactions is no longer necessary.
The handy way to check whether something is safe to use is to look at its "Baseline" status, which is shown on MDN and caniuse. Baseline means the feature works across the current versions of Chrome, Edge, Firefox and Safari. Everything below is Baseline.
Container queries
Media queries respond to the screen size. Container queries respond to the size of a component's container. This is a big deal for reusable components.
A product card can show a horizontal layout when it sits in a wide content area and a stacked layout in a narrow sidebar, using the same HTML and CSS. No more writing three variations of a card for different contexts.
You mark an element as a container with container-type: inline-size, then write @container (min-width: 400px) { ... } rules for its children.
The :has() selector
Often called the "parent selector", though it's more powerful than that. It lets you style an element based on what it contains.
Some things we use it for:
- Style a form group differently when its input is invalid:
.field:has(input:invalid) - Change a card's layout if it contains an image:
.card:has(img) - Highlight a table row when its checkbox is checked
Before :has(), all of these needed JavaScript to add a class.
Native CSS nesting
You can now nest rules directly in plain CSS, the way you would in Sass:
.card { padding: 1rem; & h3 { margin: 0; } }
For small projects, this alone removes the need for a preprocessor.
Cascade layers
@layer lets you control which styles win, regardless of selector specificity or source order. It's a lifesaver when combining a framework like Bootstrap with your own styles. Put the framework in a lower layer and your overrides always win, without !important everywhere.
Subgrid
Grid items can now align their children to the parent grid. The classic use case is a row of cards where the titles, descriptions and buttons line up perfectly across cards, even when the text lengths differ.
Smaller wins
aspect-ratiofor keeping images and videos in proportion without padding tricks.clamp()for fluid typography that scales smoothly between a minimum and maximum size.gapin flexbox for spacing items without margin hacks.accent-colorfor styling checkboxes, radio buttons and range sliders to match your brand in one line.text-wrap: balancefor headings that don't leave a single lonely word on the last line.:focus-visiblefor focus outlines that appear for keyboard users but not on mouse clicks.
What this means for your projects
Less JavaScript means faster pages, fewer things to break, and simpler code to maintain. When we build a new site now, we reach for CSS first and only add JavaScript when there's genuinely no CSS solution. More and more often, there is one.
