CSS Nesting

What You'll Learn

  • How to use native CSS nesting without preprocessors
  • Understanding the & parent selector
  • Nesting pseudo-classes and pseudo-elements
  • Using combinators with nested selectors
  • Nesting media queries within component styles
  • Differences between CSS nesting and SASS/LESS
  • Syntax rules and best practices for nesting
  • When to use nesting vs. traditional selectors

Introduction to CSS Nesting

CSS nesting is a native browser feature that allows you to write selectors inside other selectors, keeping related styles organized in one place. Previously, this was only available through preprocessors like SASS or LESS, but now it's built directly into CSS.

Nesting helps you write more maintainable CSS by grouping component-related styles together and reducing repetition. Instead of writing .card h3, .card p, and .card .meta as separate rules, you can nest them all inside the .card block.

Basic Nesting Syntax

The simplest form of nesting is placing child selectors inside a parent selector block. This creates descendant selectors automatically. When you nest a selector, the browser generates the full selector path for you.

Syntax

.card { background: white; border: 1px solid #e0e0e0; padding: 1.5rem; h3 { color: #667eea; margin-top: 0; } p { color: #666; line-height: 1.6; } .meta { color: #999; font-size: 0.875rem; } }

This nested CSS is equivalent to writing:

.card { background: white; border: 1px solid #e0e0e0; padding: 1.5rem; } .card h3 { color: #667eea; margin-top: 0; } .card p { color: #666; line-height: 1.6; } .card .meta { color: #999; font-size: 0.875rem; }

Live Example

The & Parent Selector

The ampersand (&) represents the parent selector and is crucial for creating modifier classes, pseudo-classes, and pseudo-elements. It's replaced with the parent selector when the CSS is processed.

Modifier Classes

Use & to create variations of a component by adding modifier classes:

.button { background: #667eea; color: white; padding: 0.75rem 1.5rem; border: none; &.secondary { background: #6c757d; } &.success { background: #28a745; } &.danger { background: #dc3545; } }

This generates .button.secondary, .button.success, and .button.danger. The & is replaced with .button.

Live Example

Nesting Pseudo-Classes and Pseudo-Elements

Pseudo-classes like :hover, :focus, and :valid, along with pseudo-elements like ::before and ::placeholder, can be nested using the & parent selector.

Pseudo-Classes

input { width: 100%; padding: 0.75rem; border: 2px solid #ddd; &:focus { border-color: #667eea; outline: none; } &:hover { border-color: #bbb; } &:valid { border-color: #28a745; } &:invalid { border-color: #dc3545; } }

Pseudo-Elements

input { &::placeholder { color: #aaa; font-style: italic; } &:disabled { background: #f5f5f5; cursor: not-allowed; } }

Live Example

Nesting with Combinators

CSS combinators (> direct child, + adjacent sibling, ~ general sibling) work perfectly with nesting. Use & before the combinator to reference the parent.

Direct Child Combinator

.article { background: white; padding: 2rem; & > header { border-bottom: 2px solid #f0f0f0; padding-bottom: 1rem; } & p { color: #666; line-height: 1.6; } }

Adjacent Sibling Combinator

.article { margin-bottom: 1.5rem; & + .article { margin-top: 2rem; border-top: 3px solid #667eea; } }

This targets articles that immediately follow another article, adding extra spacing and a top border.

Live Example

Nested Media Queries

One of the most powerful features of CSS nesting is the ability to nest media queries directly within a component's styles. This keeps all responsive behavior for a component in one place instead of scattered in separate @media blocks.

Syntax

.responsive-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 1.5rem; h3 { font-size: 1.25rem; } p { font-size: 0.95rem; } @media (min-width: 768px) { padding: 2.5rem; h3 { font-size: 1.75rem; } p { font-size: 1.05rem; } } @media (min-width: 1024px) { padding: 3.5rem; h3 { font-size: 2.25rem; } p { font-size: 1.15rem; } } }

Responsive Grid Example

.grid { display: grid; grid-template-columns: 1fr; gap: 1rem; @media (min-width: 640px) { grid-template-columns: repeat(2, 1fr); } @media (min-width: 1024px) { grid-template-columns: repeat(3, 1fr); } }

Live Example

Differences from SASS/LESS

While native CSS nesting looks similar to SASS and LESS, there are important differences to be aware of. Understanding these differences helps you write correct CSS and avoid common mistakes when transitioning from preprocessors.

Type Selectors Require &

In SASS, you can nest type selectors directly. In native CSS, type selectors must use & to be explicit about the relationship:

/* SASS - this works */ .card { p { color: blue; } } /* Native CSS - you can write either */ .card { p { color: blue; } /* Works - creates .card p */ & p { color: blue; } /* Also works - more explicit */ }

No Parent Selector Interpolation

SASS allows placing the parent selector anywhere in the nested selector. Native CSS only supports & at the beginning:

/* SASS - this works */ .button { .dark-theme & { color: white; } } /* Generates: .dark-theme .button */ /* Native CSS - this doesn't work */ .button { .dark-theme & { /* Not supported */ color: white; } }

No Automatic Concatenation

SASS allows creating new class names by concatenating strings. Native CSS does not:

/* SASS - this works */ .btn { &-primary { background: blue; } &-secondary { background: gray; } } /* Generates: .btn-primary, .btn-secondary */ /* Native CSS - this doesn't work as expected */ .btn { &-primary { background: blue; } /* Not supported */ }

Syntax Rules and Gotchas

Native CSS nesting has specific syntax rules that differ slightly from what you might be used to with preprocessors. Following these rules ensures your nested CSS works correctly across all browsers.

When to Use &

  • Always use & for pseudo-classes: &:hover
  • Always use & for pseudo-elements: &::before
  • Always use & for modifier classes: &.active
  • Always use & for combinators: & > child
  • Optional for type selectors: Both p and & p work
  • Optional for class selectors: Both .child and & .child work

Valid Nesting

.parent { color: blue; /* All of these are valid */ &:hover { color: red; } &.active { font-weight: bold; } & > .child { margin: 0; } .descendant { padding: 1rem; } p { line-height: 1.6; } @media (min-width: 768px) { font-size: 1.2rem; } }

Invalid Nesting

.parent { color: blue; /* These are NOT valid */ .theme & { color: red; } /* & must be at start */ &-modifier { color: green; } /* No concatenation */ :hover { color: orange; } /* Missing & for pseudo-class */ }

When to Use Nesting

While nesting is powerful, it's not always the best choice. Understanding when to nest and when to write separate selectors helps you maintain clean, performant CSS.

Good Use Cases

  • Component styles - Keep all styles for a component together
  • State variations - :hover, :focus, :active states
  • Modifier classes - .button.primary, .card.featured
  • Media queries - Responsive behavior for a specific component
  • Pseudo-elements - ::before, ::after, ::placeholder
  • Immediate children - & > .child when limiting scope

When to Avoid Nesting

  • Deep nesting - More than 3-4 levels becomes hard to read
  • Unrelated selectors - Don't nest just to group code
  • Simple global styles - Base element styles don't need nesting
  • Overly specific selectors - Nesting increases specificity

Example: Good vs. Overly Nested

/* Good - 2-3 levels, clear structure */ .card { padding: 1rem; .card-header { border-bottom: 1px solid #eee; h3 { margin: 0; } } &:hover { box-shadow: 0 4px 8px rgba(0,0,0,0.1); } } /* Too deep - hard to read and overly specific */ .container { .sidebar { .widget { .widget-header { .widget-title { .widget-icon { /* This is 6 levels deep! */ color: blue; } } } } } }

Key Takeaways