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

list.nth($list, $n)

Element at position $n (1-based; negative counts from the end)

list.nth(a b c, 2)b

list.length($list)

Number of elements

list.length(a b c)3

list.index($list, $v)

Position of $v, or null if absent

list.index(a b c, b)2

list.append($list, $v)

New list with $v added at the end

list.append(a b, c)a b c

list.join($l1, $l2)

Concatenates two lists

list.join(a b, c d)a b c d

list.separator($list)

comma, space, or slash

list.separatora, bcomma

list.slash($v1, $v2…​)

Builds a slash-separated list

list.slash(16px, 1.5)16px/1.5

list.zip($l1, $l2…​)

Combines lists element-wise

list.zip(1px 2px, solid dashed)(1px solid), (2px dashed)

list.is-bracketed($list)

Whether the list uses [ ]

list.is-bracketed([a b])true

@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) is 1, and list.nth(10px, 1) is 10px. 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) returns null.

  • Parentheses around a comma expression create a list, not a group. (1, 2) is a list; (1 + 2) is 3.

  • 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

map.get($map, $key)

Value for $key, or null if absent

map.get($bp, md)768px

map.set($map, $key, $v)

New map with $key set

map.set($bp, xxl, 1400px)

map.merge($m1, $m2)

New map combining both; $m2 wins on conflicts

map.merge($defaults, $overrides)

map.remove($map, $keys…​)

New map without those keys

map.remove($bp, sm)

map.has-key($map, $key)

Whether the key exists

map.has-key($bp, md)true

map.keys($map)

Comma-separated list of keys

map.keys($bp)sm, md, lg, xl

map.values($map)

Comma-separated list of values

map.values($bp)576px, 768px, …​

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().

Iterating a map

@each with two variables destructures each entry:

@each $name, $value in $breakpoints {
  .min-#{$name} { min-width: $value; }
}

Map iteration order is the insertion order, so the output is deterministic and matches how the map was written.

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.