Debugging Sass Maps

Share this article

Maps are great, but maps are not that easy to debug. When you have simple maps, it’s usually quite easy to understand what’s going on, but when you work with huge and/or nested, possibly dynamically updated maps, it can get difficult to keep track of what happens.

…and then, all of a sudden, a bug appears somewhere in your code. Breaking the site you’re developing somehow.

Fear not my friends, I have a possible solution.

What about inspect() or @debug?

The other day, web developer Stuart Trann asked how we could debug a map to see what it looks like.

The first thing that came to my mind was the inspect() function. This function was added at pretty much the same time when maps came out, in order to inspect the content of a map.

You may have noticed that if you try to display a map as a CSS value, you get this message:

(sass: map) isn’t a valid CSS value.

Dang. So that’s why the inspect() function has been implemented to Sass core. So you can see something like:

test {
  inspect: (sass: map);
}

A problem with inspect() is that it displays the map as a single line, which is far from convenient, especially when you have loads of keys in there. One could think of @debug, which intends to print some content in the console but that really doesn’t help much since it also keeps it to one line.

Time to build something that helps us folks.

Mixin-ify all the things!

What if we used the fact that CSS is quite neat when it comes to displaying key/value pairs? We could loop on the map, and display the key and the value as a declaration in a dummy selector. Let’s do this!

/// Prints a map as a CSS rule
/// @param {Map} $map

@mixin debug-map($map) {
    @at-root {
        __properties__ {
            @each $key, $value in $map {
                #{$key}: $value;
            }
        }
    }
}

Note: we are using __properties__ to make it clear that it’s some internal debugging stuff, kind of like __proto__ in JavaScript.

Not bad. Now, what if we have a nested map inside this one? Sass will fail on the $value display. So we could either come up with some weird and complex logic, or we could keep it simple and use inspect(), like this:

/// Prints a map as a CSS rule
/// @param {Map} $map

@mixin debug-map($map) {
    @at-root {
        __properties__ {
            @each $key, $value in $map {
                #{$key}: inspect($value);
            }
        }
    }
}

This way, if we encounter a nested map, it will be display as any other value. If we want to debug this nested map now, we should use the debug-map mixin on this specific value directly.

Adding some extra informations

What if we decided to add some extra information, like the length, the depth or the string representation of the inspected map? Now, that could be helpful, couldn’t it?

To do that, we need to tweak our mixin a bit. Instead of directly outputting a selector, we could print a proprietary @-directive grouping everything we need, like so:

@mixin debug-map($map) {
    @at-root {
        @debug-map {
            __toString__: inspect($map);
            __length__: length($map);
            __keys__: map-keys($map);
            __properties__ {
                @each $key, $value in $map {
                    #{$key}: inspect($value);
                }
            }
        }
    }
}

To compute the depth, we need an extra tool since there is no built-in function for this. It turns out I’ve made such a function a while back but never blogged about it. It’s about time I guess!

/// Compute the maximum depth of a map
/// @param {Map} $map
/// @return {Number} max depth of `$map`

@function depth($map) {
  $level: 1;

  @each $key, $value in $map {
    @if type-of($value) == "map" {
      $level: max(depth($value) + 1, $level);
    }
  }

  @return $level;
}

This is a recursive function, looping over each key/value pair from the map until it finds a map, in which case it calls itself and so on. Then, it returns a number: the map’s depth. Let’s update our debug-map mixin with this information:

@mixin debug-map($map) {
    @at-root {
        @debug-map {
            __toString__: inspect($map);
            __length__: length($map);
            __depth__: depth($map);
            __keys__: map-keys($map);
            __properties__ {
                @each $key, $value in $map {
                    #{$key}: inspect($value);
                }
            }
        }
    }
}

A last addition I can suggest is printing the type of each value from the map right before its associated key. This could be handy in some cases:

@mixin debug-map($map) {
    @at-root {
        @debug-map {
            __toString__: inspect($map);
            __length__: length($map);
            __depth__: depth($map);
            __keys__: map-keys($map);
            __properties__ {
                @each $key, $value in $map {
                    #{'(' + type-of($value) + ') ' + $key}: inspect($value);
                }
            }
        }
    }
}

Let’s try this!

Let’s look at the following map for our tests:

$person: (
    'name': 'Kitty Giraudel',
    'location': 'France',
    'age': 22,
    'glasses': true,
    'kids': null,
    'skills': ('HTML', 'CSS', 'Sass', 'JavaScript'),
    'languages': (
        'French': 'native',
        'English': 'fluent',
        'Spanish': 'notions'
    )
);

