Lists and Maps
|
This section documents Sass/SCSS as implemented by Dart Sass, the current official and actively maintained compiler — it is not tied to any specific book, build tool, or CSS framework (Bootstrap, Bulma, etc.). Unlike the other reference sections on this site, no single reference book underpins it: the content was generated with the assistance of AI from general knowledge of Dart Sass, and should be verified against the current official documentation at sass-lang.com before relying on it in production. Sass continues to evolve, so behaviour described here may lag the compiler you are actually running. This section’s bibliography lists the reference material consulted while preparing these pages. |
Lists and maps are Sass’s two collection types. They are what turn a stylesheet from a pile of literal values into something driven by a single source of truth — a palette, a spacing scale, a set of breakpoints.
Both are immutable. Every "modifying" function returns a new collection rather than changing the original, so results must be reassigned.
Lists
A list is an ordered sequence of values. Sass is unusually relaxed about how one is written: elements may be separated by commas, spaces, or slashes, and the surrounding parentheses are optional in most positions.
$comma-list: 10px, 20px, 30px;
$space-list: 10px 20px 30px;
$font-stack: "Inter", "Helvetica Neue", sans-serif;
$shorthand: 1px solid #ccc; // yes, this is a 3-element list
$nested: (1px 2px), (3px 4px); // a 2-element list of 2-element lists
$empty: ();
$single: (10px,); // trailing comma forces a 1-element list
The key insight is that ordinary CSS values already are lists. margin: 10px 20px is a space-separated
two-element list, and font-family: a, b, c is a comma-separated three-element list. This is why a list’s
separator is preserved through the compiler — it becomes the punctuation in the output.
List functions
Load sass:list. Sass lists are 1-indexed, which is the single most common source of off-by-one bugs for
anyone arriving from another language.
| Function | Description | Example |
|---|---|---|
|
Element at position |
|
|
Number of elements |
|
|
Position of |
|
|
New list with |
|
|
Concatenates two lists |
|
|
|
|
|
Builds a slash-separated list |
|
|
Combines lists element-wise |
|
|
Whether the list uses |
|
@use "sass:list";
$shadows: (0 1px 2px rgb(0 0 0 / 0.1),); // trailing comma forces a comma-separated list
$shadows: list.append($shadows, 0 4px 12px rgb(0 0 0 / 0.15));
.card { box-shadow: $shadows; }
Note the reassignment — list.append returns a new list, it does not mutate $shadows in place. The base list
must already be comma-separated (hence the trailing-comma trick above): appending to a space-separated list and
passing $separator: comma doesn’t wrap the new element in, it re-separates every existing element too, turning
0 1px 2px rgba(…) into four comma-joined pieces instead of one shadow.
Iterating a list
$sides: top, right, bottom, left;
@each $side in $sides {
.p-#{$side} { padding-#{$side}: 8px; }
.m-#{$side} { margin-#{$side}: 8px; }
}
Gotchas
-
A single value is a one-element list.
list.length(10px)is1, andlist.nth(10px, 1)is10px. This makes functions that accept "one or several" values easy to write. -
()is both the empty list and the empty map. They are the same value;map.get((), x)returnsnull. -
Parentheses around a comma expression create a list, not a group.
(1, 2)is a list;(1 + 2)is3. -
Comma beats space in nesting. In
1px 2px, 3px 4px, the comma is the outer separator, giving a two-element list of two-element lists.
Maps
A map associates keys with values. It must always be written with parentheses:
$theme-colours: (
primary: #3f51b5,
success: #43a047,
warning: #fb8c00,
danger: #e53935
);
$breakpoints: (
"sm": 576px,
"md": 768px,
"lg": 992px,
"xl": 1200px
);
Keys may be any Sass value — unquoted strings (as above) are conventional and read well. Quoted and unquoted
strings compare equal, so map.get($breakpoints, md) and map.get($breakpoints, "md") both work.
Maps are not valid CSS values: a map cannot be used as a property value directly. It exists purely as compile-time data.
Map functions
Load sass:map.
| Function | Description | Example |
|---|---|---|
|
Value for |
|
|
New map with |
|
|
New map combining both; |
|
|
New map without those keys |
|
|
Whether the key exists |
|
|
Comma-separated list of keys |
|
|
Comma-separated list of values |
|
map.get, map.set, and map.has-key also accept multiple keys to reach into nested maps:
@use "sass:map";
$config: (
colours: (
brand: (primary: #3f51b5, secondary: #7986cb),
state: (error: #e53935)
)
);
$primary: map.get($config, colours, brand, primary); // #3f51b5
$config: map.set($config, colours, state, warning, #fb8c00);
Without the multi-key form this would be a chain of nested map.get() calls, each needing its own null check.
Worked example: a responsive breakpoints map
This is the single most common real use of a map — one source of truth for breakpoints, consumed by a mixin (see Mixins):
@use "sass:map";
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
) !default;
@mixin respond-to($name) {
$width: map.get($breakpoints, $name);
@if $width == null {
@error "Unknown breakpoint `#{$name}`. Available: #{map.keys($breakpoints)}.";
}
@media (min-width: $width) {
@content;
}
}
.container {
padding: 0 16px;
@include respond-to(md) { max-width: 720px; margin: 0 auto; }
@include respond-to(lg) { max-width: 960px; }
@include respond-to(xl) { max-width: 1140px; }
}
.container { padding: 0 16px; }
@media (min-width: 768px) { .container { max-width: 720px; margin: 0 auto; } }
@media (min-width: 992px) { .container { max-width: 960px; } }
@media (min-width: 1200px) { .container { max-width: 1140px; } }
The !default on $breakpoints means a consumer can supply their own scale via @use … with (see
Partials & Modules), and the @error guard turns a typo into a clear
compile-time failure naming the valid options, rather than a silently missing media query.
Worked example: a palette with generated utilities
@use "sass:map";
@use "sass:color";
$palette: (
primary: #3f51b5,
success: #43a047,
danger: #e53935
);
// Derive light/dark variants without hand-writing them
@each $name, $base in $palette {
.bg-#{$name} { background: $base; }
.bg-#{$name}-light { background: color.adjust($base, $lightness: 20%); }
.bg-#{$name}-dark { background: color.adjust($base, $lightness: -15%); }
.text-#{$name} { color: $base; }
}
Adding one entry to $palette now produces four more classes automatically — see
Control Flow for the loop constructs and
Built-in Modules for color.adjust().
Immutability in practice
Because both types are immutable, a loop that builds up a collection must reassign each time:
@use "sass:map";
@use "sass:math";
$scale: ();
@for $i from 1 through 6 {
$scale: map.set($scale, $i, 4px * $i); // reassign -- map.set returns a new map
}
// $scale is now (1: 4px, 2: 8px, 3: 12px, 4: 16px, 5: 20px, 6: 24px)
Forgetting the reassignment is a silent no-op: the function computes a new collection and throws it away. If a map or list "isn’t updating", this is almost always why.