Loading...

CSS Nesting

Variables named your values. calc and friends computed them. Nesting organizes the selectors that use those values: keep a card’s title, button, and hover styles inside .card instead of repeating the parent name across the file.

Nest a child selector

Write a rule inside another rule. A nested class or type selector is joined as a descendant of the parent (space combinator) unless you say otherwise with &:

Card styles nested under one parent

css
.card {
padding: 1.25rem;
border-radius: 0.75rem;
background: var(--surface, #fff);

.title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
}

.body {
color: var(--muted, #444);
line-height: 1.5;
}
}

The browser expands this to .card .title and .card .body. You still get ordinary descendant selectors — nesting is sugar for structure and locality, not a new matching model.

The & parent selector

Use & when the nested rule must attach to the same element as the parent — pseudo-classes, pseudo-elements, or an extra class on the parent:

Hover and modifier on the same element

css
.button {
display: inline-flex;
padding: 0.6rem 1.1rem;
border: 0;
border-radius: 0.5rem;
background: var(--brand, #2563eb);
color: #fff;

&:hover {
background: var(--brand-hover, #1d4ed8);
}

&:focus-visible {
outline: 2px solid var(--brand, #2563eb);
outline-offset: 2px;
}

&.is-primary {
background: var(--brand-strong, #1e40af);
}
}

&:hover means .button:hover. &.is-primary means .button.is-primary. Without &, a bare :hover { } is invalid nesting in native CSS — the & makes the attachment explicit.

Combinators with &

Put the combinator after & when you want a child, sibling, or other explicit link — not a plain descendant space:

Child and adjacent patterns with &

css
.nav {
display: flex;
gap: 0.75rem;

& > .item {
padding: 0.35rem 0.6rem;
}

& > .item + .item {
border-inline-start: 1px solid #ddd;
}

/* same as nesting .item without & — descendant */
.badge {
font-size: 0.75rem;
}
}

& > .item → .nav > .item. A nested .badge without & → .nav .badge. Choose intentionally: deep accidental descendants are a common nesting smell.

Nest @media with the component

You can nest at-rules such as @media inside a style rule so breakpoints live next to the component they change:

Responsive tweaks beside the component

css
.hero {
padding: 2rem 1.25rem;

h1 {
font-size: clamp(1.75rem, 1.2rem + 2vw, 2.75rem);
}

@media (min-width: 48rem) {
padding: 3rem 2rem;

h1 {
max-width: 20ch;
}
}
}

Expanded, the media query still wraps the declarations that belong to .hero (and .hero h1). Pair this with tokens and clamp() from earlier Modern Toolkit lessons — nesting only relocates the rules.

Nesting patterns at a glance

📊 Native nesting cheatsheet

You write It means Use when
.card { .title { } } .card .title Style a descendant under the component
.btn { &:hover { } } .btn:hover Pseudo-class on the same element
.btn { &.big { } } .btn.big Extra class on the same element
.nav { & > a { } } .nav > a Explicit child combinator
.box { @media (…) { } } Media query scoped to that rule’s selectors Keep breakpoints with the component

Keep nests shallow

Two or three levels usually stay readable. Deeper trees recreate the “selector pyramid” problem Sass teams hit: hard diffs, higher specificity, and styles that only make sense inside one giant parent. Prefer a clear component root (.card, .nav) and nest states and close children — not the whole page.

Component bundle: tokens + nest + states

A realistic slice ties the last three toolkit ideas together without repeating the calc or variables lessons:

Panel with tokens, nested parts, and a state

css
.panel {
--panel-pad: 1rem;
padding: var(--panel-pad);
border: 1px solid #e5e7eb;
border-radius: 0.75rem;

.heading {
margin: 0 0 0.5rem;
font-weight: 600;
}

.actions {
display: flex;
gap: calc(var(--panel-pad) / 2);
margin-block-start: var(--panel-pad);
}

&.is-compact {
--panel-pad: 0.5rem;
}

@media (max-width: 36rem) {
.actions {
flex-direction: column;
}
}
}

Tokens still live as custom properties; calc still derives a gap; nesting keeps heading, actions, compact, and the small-screen tweak in one place.

Try it yourself

  1. Take flat rules .card {}, .card .title {}, .card .meta {} and rewrite them with nesting under .card.
  2. Add &:hover on a .link so only the link (not a nested span) changes color.
  3. Nest @media (min-width: 40rem) inside .sidebar to widen padding — confirm DevTools shows the expanded selectors.
  4. Convert a mistaken nested :hover { } (no &) into &:hover { } and note that it validates.
  5. Limit a messy three-level nest to two levels by promoting an inner block to its own class.

Common mistakes

  • Writing :hover { } nested without & — invalid or not what you meant; use &:hover
  • Expecting bare .child to mean .parent.child — that needs &.child
  • Copying Sass &__element BEM hacks blindly — native nesting follows CSS selector grammar
  • Nesting five levels deep “because we can” — readability and specificity suffer
  • Thinking nesting replaces var() or calc() — it only groups selectors and at-rules

Summary

  • Nest child selectors to keep component styles local; they expand to descendants by default
  • Use & for same-element states (:hover, .is-active) and explicit combinators
  • Nest @media beside the component you are tuning
  • Keep nests shallow; nesting is structure, not a design-token or math feature
  • Next in Modern Toolkit: container queries — style from parent size, not only the viewport

🧠 Test Your Knowledge

Ready to Start

Test Your Knowledge

Challenge yourself with this interactive quiz and see how well you understand the topic

❓
6
Questions
🎯
70%
To Pass
♾️
∞
Time
🔄
∞
Attempts

📝 Instructions

  • Read each question carefully
  • Select the best answer for each question
  • You can retake the quiz as many times as you want
  • Your progress will be shown at the top