Friendlier color names with Sass maps

Posted on 12 February, 2014

For us front end devs, Sass is a marvelous, marvelous thing, but although it gives you numerous powerful tools, it’s fairly unopinionated about exactly how you should put them to use. If you’re not careful, it’s just as easy to end up with a mess of poorly structured code as it was in the dark pre-Sass days.

One thing we’ve struggled with recently has been variable naming conventions, particularly when it comes to colors.

Here are some of our old-style Sass color declarations from the _config.scss file for a recent project:

// Primary tones - purples
$color__primary--xxdark: #302844;
$color__primary--xdark: #342C52;
$color__primary--dark: #322161;
$color__primary--darkmid: #3B2772;
$color__primary--lightmid: #462E85;
$color__primary--light: #65519A;

// Neutral tones - greys
$color__neutral--xxdark: #52595E;
$color__neutral--xdark: #58646E;
$color__neutral--dark: #8C9296;
$color__neutral--darkmid: #D1D5D9;
$color__neutral--mid: #C9CED5;
$color__neutral--lightmid: #D8DDE2;
$color__neutral--light: #E5E7EA;
$color__neutral--xlight: #EFF1F2;
$color__neutral--xxlight: #F7F8F8;

The problem

Although we have a consistent naming pattern in use above ($namespace__palette--tone), we’ve found three major problems with it:

  1. It’s not descriptive enough: It’s unclear what color will actually be output when declaring (for example) $color__neutral--mid

  2. It lacks structure: The list of variables (however arranged) can’t fully convey the relationships that exist between and within color palettes

  3. It’s too verbose: The variable names are too long, and are too hard to remember when writing actual module code

To replace this pattern, we’re moving to a system based on natural color names (rather than primary, neutral etc) and harnessing the power of Sass maps to structure our colors into one or more palettes. Here’s how we got there.

Solving issue 1: The naming of things

There are only two hard things in Computer Science: cache invalidation and naming things.”

— Phil Karlton

Naming colors in terms of document hierarchy (primary, secondary) or general tone (neutral) appeared seductive at first, seeming as it did to abstract color implementation from the vaguries of minor design changes. However in practice we’ve found it to be something of a path to madness.

With this approach to naming, it quickly becomes difficult for to understand what color is actually being declared for a value when writing and (more importantly) *reading* Sass. Cue much developer jumping back and forth between the color variable declarations and the module under development, frequent grepping for variable names to see what color they point to, and general frustration that There Must Be A Better Way.

The theoretical benefits of our previous approach were also undermined by our experience that, while project colors do change from time to time, very rarely does, say, the primary link color change from blue to green, unless you’re working on a specifically themeable/​skinnable project or a major re-design effort. And in the rare instances where a design change is of that magnitude, grepping and replacing as required isn’t such a big deal, compared to the benefits we derive from color-based descriptive names that are easily read and understood in context.

We do tend to have a range of tones/​shades for our main colors though, which leads us to…

Solving issue 2: Gimme structure

Maps are a new datatype in Sass 3.3 that are associations between SassScript values: think of them as like associative arrays in PHP, Ruby hashes or indeed JavaScript objects.

Instead of declaring separate variables per color, in our project’s _config.scss we now declare a single global variable, $palettes which is a nested map of named color palettes:

$_color-base-grey: rgb(229,231,234);

$palettes: (
    purple: (
        base:   rgb(42,40,80),
        light:  rgb(51,46,140),
        dark:   rgb(40,38,65)
    ),
    grey: (
        base:  $_color-base-grey,
        light: lighten($_color-base-grey, 10%),
        dark: darken($_color-base-grey, 10%)
    )
);

At a minimum every palette defines a base color, and then optionally adds tones use the following naming pattern:

  • x-dark
  • dark *
  • mid-dark
  • base (default)
  • mid-light
  • light *
  • x-light

*If we have fewer than seven tones, we’ll define light and dark first to allow for adding intermediate tones at a later date.

Internal to a palette, tones may be declared a separate rgb(a) values (or indeed as hsl(a) if that’s your thing), or derived programatically from the base color with Sass’s color functions. The aim is that you shouldn’t have to worry about how a tone is generated in order to use it in a color declaration.

Solving issue 3: Keeping it short

To actually call our palettes in our styles, we use a very simple custom Sass function to access colors in our palette by name and optional tone/​shade:

@function palette($palette, $tone: 'base') {
    @return map-get(map-get($palettes, $palette), $tone);
}

By declaring a default value of 'base' for our tone parameter, we can then use just palette($palette-name) to access the base color for a palette like so:

a {
    color: palette(purple);
    &:hover {
        color: palette(purple, light);
    }
}

So there you have it, with a little help from Sass maps we now have a succinct, structured method for declaring and using colors in our stylesheets. The code for palette() is available in this Pen if you want to try it out.

This article was originally posted in 2014 by Tom Davies on the Erskine blog.