And now, let’s debug it!

@include debug-map($person);

Once compiled it would yield this result:

@debug-map {
  __toString__: ("name": "Kitty Giraudel", "location": "France", "age": 22, "glasses": true, "has-kids": false, "skills": ("HTML", "CSS", "Sass", "JavaScript"), "languages": (("French": "native", "English": "fluent", "Spanish": "notions")));
  __length__: 7;
  __depth__: 2;
  __keys__: "name", "location", "age", "glasses", "has-kids", "skills", "languages";
  __properties__ {
    (string) name: "Kitty Giraudel";
    (string) location: "France";
    (number) age: 22;
    (bool) glasses: true;
    (null) kids: false;
    (list) skills: "HTML", "CSS", "Sass", "JavaScript";
    (map) languages: ("French": "native", "English": "fluent", "Spanish": "notions");
  }
}

Final thoughts

There you go folks. I hope that this might come in handy if you ever struggle with maps. Feel free to improve or add extra features to this little mixin. My goal was to keep it as simple as it can be and not to over-crowd it too much. Even the map depth might seem a little overkill in most cases, but you know… Nice addition!

Frequently Asked Questions (FAQs) on Debugging Sass Maps

What is the purpose of using Sass Maps in CSS pre-processing?

Sass Maps are a powerful feature in CSS pre-processing that allows you to store multiple values in a single Sass variable. They are similar to arrays in JavaScript, but they allow you to use any value, not just integers, as keys. This makes them incredibly versatile for storing and manipulating data in your stylesheets. For example, you can use Sass Maps to store color palettes, font stacks, or responsive breakpoints, and then use Sass functions to retrieve or manipulate these values.

How can I debug Sass Maps using the @debug rule?

The @debug rule in Sass is a useful tool for debugging Sass Maps. It prints the value of a Sass expression to the console, which can help you understand what’s going on in your code. To use it, simply include the @debug rule followed by the expression you want to inspect. For example, if you have a Sass Map called $colors, you could use @debug $colors to print the entire map to the console.

What is the difference between @debug and @warn in Sass?

Both @debug and @warn are used for debugging in Sass, but they serve slightly different purposes. The @debug rule prints the value of a Sass expression to the console, which can be useful for understanding what’s going on in your code. On the other hand, the @warn rule is used to display warnings. It’s useful for alerting you to potential issues in your code that might not cause an error but could lead to unexpected results.

How can I use the @each directive to iterate over a Sass Map?

The @each directive in Sass allows you to iterate over each key-value pair in a Sass Map. This can be useful for generating repetitive CSS. For example, if you have a Sass Map of colors, you could use the @each directive to generate a series of classes, each with a different background color.

Can I nest Sass Maps, and how can I access nested values?

Yes, you can nest Sass Maps, which means you can have a Sass Map as a value within another Sass Map. This can be useful for organizing complex data structures. To access a value in a nested Sass Map, you can use the map-get function with the key for the nested map, followed by the key for the value you want to retrieve.

How can I use the map-get function to retrieve values from a Sass Map?

The map-get function in Sass allows you to retrieve a value from a Sass Map using its key. The function takes two arguments: the map and the key. For example, if you have a Sass Map called $colors that contains a key ‘primary’ with a value of ‘blue’, you could use map-get($colors, primary) to retrieve the value ‘blue’.

What is the map-has-key function in Sass, and how can I use it?

The map-has-key function in Sass allows you to check if a Sass Map contains a specific key. This can be useful for preventing errors in your code. The function takes two arguments: the map and the key you want to check for. If the map contains the key, the function returns true; otherwise, it returns false.

How can I add, update, or remove values in a Sass Map?

Sass provides several functions for manipulating Sass Maps, including map-merge, map-remove, and map-set. The map-merge function allows you to combine two maps into one, while the map-remove function allows you to remove a key-value pair from a map. The map-set function allows you to add a new key-value pair to a map or update an existing one.

Can I use Sass Maps with other Sass features like mixins or functions?

Yes, you can use Sass Maps in conjunction with other Sass features like mixins or functions. For example, you could create a mixin that takes a Sass Map as an argument and generates CSS based on the map’s values. Or, you could create a function that takes a Sass Map and returns a manipulated version of the map.

How can I use Sass Maps to create responsive designs?

Sass Maps can be a powerful tool for creating responsive designs. For example, you could create a Sass Map of breakpoints, and then use the map in conjunction with media queries to generate responsive CSS. This can make your code more organized and easier to maintain.

Kitty GiraudelKitty Giraudel
View Author

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/they.

sasssass mapssass mixinsStuR
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week