@extend and placeholders
@extend says: "this selector should get all the styles of that one". Unlike a mixin it does not copy declarations — it adds the selector to the existing rule. A placeholder selector starting with % is a rule made only for extending: it never appears in the CSS on its own.
A placeholder
Several kinds of messages share the same base styles. Put the base into a placeholder in styles/components/_alerts.scss:
// styles/components/_alerts.scss
%alert {
padding: 12px 16px;
border-radius: 8px;
}
.alert-success {
@extend %alert;
background: #dcfce7;
}
.alert-error {
@extend %alert;
background: #fee2e2;
}// styles/main.scss
@use "components/alerts";.alert-error, .alert-success {
padding: 12px 16px;
border-radius: 8px;
}
.alert-success {
background: #dcfce7;
}
.alert-error {
background: #fee2e2;
}<style>
.alert-error, .alert-success {
padding: 12px 16px;
border-radius: 8px;
}
.alert-success {
background: #dcfce7;
}
.alert-error {
background: #fee2e2;
}
</style>
<p class="alert-success">The order is placed.</p>
<p class="alert-error">The card was declined.</p>Both selectors were added to one rule: the base declarations exist in the CSS only once. %alert itself is not in the output.
Extending a class
You can extend an ordinary class too:
// styles/main.scss
.btn {
padding: 8px 16px;
}
.btn-large {
@extend .btn;
font-size: 20px;
}.btn, .btn-large {
padding: 8px 16px;
}
.btn-large {
font-size: 20px;
}Now in the HTML class="btn-large" is enough — there is no need to write class="btn btn-large".
Why to be careful
@extend changes every rule where the extended selector appears, not only the first one. If .btn is also used in some other rule, .btn-large gets added there too:
// styles/main.scss
.btn {
padding: 8px 16px;
}
.header .btn {
color: white;
}
.btn-large {
@extend .btn;
}.btn, .btn-large {
padding: 8px 16px;
}
.header .btn, .header .btn-large {
color: white;
}In a big project such selectors multiply, the CSS becomes hard to read, and the order of rules can change which style wins. Rules of thumb:
| Situation | Use |
|---|---|
| Shared base styles inside one component file | @extend of a %placeholder |
| Reusable styles with parameters | A mixin |
| Styles that should combine in the HTML | Two classes: btn btn-large |
Extend only placeholders, not real classes, and keep the placeholder in the same file as the rules that extend it.
Common mistake
@extend cannot reach out of a media query to a selector defined outside it:
// styles/main.scss
%hidden {
display: none;
}
@media (max-width: 600px) {
.sidebar {
@extend %hidden;
}
}Sass stops with the error You may not @extend selectors across media queries. In this case use a mixin: it copies the declarations into the media query, and that always works.