SyntaxStudy
Sign Up
SASS / SCSS Variable Arguments and Mixin Best Practices
SASS / SCSS Beginner 1 min read

Variable Arguments and Mixin Best Practices

SASS supports variable-length argument lists in mixins using the `...` (arglist) syntax. When the last parameter is followed by `...`, the mixin accepts any number of additional arguments and collects them into a single list. This is useful for CSS properties that accept multiple values, such as `box-shadow`, `transition`, or `background`. The `...` syntax also works in the opposite direction at call time. If you have a list or map of values you want to pass as individual arguments, append `...` to the variable when including the mixin: `@include mixin($args...)`. This unpacks the list into positional arguments, which is handy when building higher-level mixins that delegate to lower-level ones. Good mixin design follows a few principles: keep mixins focused on a single task, prefer default argument values over requiring callers to pass everything, document the expected argument types with comments, and favour naming conventions that make `@include` calls read like English. Overly complex mixins with many required arguments are a sign that the abstraction should be split or reconsidered.
Example
// ── Variable argument lists ($args...) ────────────────────────────────────────

// Accepts any number of box-shadow values
@mixin box-shadow($shadows...) {
    -webkit-box-shadow: $shadows;
    -moz-box-shadow   : $shadows;
    box-shadow        : $shadows;
}

.card   { @include box-shadow(0 2px 4px rgba(0,0,0,.1)); }
.modal  { @include box-shadow(0 8px 32px rgba(0,0,0,.2), 0 2px 8px rgba(0,0,0,.1)); }

// ── Unpacking a list into arguments ──────────────────────────────────────────

@mixin gradient($direction, $start-color, $end-color) {
    background: linear-gradient($direction, $start-color, $end-color);
}

$brand-gradient: (to right, #3498db, #2ecc71);

.hero {
    @include gradient($brand-gradient...);  // unpacks list as 3 arguments
}

// ── Mixin with keyword arguments map ─────────────────────────────────────────

@mixin transition($props...) {
    // $props is an arglist; keyword arguments are accessible via keywords()
    $props-map: keywords($props);
    $property  : map.get($props-map, 'property')  or all;
    $duration  : map.get($props-map, 'duration')  or 0.3s;
    $easing    : map.get($props-map, 'easing')    or ease;
    $delay     : map.get($props-map, 'delay')     or 0s;

    transition: $property $duration $easing $delay;
}

.btn {
    @include transition($property: background-color, $duration: 0.2s);
}

// ── Well-documented mixin example ─────────────────────────────────────────────

/// Visually hide an element while keeping it accessible to screen readers.
/// @param {Boolean} $focusable [false] - Allow element to be focusable when navigated to.
@mixin visually-hidden($focusable: false) {
    position: absolute;
    width   : 1px;
    height  : 1px;
    padding : 0;
    margin  : -1px;
    overflow: hidden;
    clip    : rect(0,0,0,0);
    border  : 0;

    @if $focusable {
        &:active,
        &:focus {
            position  : static;
            width     : auto;
            height    : auto;
            overflow  : visible;
            clip      : auto;
        }
    }
}