Modern CSS: a complete overview

What modern CSS can do: sixteen features with their support, what used to need JavaScript or Sass, pros and cons, four examples checked in a browser, mistakes and rules.

Stack and technologies Updated

In short

Modern CSS is the set of features that appeared in browsers over the last few years and changed how sites are built: container queries, the :has() selector, subgrid, nesting, cascade layers, new colour functions and fluid sizes. Together they remove most of the JavaScript that used to be written for layout and states, and replace Sass for everyday work. A card adapts to the column it is placed in, a form reacts to input without scripts, cards in a row align by themselves. The main features already work in all modern browsers; the newest ones are added carefully, with a fallback.

Modern CSS at a glance

The main facts in one table — what is meant, what it replaces and how to check support.

What it is
CSS features that reached all major browsers in recent years
Main features
Container queries, :has(), subgrid, nesting, @layer, oklch(), clamp()
Replaces
Layout scripts, part of the interface libraries, Sass for everyday work
Build
Not needed — the browser reads it as is
Support
Checked by the Baseline label on MDN and caniuse
Fallback
@supports — new features only where the browser understands them
Standard
W3C CSS Working Group; browsers agree on priorities in the yearly Interop project

What modern CSS can do: 16 features

Each feature with the task it solves and its support. “All modern browsers” means current Chrome, Safari, Firefox and Edge; for the newest features check support before use.

FeatureWhat it solvesSupport
Grid two-dimensional layout of a page or a block everywhere
subgrid aligning the insides of neighbouring cards all modern
@container a component adapts to its place, not to the screen all modern
:has() styling an element by what is inside it all modern
nesting rules inside rules, as in Sass all modern
@layer the order of the cascade without !important all modern
@property typed variables that can be animated all modern
oklch(), color-mix() even colours and shades without a preprocessor all modern
clamp() fluid sizes of text and spacing everywhere
dvh, svh the real screen height on phones all modern
text-wrap: balance even lines in headings all modern
light-dark() light and dark theme in one line all modern
@starting-style animation of appearing without scripts all modern
@scope styles limited to one part of the page new — check
anchor() tooltips and menus tied to an element new — check
animation-timeline animation driven by scrolling new — check

What used to need JavaScript or Sass

Ten everyday tasks: how they were solved before and what does it now.

TaskBeforeNow
A card for a wide and a narrow column a script measuring the width @container
Highlight a field with an error a script adding a class :has(:user-invalid)
Equal-height parts of cards a script or fixed heights subgrid
Lock scrolling under a window a class on body from a script html:has(dialog[open])
Nested rules and variables Sass nesting and custom properties
Hover shade of a colour Sass darken() color-mix()
Text size between phone and desktop several media queries clamp()
Full-height first screen on a phone a script reading the window height 100dvh
A tooltip next to a button a positioning library anchor positioning
Smooth appearing of a window an animation library @starting-style

Pros and cons of modern CSS

Modern CSS moves work from scripts to the browser. The gains and the costs both come from that.

Pros · 6

  • Less JavaScript

    Layout and states no longer wait for scripts to load and run.

  • Faster pages

    Fewer libraries and no layout shifts from scripts help Core Web Vitals.

  • Components that adapt themselves

    One card works in the main column, the sidebar and a modal.

  • No build step

    Nesting and variables no longer need a preprocessor.

  • Order in the cascade

    Layers settle conflicts without !important and specificity tricks.

  • Better accessibility

    States are tied to real HTML: a native dialog, form validation, focus.

Cons · 4

  • Old browsers

    Outdated phones and some in-app browsers need a fallback.

  • Uneven support of the newest

    Anchor positioning and scroll animations are checked for each project.

  • Knowledge goes out of date

    Many developers still solve with scripts what CSS already does.

  • Silent failures

    A rule the browser does not understand is skipped without an error.

What modern CSS looks like: 4 examples

Four tasks that used to need scripts. Each was checked in a real browser with real input from the keyboard.

Container queries

The same card is two columns in a 640px column and one in a 300px sidebar — on any screen width.

card.css
/* Markup: <section class="cards"> <article class="card"> <img> <div>…</div> </article> </section> */

/* The section becomes a container: the cards inside measure its width */
.cards {
  container-type: inline-size;
}

.card {
  display: grid;
  gap: 1rem;
}

/* The card adapts to the place it is put in, not to the screen:
   in a wide column — picture on the left, in a narrow sidebar — on top */
