What Happens When You Omit the Ampersand in CSS Nesting
In native CSS nesting, omitting the ampersand (&)
causes the browser to treat the nested rule as an implicit descendant
selector by automatically prepending an ampersand followed by a space
(&). While this makes nesting straightforward for child
elements and descendant classes, it completely changes how
pseudo-classes, pseudo-elements, and compound selectors evaluate.
Understanding this automatic behavior is essential for avoiding subtle
layout and styling bugs when writing modern, preprocessor-free CSS.
The Default Behavior: Implicit Descendant Combinator
When you omit the & selector inside a nested block,
modern CSS interprets the nested selector as if you had written
& [selector] with a whitespace descendant
combinator.
Consider the following CSS:
.card {
color: black;
.title {
font-weight: bold;
}
}Because the & is omitted before .title,
the browser evaluates it as:
.card .title {
font-weight: bold;
}This applies to element selectors as well, thanks to the CSS Nesting specification updates (often referred to as relaxed nesting):
.card {
p {
line-height: 1.5;
}
}This compiles conceptually to .card p, matching any
<p> tag that is a descendant of
.card.
The Pseudo-Class Pitfall
The most common issue when omitting the ampersand occurs with
pseudo-classes such as :hover, :focus, or
:nth-child.
Developers often write:
.button {
background-color: blue;
:hover {
background-color: darkblue;
}
}Because the & was omitted, the browser treats
:hover as a descendant selector:
.button :hover {
background-color: darkblue;
}Instead of styling the .button when it is hovered, this
rule styles any descendant element inside the button (such as
an icon or <span>) when that child is hovered.
To apply the state to the parent element itself, you must explicitly
include the & without a space:
.button {
&:hover {
background-color: darkblue;
}
}Compound Selectors Require the Ampersand
Omitting the & prevents you from creating compound
selectors that target the same element with multiple classes or
attributes.
If you write:
.alert {
.warning {
border-color: orange;
}
}The browser evaluates this as .alert .warning (a
.warning element nested inside an .alert
element).
If the goal is to style an element that carries both classes
simultaneously (<div class="alert warning">),
omitting the ampersand fails. You must explicitly attach the class
directly to the ampersand:
.alert {
&.warning {
border-color: orange;
}
}Modifying
Ancestors (:has and Reverse Contexts)
Omitting the & always nests downward into
descendants. You cannot apply styles conditionally based on an ancestor
or modify an outer context without explicitly placing the
&:
.item {
/* Fails: Interpreted as `.item html[dir="rtl"]` */
html[dir="rtl"] {
margin-right: 0;
}
/* Works: Interpreted as `html[dir="rtl"] .item` */
html[dir="rtl"] & {
margin-right: 0;
}
}Quick Reference Summary
- Without
&(.parent { .child {} }): Evaluates to.parent .child(Descendant selector). - With
&followed by a space (.parent { & .child {} }): Identical to omitting&. - With
&directly attached (.parent { &:hover {} }): Evaluates to.parent:hover(Same-element modification). - With
&directly attached to a class (.parent { &.active {} }): Evaluates to.parent.active(Compound selector).