Skip to content

Sass Extends and Inheritance — Complete Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Sass Extends and Inheritance. We cover key concepts, practical examples, and best practices to help you master this topic.

Sass @extend shares CSS properties between selectors by grouping them together in the compiled output, reducing repetition without duplicating declarations.

What You'll Learn

  • The @extend directive and how it works
  • Placeholder selectors (%) for abstract classes
  • When to use @extend vs @mixin
  • Chaining extends
  • Limitations and specificity issues
  • Best practices for inheritance

Why It Matters

  • @extend produces smaller CSS output than @mixin
  • Maintains the DRY principle at the selector level
  • Placeholder selectors create non-rendering base classes
  • Reduces the number of repeated property declarations

Real-World Use

  • A message component shares base styles between .error, .warning, .success
  • A button system extends a base .btn class
  • A typography system uses placeholder selectors for font patterns
  • A utility library uses extend for reusable style groups
flowchart LR
  A[Sass @extend] --> B[Base Selector]
  A --> C[Placeholder %]
  B --> D[Extended Selectors]
  C --> D
  D --> E[Grouped CSS Output]
  E --> F[.error, .warning, .success { base styles }]

Extends vs Mixins

Code Example: @extend Basics

// Base styles
.message {
    padding: 1rem;
    border-radius: 4px;
    font-size: 1rem;
    margin-bottom: 1rem;
    border: 1px solid transparent;
}

// Extended selectors
.error {
    @extend .message;
    background: #f8d7da;
    color: #721c24;
    border-color: #f5c6cb;
}

.warning {
    @extend .message;
    background: #fff3cd;
    color: #856404;
    border-color: #ffeeba;
}

.success {
    @extend .message;
    background: #d4edda;
    color: #155724;
    border-color: #c3e6cb;
}

// Compiled CSS output:
// .message, .error, .warning, .success { padding: 1rem; ... }
// .error { background: #f8d7da; ... }
// .warning { background: #fff3cd; ... }
// .success { background: #d4edda; ... }

Expected output: The compiled CSS groups .error, .warning, and .success with .message as a comma-separated selector. The base styles appear once instead of being duplicated.

Code Example: Placeholder Selectors

// Placeholder selectors (defined with %, not rendered in CSS)
%button-base {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 4px;
    font-size: 1rem;
    cursor: pointer;
    min-height: 44px;
    text-decoration: none;
}

%card-base {
    background: #fff;
    border: 1px solid #eee;
    border-radius: 8px;
    overflow: hidden;
}

// Placeholder with nested styles
%focus-visible {
    outline: 3px solid #0066CC;
    outline-offset: 2px;
}

// Using placeholders
.btn-primary {
    @extend %button-base;
    background: #0066CC;
    color: #fff;
}

.btn-secondary {
    @extend %button-base;
    background: #6c757d;
    color: #fff;
}

.featured-card {
    @extend %card-base;
    border-color: #0066CC;
    box-shadow: 0 4px 12px rgba(0,102,204,0.15);
}

// Placeholder with extend only
.only-extend {
    @extend %focus-visible;
}

// Compiled CSS:
// .btn-primary, .btn-secondary { display: inline-flex; ... }
// .featured-card { background: #fff; ... }
// .only-extend { outline: 3px solid #0066CC; ... }
// Note: %button-base, %card-base, %focus-visible do NOT appear in output

Expected output: Placeholder selectors (%) never appear in the compiled CSS. They exist only as extension points. This avoids creating unused classes in the output.

Code Example: @extend vs @mixin Comparison

// @extend approach: grouped selectors, no duplication
%flex-center {
    display: flex;
    align-items: center;
    justify-content: center;
}

.header { @extend %flex-center; }
.footer { @extend %flex-center; }
.hero { @extend %flex-center; }

// Compiled: .header, .footer, .hero { display: flex; align-items: center; justify-content: center; }

// @mixin approach: duplicated declarations
@mixin flex-center {
    display: flex;
    align-items: center;
    justify-content: center;
}

