Extend & Inheritance
@extend shares a set of styles between selectors a different way than a mixin does — instead of copying the properties into each selector, it groups every selector that shares them into one combined rule.
Placeholder selectors
A placeholder selector, written with % instead of ., exists only to be extended — it never compiles into CSS on its own:
%button-base {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.btn-primary {
@extend %button-base;
background: #1b6ec2;
color: white;
}
.btn-secondary {
@extend %button-base;
background: #565d72;
color: white;
}
.btn-primary, .btn-secondary {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.btn-primary {
background: #1b6ec2;
color: white;
}
.btn-secondary {
background: #565d72;
color: white;
}Compare this to a mixin: instead of the five shared properties being copied into .btn-primary and again into .btn-secondary, Sass combined both selectors into a single rule that lists the shared properties once. The result is smaller compiled CSS whenever many selectors share the exact same base styles.
Extending a real class instead of a placeholder
You can also @extend an ordinary class selector, not just a placeholder — though a placeholder is usually the better choice specifically because it never generates its own unused CSS rule if nothing happens to extend it:
.message {
padding: 12px;
border-radius: 4px;
}
.error {
@extend .message;
background: #fdecea;
color: #a33b3b;
}
.message, .error {
padding: 12px;
border-radius: 4px;
}
.error {
background: #fdecea;
color: #a33b3b;
}Here .message compiles into real CSS on its own (since it's a genuine class, presumably used directly in HTML somewhere) as well as being combined with .error.
@extend's selector-combining behavior can produce a less predictable compiled order than a mixin's straightforward copy-paste, especially once several files and partials are extending the same placeholder — the combined selector list ends up ordered by where each extending selector was defined, not necessarily where you'd expect. For a shared block of styles used across many unrelated selectors, many teams reach for a mixin instead purely because its output is easier to reason about, even though it does produce more repeated CSS.