C.W.K.
Stream
Lesson 04 of 05 · published

Cascade Layers & Native Nesting: Order And Locality, Built In

~12 min · cascade-layers, layer, native-nesting, modern-css

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Cascade layers end the !important wars. Native nesting eliminates the Sass build step. Both make CSS read like the architecture you intended."

The Cascade Layer Problem It Solves

Before layers, the cascade resolved conflicts by origin → specificity → source order. This made multi-stylesheet projects fragile: a third-party library's .btn { ... } could be overridden by your app's .btn { ... } only if your specificity matched or beat the library's, or if you used !important. Specificity wars proliferated; !important escalated; refactoring became dangerous.

Cascade layers introduce explicit ordering as a higher cascade priority than specificity. You declare layer order once; rules in later layers always beat earlier layers, regardless of specificity.

The @layer Syntax

Three ways to use it:

  1. Declare order: @layer reset, library, app; — establishes the layer names and order (earliest to latest). Layers declared later beat layers declared earlier.
  2. Put rules in a layer: @layer app { .btn { ... } }.
  3. Import into a layer: @import url('library.css') layer(library); — pulls a whole stylesheet into a named layer.

The Canonical Stack

The pattern most modern stylesheets use:

How Cascade Layers Resolve Conflicts

The full cascade order, with layers added:

  1. Origin and importance.
  2. Cascade layer. Later-declared layers beat earlier layers. Rules outside any layer are between layered rules and inline styles — they beat layered rules.
  3. Specificity. Within the same layer.
  4. Source order. Final tiebreaker.

So .btn { background: red } in the library layer is beaten by .btn { background: green } in the app layer, regardless of specificity. No !important, no specificity escalation.

Anonymous Layers

@layer { ... } (no name) creates an anonymous layer. Useful when you don't need to reference the layer by name elsewhere. Anonymous layers are ordered in declaration order.

Native CSS Nesting

The & selector lets you nest rules inside other rules, exactly like Sass — but built into the browser. Baseline 2023:

The & Selector In Depth

  • & represents the parent selector. .card { & .title { ... } } = .card .title { ... }.
  • &:hover = .card:hover (no space).
  • & > p = .card > p.
  • &.featured = .card.featured (compound).
  • Use & explicitly when needed; for direct descendants, the syntax allows skipping it: .card { .title { ... } } = .card .title { ... }.

Nesting + At-Rules

You can nest @media, @container, and @supports inside selectors. This is a huge readability win — the responsive variants of a component live next to the component's base rules:

Combining Layers + Nesting

Put nested rules inside a layer for maximum locality:

Cascade layers replace !important; native nesting replaces Sass. Both are baseline 2023, supported in every modern browser. By 2026, opt-in is the default. If your stylesheet has !important wars or a Sass build step that exists only for nesting, you're maintaining infrastructure the browser now provides for free.

Pippa's Note

The cwkPippa frontend uses Tailwind, which internally uses cascade layers — Tailwind's reset/components/utilities layers compose with project styles via @layer order. No !important in the project except for explicit accessibility overrides. The cwk-site uses native nesting in component CSS files — each .component.module.css groups its base rule, hover state, focus state, and responsive variants in one place. The Sass dependency was removed from cwk-site in late 2025; native nesting covers all the use cases.

Code

Canonical 4-layer architecture·css
/* Step 1: declare layer order */
@layer reset, library, app, utilities;

/* Step 2: assign rules to layers */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; line-height: 1.5; }
  /* small modern reset */
}

@layer library {
  /* @import a vendor file or paste their rules here */
  .btn {
    padding: 0.5rem 1rem;
    border-radius: 4px;
    background: blue;
  }
}

@layer app {
  /* Override the library's btn — no !important needed */
  .btn {
    background: var(--color-accent);
    border-radius: var(--radius-md);
  }
}

@layer utilities {
  /* Always-wins utility classes (think Tailwind) */
  .text-center { text-align: center; }
  .hidden { display: none; }
}
Native nesting·css
/* Native CSS nesting */
.card {
  padding: 1rem;
  border-radius: 8px;
  background: var(--bg-card);
  border: 1px solid var(--border);

  /* Nested selectors using & */
  & .title {
    font-weight: 700;
    margin-bottom: 0.5rem;
  }

  & .description {
    color: var(--fg-muted);
  }

  /* Pseudo-class on parent */
  &:hover {
    border-color: var(--color-accent);
  }

  /* Compound selector — & is the parent */
  &.featured {
    border-width: 2px;
  }

  /* Direct child without & — allowed in nesting */
  > .footer {
    margin-top: auto;
  }
}

/* Equivalent unrolled CSS:
.card { ... }
.card .title { ... }
.card .description { ... }
.card:hover { ... }
.card.featured { ... }
.card > .footer { ... }
*/
Nested at-rules + layered nested component·css
/* Nested @media + @container + @supports */
.card {
  padding: 1rem;
  display: grid;
  grid-template-columns: 1fr;

  @media (width >= 640px) {
    padding: 1.5rem;
  }

  @container (width >= 400px) {
    grid-template-columns: 120px 1fr;
  }

  @supports (display: subgrid) {
    /* progressive enhancement */
  }
}

/* Layers + nesting in one component file */
@layer app {
  .navbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1.5rem;

    & .links {
      margin-left: auto;
      display: flex;
      gap: 1rem;
    }

    &:has(.search:focus-within) {
      box-shadow: 0 0 0 2px var(--color-accent);
    }
  }
}

/* Everything related to .navbar lives in one block.
   The layer keeps the rules overridable from outside. */

External links

Exercise

Build a small project with three CSS layers: reset, library (paste a button style with red background), app (override the button to green). Verify in DevTools that the green wins, regardless of source order. Then add a navbar component using native nesting (the navbar, its links, its hover state, and a media query, all in one nested block). No Sass; no preprocessor.
Hint
If your override doesn't win, check that you put both rules in layers (layered always loses to unlayered). DevTools shows the layer each rule belongs to in the Styles panel. Nesting requires the trailing semicolon and the & for pseudo-classes (&:hover, not :hover).

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.