,

Nesting CSS Without Hiding The Structure

I have used nested CSS for years, mostly through preprocessors, but I have always had mixed feelings about it.

Nesting can make a stylesheet feel closer to the structure of the component being styled. It keeps related selectors together and reduces the amount of repeated naming. The problem is that it can also hide too much. A file can look tidy at first glance while quietly producing selectors that are too specific, too deep and harder to override later.

Now that native CSS nesting is becoming something I can reasonably think about in modern projects, I have been trying to decide how I actually want to use it. I do not want to copy every old preprocessor habit into the browser. I want the nesting to make the code easier to read, not just shorter.

Starting With The Component Boundary

The place where nesting feels most useful to me is inside a clear component boundary. If I have a card, a navigation item or a form field, nesting can keep the related states and child elements close to the parent. That makes the stylesheet easier to scan because the reader can see the shape of the component in one place.

.article-card {
  padding: 1rem;
  border: 1px solid currentColor;

  & h2 {
    margin-block-start: 0;
  }

  & a {
    text-decoration-thickness: 0.08em;
  }

  &:focus-within {
    outline: 2px solid currentColor;
    outline-offset: 0.25rem;
  }
}

That example feels reasonable because the nesting is shallow and the relationship is obvious. The nested rules belong to the card. They do not require much mental work to understand.

The danger starts when nesting becomes a way of mirroring every layer of the HTML. Just because the markup has several nested elements does not mean the CSS needs to follow every level. Deep nesting can make the final selector much more restrictive than the design actually needs.

Avoiding The Old Sass Problem

The habit I am trying to avoid is turning nesting into indentation for its own sake. I have opened old Sass files where a selector was buried five levels deep and the final CSS was far more specific than anyone intended. It worked until something needed changing, then every override had to fight against that specificity.

.page {
  & .content {
    & .listing {
      & .card {
        & .card-title {
          color: red;
        }
      }
    }
  }
}

That kind of nesting does not help me understand the design. It mostly records the accident of the current markup. If the markup changes later, the CSS becomes fragile. The selector is also much harder to reuse because it depends on the card living inside a very particular chain of ancestors.

My preference is to keep nesting close to states, variants and immediate component relationships. If I need to describe a larger layout relationship, I usually write that selector directly so the dependency is visible. Clear CSS is often more valuable than compact CSS.

States Are Where Nesting Works Well

States are one of the places where nesting feels natural. Hover, focus, active and expanded states belong close to the thing they modify. Keeping them beside the base selector makes the behaviour easier to check during development.

.menu-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;

  &:hover {
    text-decoration: underline;
  }

  &:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 0.25rem;
  }

  &[aria-expanded="true"] {
    font-weight: 700;
  }
}

That reads well to me because the states are part of the same decision. The button has a base style, then the nearby rules explain how it behaves when someone interacts with it. There is no need to search elsewhere in the file for the accessible state.

It also encourages me to think about focus at the same time as hover. That is a good habit. When the states are grouped together, it is easier to notice if one has been forgotten.

Nesting Should Not Replace Naming

One mistake I want to avoid is using nesting as a substitute for meaningful class names. A good class name still carries intent. It tells another developer what the element is for. If I rely entirely on nested element selectors, the CSS can become too dependent on the current HTML shape.

For repeated components, I still prefer useful class names on the important elements. Nesting can then organise the rules, but the naming still explains the structure. That gives the code a better chance of surviving future changes.

.pricing-card {
  container-type: inline-size;

  & .pricing-card__price {
    font-size: clamp(2rem, 8cqi, 4rem);
  }

  & .pricing-card__features {
    margin-block-start: 1.5rem;
  }
}

That approach might look slightly repetitive, but I would rather have repetition that explains the component than clever nesting that hides it.

What I Take From It

Native CSS nesting is useful, but I do not think it should change the basic discipline of writing CSS that someone can understand later. The browser supporting a feature does not automatically mean every old habit deserves to come with it.

The version of nesting I want to keep using is shallow, deliberate and close to the component it describes. It should make relationships easier to see, keep states near their base styles and reduce harmless repetition. Once it starts hiding structure, increasing specificity or making selectors depend on too much surrounding markup, it has stopped helping.