.header { @include flex-center; }
.footer { @include flex-center; }
.hero { @include flex-center; }

// Compiled: 
// .header { display: flex; align-items: center; justify-content: center; }
// .footer { display: flex; align-items: center; justify-content: center; }
// .hero { display: flex; align-items: center; justify-content: center; }

// When to use each:
// @extend: Same base styles, no parameters, used in multiple places
// @mixin: Parameterized styles, or styles used with different configurations

Expected output: @extend produces more compact output for static shared styles. @mixin duplicates declarations but allows parameters and content blocks.

Code Example: Chaining and Limitations

// Chaining extends
%visually-hidden {
    position: absolute;
    overflow: hidden;
    clip: rect(0,0,0,0);
    width: 1px;
    height: 1px;
}

%focus-style {
    outline: 3px solid #0066CC;
    outline-offset: 2px;
}

// A selector can extend multiple placeholders
.sr-only {
    @extend %visually-hidden;
}

.skip-link {
    @extend %visually-hidden;
    @extend %focus-style;

    // When focused, becomes visible
    &:focus {
        position: static;
        overflow: visible;
        clip: auto;
        width: auto;
        height: auto;
        background: #0066CC;
        color: #fff;
        padding: 0.5rem 1rem;
    }
}

// @extend inside media queries
// LIMITATION: Cannot extend from outside a media query
// This works:
@media (min-width: 768px) {
    %desktop-only { display: block; }

    .sidebar { @extend %desktop-only; }
}

// This does NOT work:
// .sidebar { @extend %desktop-only; } // ERROR: can't extend across @media

Expected output: A selector can extend multiple placeholders, combining their styles. The @extend directive cannot cross media query boundaries - both the placeholder and the extending selector must be in the same context.

Common Mistakes

  1. Using @extend in deeply nested selectors — @extend within nested selectors can produce unexpected specificity and cascading issues.
  2. Overusing @extend for everything — For parameterized styles, mixins are better. Use @extend only for static shared styles.
  3. Extending across media queries — @extend cannot cross @media boundaries. This produces a compiler error.
  4. Not using placeholder selectors — Extending real classes (.class) keeps those classes in the output even if they are not used directly. Use %placeholder to avoid this.
  5. Creating long selector chains — Extending deeply can produce selectors like .class1.class2.class3 that are hard to override.
  6. @extend in @imported files — Files imported multiple times can cause duplicate extends. Use @use instead.
  7. Forgetting that @extend groups selectors — The compiled comma-separated selectors can be very long. Monitor compiled output size.

Practice Questions

  1. How does @extend differ from @include? @extend groups selectors together (no duplication). @include copies declarations each time (duplication).
  2. What is a placeholder selector and why use it? A placeholder selector (%) never renders in the output. It is used only as an extension point to avoid generating unused classes.
  3. Can you extend a selector inside a media query? You can extend within the same media query block, but you cannot extend a selector defined outside a media query from inside one.
  4. What is the risk of long selector chains with @extend? @extend creates comma-separated selectors that can grow very long and become hard to manage in DevTools.

FAQ

Should I use @extend or @mixin for button styles?

Use @mixin if buttons need parameters (colors, sizes). Use @extend if buttons share the exact same base styles without variation.

Does @extend work with CSS modules?

It depends. CSS modules scope class names. @extend expects the extended selector to be in the same scope. Test with your specific setup.

What is the performance benefit of @extend?

@extend produces less CSS output than @mixin for static styles. This reduces file size and improves parse time marginally.

Mini Project

Build a notification component (info, success, warning, error) using @extend with placeholder selectors. Create %notification-base with common properties (padding, border-radius, flex layout, font-size, icon positioning). Create %notification-icon-base for the icon positioning. Each variant (info, success, warning, error) extends both placeholders and adds variant-specific colors. Also create a %focus-ring placeholder extended by all interactive elements. Compare the compiled CSS output size with an equivalent @mixin implementation. Verify the output has no orphaned placeholder selectors.

What's Next

Continue with Lesson 8: Sass Partials and Import to learn file organization with @use and @forward.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro