Sass selector.extend()

Beginner
8 min read
Updated: Nov 2026
5 examples
Dart Sass 1.23+

Definition and Usage

selector.extend($selector, $extendee, $extender) returns a copy of $selector rewritten as if this ran:

SCSS
#{$extender} {
  @extend #{$extendee};
}

In other words: keep the original, and add unified forms that use $extender. Load with @use "sass:selector".

Official sample idea.

selector.extend("a.disabled", "a", ".link") → a.disabled, .disabled.link (keeps the original and adds the extender form).

Miss = unchanged.

If $selector does not contain $extendee, the function returns $selector as-is.

No parent selectors.

Placeholders are allowed; & is not. Need a swap instead of keep-and-add? Use selector.replace().

Syntax

SCSS
@use "sass:selector";

selector.extend($selector, $extendee, $extender) //=> selector

selector.extend("a.disabled", "a", ".link");
// a.disabled, .disabled.link

selector.extend("a.disabled", "h1", "h2");
// a.disabled  (unchanged — no match)

selector.extend(".guide .info", ".info", ".content nav.sidebar");
// .guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar

// Use as a rule selector:
#{selector.extend(".btn.primary", ".btn", ".cta")} { ... }

// Global alias (legacy): selector-extend($selector, $extendee, $extender)
// Prefer selector.extend after @use "sass:selector"

Pattern: pick what to extend → Sass keeps the original and adds extender forms → interpolate with #{…} for CSS rules.

Extend vs Replace

Same arguments, different intent:

HelperTypical resultUse when
selector.extenda.disabled, .disabled.linkKeep original and share styles with extender
selector.replace.disabled.linkSwap $original for $replacement
Official contrast.

Docs also point to selector.replace() when you want substitution instead of @extend-style keep-and-add.

SCSS Examples

Examples use @use "sass:selector". Open View Compiled CSS for verified Dart Sass output (matches official Sass docs samples where shown).

Example 1 — Official Extend Samples

Match found, no match, and complex unification.

SCSS
@use "sass:selector";

.probe {
  --link: #{selector.extend("a.disabled", "a", ".link")};
  --miss: #{selector.extend("a.disabled", "h1", "h2")};
  --guide: #{selector.extend(".guide .info", ".info", ".content nav.sidebar")};
}

How It Works

  1. Hit: a is found → keep original + add .disabled.link.
  2. Miss: no h1 → return unchanged.
  3. Complex: .info is extended by a multi-part selector.
  4. Matches the official Sass docs sample idea.

Example 2 — Build Rule Selectors

Interpolate the extended result into a real style rule.

SCSS
@use "sass:selector";

#{selector.extend(".btn.primary", ".btn", ".cta")} {
  background: teal;
}

How It Works

  1. .btn is the extendee inside .btn.primary.
  2. .cta extends it → .primary.cta is added.
  3. Original kept: .btn.primary still matches.
  4. #{…} turns the list into one CSS rule.

Example 3 — Share Styles Mixin

Wrap extend so callers pass selector, extendee, and extender.

SCSS
@use "sass:selector";

@mixin share-styles($selector, $extendee, $extender) {
  #{selector.extend($selector, $extendee, $extender)} {
    color: inherit;
  }
}

@include share-styles("a.disabled", "a", ".link");

How It Works

  1. Mixin packages the three arguments.
  2. Same result as the official first sample.
  3. Reusable for design-system helpers.
  4. Compile-time only — browsers see finished selectors.

Example 4 — Extend vs Replace

Same arguments, different intent.

SCSS
@use "sass:selector";

.compare {
  --extend: #{selector.extend("a.disabled", "a", ".link")};
  --replace: #{selector.replace("a.disabled", "a", ".link")};
}

How It Works

  1. extend keeps a.disabled.
  2. replace drops the original match.
  3. Both produce .disabled.link.
  4. Choose extend when you need keep-and-add.

Example 5 — Alternate Class Loop

Extend one base selector with several alternate class names.

SCSS
@use "sass:selector";

$base: ".card.featured";
$alts: ".promo", ".highlight";

@each $alt in $alts {
  #{selector.extend($base, ".card", $alt)} {
    border-width: 2px;
  }
}

How It Works

  1. $base is the selector to rewrite.
  2. @each supplies alternate extenders.
  3. .featured stays on original and new forms.
  4. Useful for promo / highlight class aliases.

Edge Cases & Pitfalls

Most extend bugs are confusing it with replace or passing &.

keep

Original is kept

Need a swap only? Use selector.replace.

miss

No match = no change

If $extendee is absent, the selector returns unchanged.

&

Parent selectors forbidden

Placeholders are fine; do not pass &.

#{}

Interpolate for rules

Wrap results in #{…} to emit CSS selectors.

Sass Support

Prefer the module form with current Dart Sass.

APIStatus
selector.extend()Dart Sass 1.23+ via @use "sass:selector"
Return typeSelector
Parent selectorsNot allowed
Legacy globalselector-extend($selector, $extendee, $extender)

Compile with the Dart Sass CLI.

Key Takeaways

  • @extend-style rewrite — keep original, add extender forms.
  • Miss stays unchanged when $extendee is absent.
  • vs replace — replace drops the original match.
  • Prefer @use "sass:selector" (Dart Sass 1.23+).

One line: to rewrite a selector like @extend, use selector.extend($selector, $extendee, $extender).

Compile Notes

Practical checklist:

checklist.txt
1. @use "sass:selector"
2. Pass $selector, $extendee, $extender
3. Expect keep-and-add (not a swap)
4. Interpolate with #{} for CSS rules
5. Use replace when you want substitution only

Frequently Asked Questions

selector.extend($selector, $extendee, $extender) returns a copy of $selector rewritten as if $extender { @extend $extendee; } ran. It keeps matches of $extendee and adds forms that use $extender. Example: extend("a.disabled", "a", ".link") becomes a.disabled, .disabled.link.
extend keeps the original selector and adds extended variants (a.disabled, .disabled.link). replace swaps $original for $replacement and drops the original match (.disabled.link only).
If $selector does not contain $extendee, the function returns $selector unchanged.
No. Arguments may contain placeholder selectors, but not parent selectors.
Yes. selector-extend($selector, $extendee, $extender) is the legacy global name. Prefer selector.extend after @use "sass:selector".
Dart Sass 1.23+. LibSass and Ruby Sass do not load sass:selector with @use; they may still offer the legacy global name.

Next: Sass selector.is-superselector()

Check whether one selector matches everything another matches.

is-superselector() →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful