HTML & CSS
Article

Making Use of Sass’ Zip() Function

By Dennis Gaebel

Sass alleviates the need to write repetitive code when it comes to our stylesheets, but you knew that already or else you wouldn’t be reading this far. I want to show you a nice, savvy way to write shorthand properties without the need to remember the order of values for CSS like animation and font.

Before You Begin : Since I’m using Sass maps it might be a good idea to get comfortable with them. It just so happens there are at least two great articles.

Never Memorize What You Can Look Up In A Book

The title you just read is a famous quote from Albert Einstein -and a good one at that. My best guess is that I’m not the only developer in the world asking themselves “Now…does direction come after fill-mode? or is it the other way around?”

The setup that follows might not be for everyone, but I want you to take note of the approach. You could change this to work any way you want. Remember … the sky’s the limit.

Sass

@function sh-setup($config) {
  @return zip(map-values($config)...);
}

$animation_config: (
  name: none,
  duration: 0s,
  timing: ease,
  delay: 0s,
  iteration: 1, // infinite
  direction: normal, // reverse, alternate, alternate-reverse
  fill-mode: none, // forwards, backwards, both
  play-state: running
);

.object {
  animation: sh-setup($animation_config);
}

CSS

.object {
  animation: none 0s ease 0s 1 normal none running;
}

What Happened?

Our sh-setup() function takes an argument called config that passes it’s value to map-values() and the result is finally passed to the zip() function. This function takes a comma separated list returned by map-values() and converts that list into a space separated string! Whoa! Daaaaaannnnnggggg!!!

So…that’s cool and all, but why the three full stops (or ellipsis) at the end of map-values()? Well, the ellipsis says to map-values() “allow multiple values that are comma separated to be passed.” For example, when we strip away zip() from the equation we’ll end up with the following string returned.

none, 0s, ease, 0s, 1, normal, none, running

If we didn’t add ... Sass would output our shorthand incorrectly because zip() can’t accept our multiple comma separated values. It sees the output above as one single string. The ... helps tell Sass to accept an unknown number of arguments in order to strip away the commas. We the developers have to tell Sass it’s alright to split the string into separate cases so we end up with:

none 0s ease 0s 1 normal none running

Going Further

Now that we’ve done it with animation let’s try that same approach with the font shorthand. The font property takes values for it’s shorthand in a very specific order otherwise the entire statement fails. If you don’t recall the order (ha-ha, who does) then let me refresh your memory:

  • font-style
  • font-variant
  • font-weight
  • font-size
  • line-height
  • font-family

Now let’s set this up to work the way we want. Take note we’re not touching our sh-setup() function one bit and instead we’re working around it.

@function sh-setup($config) {
  @return zip(map-values($config)...);
}

$fonts: "'Proxima Nova', Arial, sans-serif";

$font_config: (
  font-style: normal,
  font-variant: normal,
  font-weight: normal,
  font-measurements: 100%/1.5,
  font-family: unquote($fonts)
);

body {
  font: sh-setup($font_config);
}

CSS

font: normal normal normal 100%/1.5 'Proxima Nova', Arial, sans-serif;

I placed font-size and line-height into one variable in order to avoid over complication since we need the / to come in-between the font-size and line height value. The unquote() function is the magic that allows us to output multiple fonts for the font-family property. This function helps preserve the single quotes recommended by the W3C for font family names containing whitespace to avoid mistakes in escaping. We also maintain commas for multiple font-family names.

Another way we could do the previous approach is defining another map to hold our font-families, and still use the same function we’ve been using all along.

@function sh-setup($config) {
  @return zip(map-values($config)...);
}

$fonts: (
  primary-font: "'Proxima Nova'", // font family names containing whitespace write like this"'Font Name'"
  font-fallback1: Helvetica,
  font-fallback2: Arial,
  font-failure: sans-serif
);

$font_config: (
  font-style: normal,
  font-variant: normal,
  font-weight: normal,
  font-measurements: 100%/1.5,
  font-family: unquote(#{map-values($fonts)})
);

body {
  font: sh-setup($font_config);
}

I like the readability in this case and I know exactly what font is intended for display in terms of priorities. The downside is that I had to interpolate map-values() within unquote() making this a bit more complicated for the sake of readability.

Parting Thoughts

Now that we’ve seen these use cases with the animation and font shorthand, where else do you think we could use something like zip()? Could it be used for the background shorthand? How about the grid syntax? It’s really up to you to decide the approach. I hope I’ve inspired you at the very least to investigate other helper functions like zip() available to front-end developers that want to use Sass. As you venture into the unknown keep in mind “A person who never made a mistake never tried anything new.” ~Einstein

Comments
HugoGiraudel

I found a way to simplify the example with fonts:

$font_config: (
  font-style: normal,
  font-variant: normal,
  font-weight: normal,
  font-measurements: 100%/1.5,
  font-family: #{'Proxima Nova', 'Helvetica', 'Arial', sans-serif}
);

Simply by interpolating the list of font families, you cast it as a string thus circumventing the zip issue you would face when dealing with a list. No need for an extra map, a double-quote hack or the unquote function. Easy peasy.

Edit: Sass Guidelines recommend always quoting string, even if they don't include spaces. wink

dezinescientist

Funny Hugo,

This Sass code is extracted from your guidelines document:

$breakpoints: (
  'medium': (min-width: 800px),
  'large': (min-width: 1000px),
  'huge': (min-width: 1200px),
);

As you can see, each value field in the outer map is a map itself whose key is an unquoted string data type in contradiction to your recommendation/advise here.

Maybe if you follow your own advise, we can then listen to you attentively.

What do you think?

HugoGiraudel

I suppose I should clarify Sass Guidelines to explain the difference between a string intended to be use as a CSS value, and a string when sticking to the data type only.

For instance, we do not quote sans-serif even if it a string, because it will fail when output in CSS if wrapped in quotes. For the same reason, we do not quote min-width because when printed as part as a @media directive, it will fail as well if quoted.

I hope it makes sense. I'll add something about this to the guidelines, thanks for noticing.

Edit: done. This will be live in 1.0.1.

dezinescientist
  • sans-serif is not a string. It's a CSS identifier (keyword) instead. There's a huge difference between strings and identifiers in CSS.

  • min-width is a media feature housed inside an expression denoted by a pair of parentheses, and this expression happens to resemble the syntax and grammar of a Sass map. Again this is not a string to begin with and therefore to warrant the quotes treatment.

HugoGiraudel

When it comes to Sass, both are strings. End of story.

dezinescientist

and thus the optional quotation in Sass for these constructs to avoid this trap.

Your guidelines are draconian and unnecessarily stringent.

I suggest that you take it easy and be reminded that W3C standards at the end of the day are just recommendations for user agents vendors and not laws or rules. Just imagine what can be said about your so-called guidelines then.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.