Latest posts

Friendlier colour names with Sass maps

For us front end devs, Sass is a marvellous, marvellous 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 easy to end up with 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 colours.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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 colour 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 colour 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 colour names (rather than primary, neutral etc) and harnessing the power of Sass maps to structure our colours 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 colours in terms of document hierarchy (primary, secondary) or general tone (neutral) appeared seductive at first, seeming as it did to abstract colour 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 colour is actually being declared for a value when writing and (more importantly) reading Sass. Cue much developer jumping back and forth between the colour variable declarations and the module under development, frequent grepping for variable names to see what colour 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 colours do change from time to time, very rarely does, say, the primary link colour 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 colour-based descriptive names that are easily read and understood in context.

We do tend to have a range of tones/shades for our main colours 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 colour, in our project’s _config.scss we now declare a single global variable, $palettes which is a nested map of named colour palettes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$_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 colour, 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 colour with Sass’s colour 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 colours in our palette by name and optional tone/shade:

1
2
3
@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 colour for a palette like so:

1
2
3
4
5
6
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 colours in our stylesheets. The code for palette() is available in this Pen if you want to try it out.

Tom Davies
Published in: