4 Topics
Chapter•105 Minutes

Mixins, Placeholders, and Code Reuse

Build reusable styling abstractions using mixins, parameters, defaults, content blocks, placeholder selectors, and carefully chosen inheritance patterns.

Creating and Including Mixins

A mixin is a reusable block of Sass declarations or rules. Define one with @mixin and include it with @include.

Mixins are useful for repeated patterns such as focus indicators, layout helpers, responsive rules, and compatibility declarations. They can also emit nested selectors and media queries.

Unlike a function, a mixin primarily produces stylesheet output. A function computes and returns a value that can be used in an expression.

Because a mixin's declarations are generally expanded wherever it is included, including a large mixin in many selectors can duplicate CSS. Reuse should be evaluated against the size and clarity of the generated output.

Reusable Focus and Flexbox Mixins
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

@mixin focus-ring($color: #333) {
  &:focus-visible {
    outline: 3px solid $color;
    outline-offset: 3px;
  }
}

.modal {
  @include flex-center;
}

.button {
  @include focus-ring(#5b5bd6);
}
💡 The flex-center mixin emits three declarations. The focus-ring mixin accepts a color argument and generates a nested focus selector. A mixin is a good abstraction when multiple components need the same meaningful behavior. It is less useful when it merely hides a single obvious declaration without improving consistency.
Knowledge Check

Which pair of directives defines and invokes a Sass mixin?

Mixin Parameters, Defaults, and Keyword Arguments

Mixins can accept positional parameters, default values, and keyword arguments. Parameters make a reusable pattern configurable without duplicating its implementation.

A parameter with a default value is optional. A caller can override that value by passing a positional or named argument.

Keyword arguments are especially useful when a mixin has several parameters because they make call sites self-documenting. Avoid overly generic mixins with many unrelated parameters; several small mixins are often easier to understand.

A Responsive Container Mixin
@mixin container(
  $max-width: 72rem,
  $gutter: 1rem
) {
  width: min(calc(100% - 2 * #{$gutter}), #{$max-width});
  margin-inline: auto;
}

.page-container {
  @include container;
}

.article-container {
  @include container($max-width: 48rem, $gutter: 1.5rem);
}
💡 The mixin accepts a maximum width and horizontal gutter. The default call uses the default arguments, while the article container supplies custom values using keyword arguments. The CSS min() and calc() functions perform browser-side layout calculations. Sass interpolation is used to place the configured values into the CSS calculation.
Knowledge Check

What is a major advantage of keyword arguments in a mixin with multiple parameters?

Content Blocks and @content

A mixin can expose a content block using @content. The caller supplies additional Sass content inside the @include block, and the mixin inserts that content at the designated position.

This pattern is useful for reusable media-query wrappers, feature-specific style contexts, and APIs that need to wrap caller-provided declarations.

Content blocks should remain straightforward. Complex nested content APIs can make it difficult to determine where declarations will be emitted.

Reusable Responsive Wrapper
@mixin respond-to($min-width) {
  @media (min-width: $min-width) {
    @content;
  }
}

.dashboard {
  display: grid;
  grid-template-columns: 1fr;

  @include respond-to(48rem) {
    grid-template-columns: 16rem 1fr;
    gap: 1.5rem;
  }
}
💡 The mixin accepts a breakpoint and wraps the caller's declarations in a media query. The included content becomes the media query's body. This abstraction can be useful when a project has a standardized responsive API. Keep breakpoint values consistent across the codebase.
Knowledge Check

What does `@content` allow a mixin to do?

Placeholder Selectors and @extend

A placeholder selector begins with % and is not emitted as a standalone selector unless it is extended by another selector. It is useful for sharing a set of declarations among related selectors.

The @extend directive allows a selector to inherit the selector rules associated with another selector. Sass may combine selectors in generated CSS, which can create larger selector groups than expected.

Mixins and placeholders serve different purposes. Mixins copy declarations into each inclusion point, whereas placeholders and @extend participate in selector unification. Neither is universally superior.

Prefer mixins when the caller needs different parameter values or when explicit component output is desirable. Consider placeholders for tightly related selectors that genuinely share a structural rule set. Avoid extending broad selectors across unrelated components.

Mixin
Output: Declarations expanded at include sites
The generated rules can be easy to associate with each component.
Parameters: Supported
Values and behavior can be configured per inclusion.
Placeholder and extend
Output: Selectors unified
Sass combines rules involving extending selectors.
Parameters: Not a mixin-style parameter API
Use a mixin when per-call configuration is required.
Sharing Declarations with a Placeholder
%message-base {
  padding: 1rem;
  border-radius: 0.5rem;
}

.success-message {
  @extend %message-base;
  color: #155724;
  background: #d4edda;
}

.error-message {
  @extend %message-base;
  color: #721c24;
  background: #f8d7da;
}
💡 The placeholder's declarations are shared by the extending selectors. The placeholder itself does not appear as a standalone selector in the generated CSS. Inspect compiled CSS when using @extend, especially when extending selectors with complex relationships. Selector unification can produce output that is less obvious from the SCSS source.
Knowledge Check

What happens to a placeholder selector such as `%message-base` when it is not extended?