Placeholder Selectors %
Define extend-only selectors that never appear in the output alone.
%name { } .thing { @extend %name; }A placeholder selector starts with a percent sign, like %card. It produces no CSS on its own — it only appears in the output when something @extends it.
This solves the biggest problem with extending real classes: you get shared, deduplicated styles without an orphan class you never use in HTML. Placeholders are the recommended target for @extend.
Example
%button-base {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
@extend %button-base;
background: #CC6699;
}
.btn-secondary {
@extend %button-base;
background: #6699CC;
}
/* %button-base itself emits no CSS; its rules are shared by both buttons. */When to use it
- A library defines %flex-center as a placeholder so any component that extends it gets the centering styles without the placeholder itself appearing in the output CSS.
- A developer creates a %clearfix placeholder and extends it from five layout containers, keeping the generated CSS DRY with a single grouped selector.
- A design system uses %visually-hidden as a placeholder instead of a class to prevent the base rule from appearing in output when nothing extends it.
More examples
Placeholder that never renders alone
The %flex-center placeholder never produces a .%flex-center rule in output; only the extending selectors appear, grouped together.
%flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.hero { @extend %flex-center; min-height: 80vh; }
.dialog { @extend %flex-center; padding: 2rem; }Placeholder for reusable card base
Three card variants all extend %card, resulting in a single grouped selector rule in the compiled CSS with no wasted output.
%card {
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
}
.product-card { @extend %card; }
.blog-card { @extend %card; }
.profile-card { @extend %card; }Placeholder vs class: output difference
Demonstrates that a placeholder selector produces zero output until at least one rule extends it, unlike a regular class.
// Using a class — .base appears in output even if unused
.base { color: red; }
// Using a placeholder — nothing appears if nothing extends it
%base { color: red; }
// Only after extending:
.warning { @extend %base; font-weight: bold; }
// Output: .warning { color: red; font-weight: bold; }
Discussion