Chapter 13: Sass Map
Sass: Maps and the sass:map functions.
Maps are basically key-value dictionaries in Sass — think JSON objects but for your styles. They are the backbone of almost every modern Sass design system in 2026: colors, spacing scales, typography, breakpoints, themes, component variants, you name it.
Today we’ll treat this like a practical lab session: understand maps, load the module, go through every important sass:map function with examples, see real-world patterns, and build a small theme system together.
1. Quick Refresher – What is a Map in Sass?
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$breakpoints: ( "xs": 480px, "sm": 576px, "md": 768px, "lg": 992px, "xl": 1200px, "xxl": 1400px ); $colors: ( primary: #6c5ce7, secondary: #00b894, success: #00cec9, warning: #f1c40f, danger: #e74c3c, light: #f8f9fa, dark: #2d3436 ); |
- Keys → usually strings (but can be numbers, colors, etc.)
- Values → anything: numbers, colors, lists, other maps (nested maps = very common!)
- Maps are immutable — every function returns a new map (no side effects → safer code)
2. Loading the Module (Modern 2026 Requirement)
Always do this at the top:
|
0 1 2 3 4 5 6 7 |
@use 'sass:map' as m; // 'm' is short & common alias // or @use 'sass:map'; → map.get(...) |
Old map-get() etc. still work but show deprecation warnings in recent Dart Sass.
3. The sass:map Functions – Full Practical Tour (Dart Sass 2026)
Here are the essential ones, with playground examples + real usage.
A. map.get($map, $key, …$keys) – Get Value (Most Used!)
Supports deep lookup with multiple keys (huge for nested configs).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@debug m.get($colors, "primary"); // #6c5ce7 // Deep get example $theme: ( "light": ( "bg": white, "text": black, "accent": #6c5ce7 ), "dark": ( "bg": #2d3436, "text": #dfe6e9, "accent": #a29bfe ) ); @debug m.get($theme, "dark", "accent"); // #a29bfe |
Real usage – safe getter with fallback:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
@function color($name, $fallback: null) { @return m.get($colors, $name) or $fallback or #000; } .btn { background: color("primary"); } |
B. map.set($map, $key, $value, …$keys) – Create New Map with Updated Value
Deep set too!
|
0 1 2 3 4 5 6 7 8 |
$new-colors: m.set($colors, "info", #3498db); @debug $new-colors; // original + "info": #3498db |
Deep:
|
0 1 2 3 4 5 6 |
$updated-theme: m.set($theme, "light", "accent", #0984e3); |
Use case: Override theme tokens without mutating original.
C. map.merge($map1, $map2, …$maps) / map.deep-merge() (since ~1.27)
Shallow merge:
|
0 1 2 3 4 5 6 7 8 9 10 |
$base: ("font": "Inter", "size": 16px); $override: ("size": 18px, "weight": 500); $merged: m.merge($base, $override); // ("font": "Inter", "size": 18px, "weight": 500) |
Deep merge (great for themes):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
@use 'sass:map' as m; $light-overrides: ( "text": #111, "button": ("bg": #6c5ce7, "hover": #5a4bc7) ); $full-light: m.deep-merge($theme, ("light": $light-overrides)); |
Pro tip 2026: Use deep-merge for configuration overriding in libraries.
D. map.remove($map, $key, …$keys) – New Map Without Key(s)
|
0 1 2 3 4 5 6 |
$no-dark: m.remove($colors, "dark", "light"); |
Deep:
|
0 1 2 3 4 5 6 |
$no-accent: m.remove($theme, "dark", "accent"); |
Use case: Strip unwanted tokens before export.
E. map.keys($map) / map.values($map)
|
0 1 2 3 4 5 6 7 8 |
@debug m.keys($breakpoints); // "xs" "sm" "md" "lg" "xl" "xxl" (as list) @debug m.values($colors); // list of all color values |
Use case: Generate classes dynamically.
|
0 1 2 3 4 5 6 7 8 9 10 |
@each $bp in m.keys($breakpoints) { @media (min-width: m.get($breakpoints, $bp)) { .container-#{$bp} { max-width: m.get($breakpoints, $bp); } } } |
F. map.has-key($map, $key, …$keys) – Check Existence
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
@if m.has-key($colors, "primary") { // yes! } @if m.has-key($theme, "dark", "accent") { // deep check } |
Use case: Safe access in functions/mixins.
4. Real-World Example – Mini Theme System
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// _themes.scss @use 'sass:map' as m; $base-theme: ( "colors": ( "primary": #6c5ce7, "text": #2d3436, "bg": #f8f9fa ), "spacing": ( "xs": 4px, "sm": 8px, "md": 16px, "lg": 24px ), "typography": ( "base": 16px, "scale": 1.25 ) ); // Override for dark mode $dark-overrides: ( "colors": ( "primary": #a29bfe, "text": #dfe6e9, "bg": #2d3436 ) ); $themes: ( "light": $base-theme, "dark": m.deep-merge($base-theme, $dark-overrides) ); // Usage function @function theme($mode, $path...) { @return m.get(m.get($themes, $mode), $path...); } // In main file @use 'themes' as t; body.dark { background: t.theme("dark", "colors", "bg"); color: t.theme("dark", "colors", "text"); } .py-md { padding: t.theme("light", "spacing", "md"); // 16px } |
This pattern is very common in 2026 component libraries and design systems (Tailwind-like but custom).
5. Quick Tips & Gotchas (Dart Sass 2026)
- Maps are immutable → always assign result: $new := m.set(…)
- Keys can be anything, but strings are safest for readability
- Deep operations (multiple keys) are supported in get/set/remove/has-key
- Use m.deep-merge() for nested configs (shallow merge() only top level)
- Combine with @each + m.keys()/m.values() for loops
- For very large maps → performance is usually fine (Sass is compile-time)
- Old globals (map-get, map-merge, etc.) → avoid in new code
You’ve now mastered maps — this unlocks scalable, themeable, maintainable Sass like nothing else.
Next — want to build a complete responsive typography + spacing system using maps + lists + math functions? Or create a full button variant generator with maps? Or explore how maps work with @content in mixins?
Just tell me — we’re building something really powerful here! 🚀
