SASS/SCSS

Nesting: Selectors & Properties

20 min Lesson 5 of 30

Nesting: Selectors & Properties

Nesting is one of the most beloved features of SASS. It allows you to write selectors that mirror the HTML structure, making your code more readable and maintainable. However, with great power comes great responsibility - improper nesting can lead to bloated, overly-specific CSS. In this comprehensive lesson, we'll master both selector and property nesting, along with best practices to keep your code clean.

Why Nesting Matters

In plain CSS, you have to repeat parent selectors over and over:

Repetitive Plain CSS

.navigation {
  background: #333;
  padding: 20px;
}

.navigation ul {
  list-style: none;
  margin: 0;
}

.navigation ul li {
  display: inline-block;
  margin-right: 10px;
}

.navigation ul li a {
  color: white;
  text-decoration: none;
  padding: 5px 10px;
}

.navigation ul li a:hover {
  background: #555;
}

.navigation ul li a:active {
  background: #777;
}

This is verbose, error-prone, and hard to maintain. With SASS nesting, you can write the same styles in a structure that mirrors your HTML:

Clean SCSS with Nesting

.navigation {
  background: #333;
  padding: 20px;

  ul {
    list-style: none;
    margin: 0;

    li {
      display: inline-block;
      margin-right: 10px;

      a {
        color: white;
        text-decoration: none;
        padding: 5px 10px;

        &:hover {
          background: #555;
        }

        &:active {
          background: #777;
        }
      }
    }
  }
}

Much cleaner! The structure visually represents the HTML hierarchy, making it easier to understand and modify.

Tip: Nesting makes your SCSS more maintainable because related styles are grouped together. If you need to change the navigation, all its styles are in one place!

How Nesting Compiles to CSS

Understanding how nesting compiles helps you predict the output and avoid mistakes. SASS converts nested selectors into descendant selectors in CSS:

SCSS Input

.card {
  padding: 20px;

  .card-header {
    font-size: 24px;
  }
}

CSS Output

.card {
  padding: 20px;
}

.card .card-header {
  font-size: 24px;
}

The nested .card-header becomes .card .card-header (a descendant selector).

Multiple Levels of Nesting

Deep Nesting SCSS

.article {
  .article-header {
    .article-title {
      font-size: 32px;
    }
  }
}

CSS Output

.article .article-header .article-title {
  font-size: 32px;
}

Each level of nesting adds another selector to the chain.

The Parent Selector (&): Your Best Friend

The ampersand (&) is a special character that references the parent selector. It's essential for pseudo-classes, pseudo-elements, and modifier classes.

Pseudo-Classes with &

SCSS with Pseudo-Classes

.button {
  background: #3498db;
  color: white;
  padding: 10px 20px;

  &:hover {
    background: #2980b9;
  }

  &:focus {
    outline: 2px solid #3498db;
    outline-offset: 2px;
  }

  &:active {
    transform: scale(0.98);
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

CSS Output

.button {
  background: #3498db;
  color: white;
  padding: 10px 20px;
}
.button:hover {
  background: #2980b9;
}
.button:focus {
  outline: 2px solid #3498db;
  outline-offset: 2px;
}
.button:active {
  transform: scale(0.98);
}
.button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

Pseudo-Elements with &

SCSS with Pseudo-Elements

.button {
  position: relative;

  &::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(255, 255, 255, 0.1);
    opacity: 0;
    transition: opacity 0.3s;
  }

  &:hover::before {
    opacity: 1;
  }

  &::after {
    content: "→";
    margin-left: 5px;
  }
}
Note: Use :: (double colon) for pseudo-elements like ::before and ::after, and : (single colon) for pseudo-classes like :hover and :focus. This is the modern CSS standard.

Modifier Classes with &

The & is perfect for creating modifier classes (like BEM methodology):

SCSS with Modifier Classes

.button {
  padding: 10px 20px;
  background: #3498db;
  color: white;

  // Class modifier: .button.primary
  &.primary {
    background: #3498db;
  }

  // Class modifier: .button.secondary
  &.secondary {
    background: #95a5a6;
  }

  // Class modifier: .button.danger
  &.danger {
    background: #e74c3c;
  }

  // BEM-style modifier: .button--large
  &--large {
    padding: 15px 30px;
    font-size: 18px;
  }

  // BEM-style modifier: .button--small
  &--small {
    padding: 5px 10px;
    font-size: 12px;
  }
}

CSS Output

.button {
  padding: 10px 20px;
  background: #3498db;
  color: white;
}
.button.primary {
  background: #3498db;
}
.button.secondary {
  background: #95a5a6;
}
.button.danger {
  background: #e74c3c;
}
.button--large {
  padding: 15px 30px;
  font-size: 18px;
}
.button--small {
  padding: 5px 10px;
  font-size: 12px;
}
Tip: Notice &.secondary (no space) creates .button.secondary (both classes on same element), while & .secondary (with space) would create .button .secondary (descendant selector).

Context-Based Styling with &

You can place & anywhere in the selector, even after another selector:

Parent Context Styling

.button {
  background: blue;
  color: white;

  // When button is inside .dark-theme
  .dark-theme & {
    background: black;
    border: 1px solid white;
  }

  // When button is inside a nav
  nav & {
    margin-right: 10px;
  }

  // When button is a direct child of .toolbar
  .toolbar > & {
    margin: 5px;
  }
}

CSS Output

.button {
  background: blue;
  color: white;
}
.dark-theme .button {
  background: black;
  border: 1px solid white;
}
nav .button {
  margin-right: 10px;
}
.toolbar > .button {
  margin: 5px;
}

Property Nesting

SASS also allows you to nest properties that share the same prefix (like font-, border-, background-). This is less commonly used but can reduce repetition:

Property Nesting Example

.text {
  font: {
    family: Arial, sans-serif;
    size: 16px;
    weight: bold;
    style: italic;
  }

  border: {
    width: 1px;
    style: solid;
    color: #ccc;
    radius: 4px;
  }

  background: {
    color: white;
    image: url('pattern.png');
    repeat: no-repeat;
    position: center;
  }
}

CSS Output

.text {
  font-family: Arial, sans-serif;
  font-size: 16px;
  font-weight: bold;
  font-style: italic;
  border-width: 1px;
  border-style: solid;
  border-color: #ccc;
  border-radius: 4px;
  background-color: white;
  background-image: url("pattern.png");
  background-repeat: no-repeat;
  background-position: center;
}

Property Nesting with Shorthand

You can combine property nesting with shorthand values:

Mixed Property Nesting

.element {
  border: 1px solid #ccc {
    radius: 4px;
    top-color: blue;
  }

  margin: 0 auto {
    top: 20px;
    bottom: 20px;
  }
}

