SyntaxStudy
Sign Up
SASS / SCSS Beginner 1 min read

Placeholder Selectors with %

A placeholder selector is defined with a percent sign prefix: `%placeholder-name`. It looks like a class selector in SASS source code, but it is never output to the compiled CSS on its own — it only appears in the output when extended by another selector. This makes placeholders ideal as abstract bases for `@extend` because they carry zero overhead if nothing extends them. Placeholders solve the main design problem with extending regular classes: if you extend a real class `.message`, that class gets compiled into your CSS even if you never actually use it in your HTML. With a placeholder `%message`, no CSS is emitted unless something extends it. The base styles exist purely as a reuse vehicle. The practical workflow is: define your shared styles as a `%placeholder`, then `@extend` it from any number of actual selectors. The compiled CSS contains only the selector groups that genuinely exist in your HTML, along with their shared declarations. This pattern is especially powerful in component libraries where many visual variants share structural styles.
Example
// ── %placeholder selectors — emitted only when extended ───────────────────────

// This block produces NO CSS on its own
%flex-center {
    display        : flex;
    align-items    : center;
    justify-content: center;
}

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

%button-reset {
    background: none;
    border    : none;
    padding   : 0;
    margin    : 0;
    cursor    : pointer;
    font      : inherit;
    color     : inherit;
}

// ── Extending placeholders in real selectors ──────────────────────────────────

.hero           { @extend %flex-center; min-height: 60vh; }
.modal-overlay  { @extend %flex-center; position: fixed; inset: 0; }
.loading-center { @extend %flex-center; padding: 4rem; }

.sr-only        { @extend %visually-hidden; }
.skip-link      {
    @extend %visually-hidden;
    &:focus {
        position: static;
        clip    : auto;
        width   : auto;
        height  : auto;
    }
}

.icon-btn       { @extend %button-reset; display: inline-flex; }

// ── Compiled CSS (only the extended selectors appear, never the % rules) ───────
// .hero, .modal-overlay, .loading-center { display: flex; align-items: center; justify-content: center; }
// .hero { min-height: 60vh; }
// .sr-only, .skip-link { position: absolute; width: 1px; ... }
// .icon-btn { background: none; border: none; ... display: inline-flex; }