@container (width > 28rem) {
  .card {
    grid-template-columns: 10rem 1fr;
    align-items: start;
  }
}

The :has() selector

The field turns red only after the person typed and left it, not on an empty form.

states.css
/* The field reacts to what was typed into it — without JavaScript */
.field:has(input:user-invalid) {
  --field-color: oklch(55% 0.2 25);
}
.field label,
.field .hint {
  color: var(--field-color, inherit);
}
.field input {
  border: 1px solid var(--field-color, #cfd1cf);
}

/* A card with a picture gets two columns, a card without one stays single */
.card:has(> img) {
  grid-template-columns: 8rem 1fr;
}

/* While a dialog is open, the page underneath does not scroll */
html:has(dialog[open]) {
  overflow: hidden;
}

Subgrid

Three cards with texts of different length: the buttons stand on one line.

cards.css
/* Cards in a row: titles, texts and buttons line up across cards,
   however long the text in each one is */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1.5rem;
}

.card {
  display: grid;
  grid-row: span 3;              /* title, text, button */
  grid-template-rows: subgrid;   /* rows are shared with the neighbours */
  gap: 0.75rem;
}

Layers, tokens and nesting

.hidden from the last layer beats even an id selector from an earlier one; the text is 16px at 375px and 20px at 1440px.

base.css
/* The order of layers is declared once: a later layer wins
   regardless of selector weight — no !important needed */
@layer reset, tokens, components, utilities;

@layer tokens {
  :root {
    --ink: oklch(21% 0.006 150);
    /* fluid size: 16px on a phone, 20px on a wide screen, no media queries */
    --text-base: clamp(1rem, 0.9rem + 0.4vw, 1.25rem);
  }
}

@layer components {
  .button {
    min-height: 2.75rem;
    padding-inline: 1.25rem;
    border: 0;
    font-size: var(--text-base);
    background: var(--ink);
    color: white;
    transition: background-color 0.15s;

    /* nesting without Sass */
    &:hover {
      background: color-mix(in oklab, var(--ink) 80%, white);
    }
    &:focus-visible {
      outline: 2px solid var(--ink);
      outline-offset: 2px;
    }
  }
}

@layer utilities {
  .hidden {
    display: none;
  }
}

Common mistakes with modern CSS

  1. A query without a container

    Without container-type on a parent, @container silently does nothing.

  2. size instead of inline-size

    A container of type size loses its height from content and collapses.

  3. 100vh on phones

    The browser bar covers the bottom of the first screen; 100dvh takes it into account.

  4. :invalid instead of :user-invalid

    An empty form is all red before the person has typed a single letter.

  5. New features without a fallback

    Where the browser does not support a feature, the layout falls apart instead of becoming simpler.

  6. Layers announced in pieces

    The order of layers is set by the first mention; declared in different files, it becomes random.

7 rules for modern CSS in a real project

  1. 01

    Check Baseline

    Before using a feature, look at its label on MDN: widely available, newly available or limited.

  2. 02

    New on top of working

    The basic layout works everywhere, and @supports adds the improvement where it is understood.

  3. 03

    Tokens in variables

    Colours, spacing and sizes are custom properties in one place.

  4. 04

    Layers at the top

    One line @layer reset, tokens, components, utilities at the start of the main file.

  5. 05

    Containers for components

    Media queries for the page, container queries for what lives inside it.

  6. 06

    Logical properties

    padding-inline and margin-block instead of left and right — ready for any writing direction.

  7. 07

    Respect reduced motion

    Every animation has a prefers-reduced-motion branch.

Questions about modern CSS

What is modern CSS?

The features of recent years — container queries, :has(), subgrid, nesting, layers — that replace scripts and preprocessors.

Do I still need Sass?

For a new project usually not: nesting, variables and colour functions are in CSS itself.

How do container queries differ from media queries?

Media queries look at the screen, container queries at the block the component is placed in.

Is :has() slow?

No, in ordinary use; very broad selectors such as *:has(…) on a huge page are the exception.

What about older browsers?

They get a simpler but working layout; improvements are added through @supports.

Does modern CSS help SEO?

Indirectly: less JavaScript and fewer layout shifts improve Core Web Vitals.

Can I animate page transitions with CSS?

Yes, with the View Transitions API — one line of CSS for transitions between pages of a site.

Online form

Layout
without excess

I build on modern CSS and a token system: adaptive layout without libraries, states without scripts, fast pages. Tell me about the project — I answer within one working day.

Or write to [email protected]