CSS Output

.element {
  border: 1px solid #ccc;
  border-radius: 4px;
  border-top-color: blue;
  margin: 0 auto;
  margin-top: 20px;
  margin-bottom: 20px;
}
Note: Property nesting is valid SCSS but not widely used in practice. Many developers find it less readable than writing properties individually. Use it sparingly!

Best Practice: Limit Nesting Depth (Max 3-4 Levels)

While nesting is powerful, over-nesting creates overly specific selectors that are hard to override and can hurt performance.

Over-Nesting (BAD)

// DON'T DO THIS
.page {
  .container {
    .sidebar {
      .widget {
        .widget-header {
          .widget-title {
            color: blue;  // Specificity nightmare!
          }
        }
      }
    }
  }
}

Compiled CSS (Too Specific)

/* This selector is way too specific! */
.page .container .sidebar .widget .widget-header .widget-title {
  color: blue;
}
Warning: This 6-level deep selector is incredibly hard to override. If you later want to style .widget-title differently on a specific page, you'll need an even more specific selector. This leads to specificity wars!

Better Approach (GOOD)

Shallow Nesting with BEM

// Much better!
.widget {
  background: white;
  padding: 20px;

  &__header {
    margin-bottom: 15px;
  }

  &__title {
    color: blue;
    font-size: 18px;
  }

  &__content {
    line-height: 1.6;
  }

  &--featured {
    border: 2px solid gold;
  }
}

Compiled CSS (Perfect Specificity)

.widget {
  background: white;
  padding: 20px;
}
.widget__header {
  margin-bottom: 15px;
}
.widget__title {
  color: blue;
  font-size: 18px;
}
.widget__content {
  line-height: 1.6;
}
.widget--featured {
  border: 2px solid gold;
}
Tip: The BEM (Block Element Modifier) methodology works beautifully with SASS. Use &__element for child elements and &--modifier for variations. This keeps specificity low while maintaining clear relationships.

The Danger of Over-Nesting: Specificity Bloat

Let's see a concrete example of why over-nesting is problematic:

Problem: Over-Nested SCSS

.header {
  .navigation {
    ul {
      li {
        a {
          color: blue;

          &:hover {
            color: darkblue;
          }
        }
      }
    }
  }
}

Compiled: Highly Specific Selectors

/* Specificity: (0, 0, 5, 0) - very high! */
.header .navigation ul li a {
  color: blue;
}

/* Specificity: (0, 1, 5, 0) - even higher! */
.header .navigation ul li a:hover {
  color: darkblue;
}

Now imagine you want to style navigation links differently on a specific page:

