Nested Maps & Deep Access

Model real design tokens with maps inside maps, then read and update them safely.

Once a project grows, a flat map stops being enough. Real design tokens are hierarchical: a colour has shades, a scale has steps, a component has variants. Sass lets you nest maps as deeply as you like, and the sass:map module gives you tools to walk that tree.

Reading a nested value

map.get() accepts multiple keys and drills down one level per key. No manual chaining, no intermediate variables.

@use 'sass:map';
$theme: (
  'color': (
    'brand': (
      'base': #CC6699,
      'dark': #A04B75,
    ),
  ),
);

// One call, three keys deep:
.btn { background: map.get($theme, 'color', 'brand', 'base'); } // #CC6699

Updating without mutating

Maps are immutable, so map.set() and map.deep-merge() return a new map. map.set() also accepts a key path, and map.deep-merge() merges nested maps recursively instead of clobbering the whole branch the way map.merge() would.

FunctionWhat it does
map.get($m, k1, k2)Read down a key path
map.set($m, k1, k2, v)Return a copy with one path changed
map.merge($a, $b)Shallow merge — top level only
map.deep-merge($a, $b)Recursive merge of nested maps
Think of a nested map as the single source of truth for your system. Everything else — utilities, components, themes — reads from it rather than hard-coding values.

Example

Example · css
@use 'sass:map';

// A realistic, nested token tree
$tokens: (
  'palette': (
    'brand':   (50: #f8e9f1, 500: #CC6699, 900: #5c2340),
    'neutral': (50: #f7f7f8, 500: #6b7280, 900: #111827),
  ),
  'radius': ('sm': 4px, 'md': 8px, 'lg': 16px),
);

// Safe, path-aware accessor
@function token($keys...) {
  @if not map.has-key($tokens, $keys...) {
    @error 'Unknown token path: #{$keys}';
  }
  @return map.get($tokens, $keys...);
}

.card {
  background: token('palette', 'brand', 50);
  color:      token('palette', 'brand', 900);
  border-radius: token('radius', 'md');
}

// Extend the system without touching the source map
$tokens: map.deep-merge($tokens, (
  'palette': ('brand': (700: #7d3358))
));
// brand 50/500/900 survive; 700 is added

When to use it

  • A design system stores its color palette as a nested map (color -> variant -> value) so components access brand.primary.500 through a helper function.
  • A developer models typography tokens as a nested map keyed by role and property, then generates CSS custom properties by recursively iterating both levels.
  • A responsive mixin library stores breakpoint and property data in a nested map so a single @each loop can emit all responsive utility classes.

More examples

Defining and reading a nested map

Defines a two-level color palette map and wraps the nested map.get() calls in a helper function for clean call sites.

Example · css
@use 'sass:map';

$palette: (
  blue: (
    100: #dbeafe,
    500: #3b82f6,
    900: #1e3a8a
  )
);

@function color($hue, $shade) {
  @return map.get(map.get($palette, $hue), $shade);
}

.btn { background: color(blue, 500); }

Updating a value in a nested map

Uses map.deep-merge() to update a single nested value without rebuilding the entire top-level map from scratch.

Example · css
@use 'sass:map';

$tokens: (color: (primary: #2563eb), space: (base: 1rem));

// Merge an override into the color sub-map
$tokens: map.deep-merge($tokens, (color: (primary: #e74c3c)));

// Now tokens color.primary == #e74c3c
.btn { background: map.get(map.get($tokens, color), primary); }

Iterating two-level nested maps

Iterates the outer map by name, then reads individual properties from each nested sub-map to emit typed typography utility classes.

Example · css
@use 'sass:map';

$scale: (sm: (size: 0.875rem, weight: 400), lg: (size: 1.25rem, weight: 600));

@each $name, $props in $scale {
  .text-#{$name} {
    font-size:   map.get($props, size);
    font-weight: map.get($props, weight);
  }
}

Discussion

  • Be the first to comment on this lesson.