4 Topics
Chapter•95 Minutes

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.

A Simple SCSS Stylesheet
$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;
  }
}
💡 The variable is resolved during compilation, and the nested 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.
Knowledge Check

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.

Nesting guidelines
Good: One or two meaningful levels
Keeps relationships visible without excessive specificity.
Risky: Long chains of element selectors
Couples styles to the exact markup hierarchy.
Alternative: Flat component classes
Often provides more predictable reuse and overriding.
Nested Selectors
.card {
  padding: 1rem;

  .title {
    font-size: 1.25rem;
  }

  .description {
    color: #555;
  }
}
💡 Sass compiles this example into .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.
Knowledge Check

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:

  • &:hover for hover states.
  • &:focus-visible for keyboard-visible focus states.
  • &::before for pseudo-elements.
  • &.is-active for an element that has both the parent class and a state class.
  • &__title for 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.

Component States and BEM
.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;
  }
}
💡 The generated selectors are .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.
Knowledge Check

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.

Generate Class Names
$component: "alert";

.#{$component} {
  padding: 1rem;

  &--success {
    border-color: green;
  }

  &--error {
    border-color: crimson;
  }
}
💡 The interpolation expression inserts the value of $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.
Knowledge Check

What is the purpose of `#{...}` in a Sass selector?