SCSS Syntax, Selectors, and Nesting
Master SCSS syntax, selector nesting, the parent selector, interpolation, combinators, and techniques for avoiding overly specific CSS.
SCSS Syntax Fundamentals
SCSS uses the same basic structure as CSS: selectors contain declaration blocks, and declarations consist of property-value pairs separated by semicolons.
Sass adds constructs such as variables, mixins, functions, control directives, and module-loading rules. These constructs are evaluated by the compiler rather than interpreted as ordinary browser CSS declarations.
Comments have different purposes. A // comment is removed during compilation. A /* ... */ comment is generally preserved in expanded output unless it is marked for removal with Sass's special comment syntax or output settings.
Understanding the distinction between source syntax and generated CSS makes compiler errors easier to diagnose.
$spacing: 1rem;
.page {
padding: $spacing;
background-color: #f5f5f5;
/* This comment may remain in expanded CSS. */
// This comment is removed from compiled CSS.
h1 {
margin-bottom: 0.5rem;
}
}h1 selector is expanded into a separate CSS selector.
The generated CSS contains .page and .page h1 rules. The browser does not see the SCSS variable or the original nesting structure.What does Sass do with a normal `//` source comment during compilation?
Selector Nesting and Its Output
Nesting allows related selectors to be grouped under a parent selector. This can make component styles easier to scan when nesting reflects a genuine relationship.
For example, nesting .card__title inside .card generates .card .card__title, which is a descendant selector. Nesting does not automatically create a BEM element selector unless you explicitly construct that selector.
Deep nesting is often harmful because each additional level can increase selector specificity, couple styles to markup structure, and make later overrides harder to understand.
Prefer a few meaningful levels rather than mirroring every HTML element inside a component.
One or two meaningful levelsLong chains of element selectorsFlat component classes.card {
padding: 1rem;
.title {
font-size: 1.25rem;
}
.description {
color: #555;
}
}.card, .card .title, and .card .description rules.
The nested selectors target descendants of .card. They do not target elements that simply have the same class elsewhere in the document.What selector is generated by `.card { .title { color: red; } }`?
The Parent Selector (&)
The parent selector & represents the complete outer selector at the nesting location. It is especially useful for component states, pseudo-classes, pseudo-elements, and naming conventions.
Common patterns include:
&:hoverfor hover states.&:focus-visiblefor keyboard-visible focus states.&::beforefor pseudo-elements.&.is-activefor an element that has both the parent class and a state class.&__titlefor BEM-style element naming.
The parent selector is not always equivalent to inserting a space. Compare &.active, which combines classes on the same element, with .active, which is a descendant selector when nested under another class.
.button {
background: #cc6699;
&:hover {
background: #ad4f80;
}
&:focus-visible {
outline: 3px solid #333;
outline-offset: 3px;
}
&.is-active {
font-weight: 700;
}
&__icon {
margin-inline-end: 0.5rem;
}
}.button, .button:hover, .button:focus-visible, .button.is-active, and .button__icon.
Notice the distinction: &.is-active selects one element with both classes, whereas &__icon constructs a class name using the parent selector text.Which selector does `&.is-active` generate inside `.button`?
Selector Interpolation and Dynamic Selector Construction
Interpolation uses #{...} to insert a Sass expression into a string or selector. It is useful when generating class names from a variable or loop.
Interpolation should be used deliberately. Dynamically generated selectors can make the final CSS harder to inspect, increase output size, and create unexpected naming collisions if naming conventions are not controlled.
Prefer straightforward selectors when the selector is known in advance. Use interpolation when a repeated, data-driven pattern genuinely benefits from it.
$component: "alert";
.#{$component} {
padding: 1rem;
&--success {
border-color: green;
}
&--error {
border-color: crimson;
}
}$component into the selector. Sass generates .alert, .alert--success, and .alert--error.
Interpolation is particularly useful in loops, token-driven utility generation, and systematic class naming.What is the purpose of `#{...}` in a Sass selector?