# Rediscovering Modern CSS

> The problems we used to solve with JavaScript and preprocessors now have native CSS answers: container queries, :has(), and native nesting.

- Published: 2026-06-07
- Category: Interface
- Tags: CSS
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/rediscovering-modern-css/
- Language: en-US
- Author: Muhammet Şafak

---
I learned CSS years ago and for a long time I assumed I already knew it. We built layouts with floats, vertical centering was a little puzzle, and whenever we needed to style a component based on some measured condition we reached for JavaScript. [Over the past few years, CSS quietly transformed into a different language](/en/blog/tailwind-css-4-new-engine-and-configuration/) — and I was slow to notice, because I kept writing it the old way. This post covers the three modern CSS additions that have saved me the most time.

## Container queries: the component decides its own context

We spent years writing media queries: "if the screen is this wide, do this." But how a component should look usually depends not on the screen width, but on **the width of its containing area**. The same card component might live inside a wide content column and a narrow sidebar at the same time.

Container queries solve exactly this:

```css
.kart-listesi {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .kart { grid-template-columns: auto 1fr; }
}
```

Now `.kart` lays itself out based on the width of `.kart-listesi`, not the viewport. This removes a long-standing barrier to writing truly reusable components.

## `:has()`: CSS finally has a parent selector

For years, the most-wanted CSS feature was "select an element based on its contents." Saying "align cards that contain an image differently" meant JavaScript or an extra class.

```css
.kart:has(img) { grid-template-rows: 200px 1fr; }
.form-alani:has(input:invalid) { border-color: crimson; }
```

`:has()` is more than just a parent selector — it lets you select an element based on any descendant state. A lot of things I used to handle with a small JavaScript listener are now a single line of CSS.

## Native nesting: one less reason to reach for a preprocessor

I used [Sass](/en/blog/writing-maintainable-css-with-sass/) for years almost exclusively for two things: variables and nesting. CSS custom properties have long taken over the variables job; nesting is now built into CSS itself:

```css
.kart {
  padding: 1rem;
  & .baslik { font-weight: 600; }
  &:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); }
}
```

On smaller projects, this can eliminate a build step and a dependency entirely. Every dependency is a maintenance burden; if the language itself does the job, there is no reason to carry the extra layer.

## Adopting these features incrementally

There is a balance I keep in mind when using all three of these features: browser support. `:has()`, container queries, and native nesting all enjoy wide support now, but every project has a different audience. So I tend to use them through a *progressive enhancement* lens — the baseline experience holds up without them, and they simply add a layer of improvement on top.

A concrete example: in an older browser that does not support container queries, the card component falls back to a reasonable single-column layout — not perfect, but entirely usable. The user never perceives anything as broken; they just see a slightly simpler layout. Adopting a new CSS feature does not mean sprinkling it everywhere; it means being confident it degrades gracefully when unsupported. That discipline lets me start using new features the day they ship, without turning adoption into an all-or-nothing decision.

## The real lesson

What these three additions have in common is this: problems we spent years solving with JavaScript, preprocessors, or extra markup are now solved by the platform itself. The deeper lesson here is not technical: **it is about relearning a tool you thought you already knew.** It is easy to learn a technology once, put it on the shelf, and say "I know that one" — but even mature tools evolve quietly. I have made it a habit to read the release notes of a tool I think I know well at least once a year. More often than not, that is where I discover that something I had been doing the hard way for years now takes a single line. Experience has a trap: it convinces you the world froze at the moment you learned it.