Trying to Override (Won't Work)

/* This won't work - not specific enough! */
.special-page .navigation a {
  color: red;
}

You'd need an even more specific selector to override it, leading to an endless specificity war.

Solution: Shallow Nesting

Better Approach

.navigation {
  ul {
    list-style: none;
  }

  &__item {
    display: inline-block;
  }

  &__link {
    color: blue;

    &:hover {
      color: darkblue;
    }
  }
}

// Now easy to override when needed
.special-page .navigation__link {
  color: red;
}

Real-World Example: Styled Card Component

Let's put everything together with a comprehensive, real-world example:

Complete Card Component SCSS

.card {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  overflow: hidden;
  transition: transform 0.3s, box-shadow 0.3s;

  // Hover state for entire card
  &:hover {
    transform: translateY(-4px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
  }

  // Card image
  &__image {
    width: 100%;
    height: 200px;
    object-fit: cover;
  }

  // Card content wrapper
  &__content {
    padding: 20px;
  }

  // Card title
  &__title {
    font-size: 24px;
    font-weight: bold;
    margin-bottom: 10px;
    color: #333;

    // Nested anchor inside title
    a {
      color: inherit;
      text-decoration: none;

      &:hover {
        color: #3498db;
      }
    }
  }

  // Card description
  &__description {
    color: #666;
    line-height: 1.6;
    margin-bottom: 15px;
  }

  // Card footer
  &__footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-top: 15px;
    border-top: 1px solid #eee;
  }

  // Card button
  &__button {
    padding: 8px 16px;
    background: #3498db;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.3s;

    &:hover {
      background: #2980b9;
    }
  }

  // Modifier: Featured card
  &--featured {
    border: 2px solid gold;

    .card__title {
      color: #f39c12;
    }
  }

  // Modifier: Compact card
  &--compact {
    .card__image {
      height: 120px;
    }

    .card__content {
      padding: 15px;
    }

    .card__title {
      font-size: 18px;
    }
  }

  // Context: Card inside a grid
  .grid & {
    margin-bottom: 20px;
  }
}
Tip: This example demonstrates best practices: BEM naming, shallow nesting (max 3 levels), proper use of & for modifiers and states, and context-based styling. Study this pattern!

Nesting vs Flat Selectors: When to Use Each

Use Nesting When:

  • Styles are tightly coupled to a parent component
  • You need pseudo-classes or pseudo-elements (&:hover)
  • Creating modifier classes (&--large)
  • Context-based styling (.dark-theme &)
  • Related elements share a namespace (.card__title)

Use Flat Selectors When:

  • Elements can be used independently
  • Nesting would exceed 3 levels
  • You need low specificity for easy overriding
  • Creating utility classes (.text-center)

Performance Considerations

While nesting doesn't directly impact CSS file size (it compiles to the same output), overly specific selectors can slow down browser rendering slightly. Modern browsers are fast, but deeply nested selectors like .a .b .c .d .e .f take longer to match than .f.

Note: In practice, selector performance is rarely a bottleneck. The bigger concern with over-nesting is maintainability and specificity conflicts, not rendering speed.

Conclusion

Nesting is a powerful SASS feature that makes stylesheets more readable and maintainable. You've learned how to nest selectors, use the parent selector (&), nest properties, and most importantly, how to avoid the common pitfalls of over-nesting. Keep nesting shallow (3-4 levels max), embrace BEM methodology, and your SASS will be clean, performant, and maintainable!

In the next lesson, we'll explore partials and imports - how to organize your SASS into modular files for better project structure.

Exercise 1: Convert Flat CSS to Nested SCSS

Take this flat CSS and convert it to well-nested SCSS:

.product { padding: 20px; }
.product .product-image { width: 100%; }
.product .product-title { font-size: 24px; }
.product .product-title a { color: blue; }
.product .product-title a:hover { color: darkblue; }
.product .product-price { font-weight: bold; color: green; }
.product .product-button { padding: 10px; background: blue; }
.product .product-button:hover { background: darkblue; }
.product.featured { border: 2px solid gold; }

Convert to BEM-style nested SCSS with proper & usage.

Exercise 2: Build a Navigation Component

Create a fully-styled navigation bar using nested SCSS:

  1. Create .nav with nested .nav__list, .nav__item, .nav__link
  2. Add hover and active states using &
  3. Create a .nav--dark modifier for dark theme
  4. Add responsive styles using media queries inside the nesting
  5. Compile and test with HTML

Exercise 3: Identify Over-Nesting

Review this over-nested SCSS and refactor it:

.page {
  .container {
    .content {
      .article {
        .article-header {
          .article-title {
            h1 {
              color: blue;
            }
          }
        }
      }
    }
  }
}

Refactor to a maximum of 2-3 nesting levels while maintaining clear relationships. Use BEM naming where appropriate. Document why your refactored version is better.