Sass Color Maps
Naming things is tough. I find naming Sass color variables to be particularly problematic.
Consider the following:
$red: #ff0000;
Many might consider this to be a poorly named variable. What if the value of $red were to change to #00ff00? Now our variable $red is actually green.
One solution, which I first read here, by Sacha Greif, is to use descriptive variable names like $red in conjunction with functional variable names like $color-link. As an example:
// Descriptive color variables. These should never change.
$red: #ff0000;
$green: #00ff00;
$blue: #0000ff;
// Functional color variables. These may change,
// but should only use existing descriptive variables listed above.
$color-link: $red;
$color-accent: $green;
This solves the problem of misnamed descriptive variables: $red is never going to be anything other than #ff0000. But, I think it introduces a new problem of maintainability. Consider the following:
// Heading colors
$color-heading-primary: $black;
$color-heading-secondary: $gray-dark;
$color-heading-tertiaty: $navy;
$color-heading-quaternary: $yellow-bright;
I've just defined four colors that will be used for headings. That's fine enough, but it means that now I have to either remember what each of my $color-heading-* vars actually equals, or refer back to the Sass partial where they're assigned, whenever I want to use them.
I might also add some border colors:
// Border colors
$color-border-primary: $gray-dark;
$color-border-secondary: $gray;
$color-border-tertiary: $gray-light;
$color-border-quaternary: $gray-lighter;
I think the assignments in this block are significantly harder to remember. They're all grays, and they're all used for border colors. Even as the person authoring the CSS on this project, I doubt I could always keep those straight. But the situation is worse still for the person who has to jump into this project after all these variables are already set up. S/he might look at a rule like border: $border-width-secondary solid $color-border-teriary; and have no idea what what that border actually looks like.
Having bumped into this problem on a number of recent projects, I recently started just using plain old descriptive variable names like $red: #ff0000; again (this too has already been written about). So when I see a rule like color: $green-dark;, I can at least understand, to some degree, what color is actually represented by the var $green-dark. If I ever want to change the value of $green-dark to be a blue color, I'll just set a new variable $blue-dark, and then search and replace for $green-dark. I've found this method easier to maintain, because 1) I've rarely ever needed to change the value of a color variable to something entirely different, and 2) the time gained in not having to memorize/refer back to a bunch of functional variable names more than makes up for any time I need to search and replace new var names.
But, there is still a problem. Consider the values I used for borders in a previous example above:
// Grays
$gray-dark: #333333;
$gray: #575757;
$gray-light: #808080;
$gray-lighter: #b3b3b3;
These values are still kind of hard to remember, but they make (some) sense. But what if I wanted to add an even lighter gray:
// Grays
$gray-dark: #333333;
$gray: #575757;
$gray-light: #808080;
$gray-lighter: #b3b3b3;
$gray-lightest: #e8e8e8;
Now I've got three "light-ish" gray color vars. What if I needed to add a gray that was lighter than the second to last, but still darker than the last?
// Grays
$gray-dark: #333333;
$gray: #575757;
$gray-light: #808080;
$gray-lighter: #b3b3b3;
$gray-lighter-er: #c4c4c4; // Lame.
$gray-lightest: #e8e8e8;
Now things are getting out of hand—these color var names are too hard to understand. I might not remember which is which, and someone jumping into the project certainly won't be able to easily start working with these.
I think Sass maps can solve this. Consider:
// Gray colors
$color-map-gray: (
1000: hsl(0, 0%, 20%), // #333333
2000: hsl(0, 0%, 34.1%), // #575757
3000: hsl(0, 0%, 50.2%), // #808080
4000: hsl(0, 0%, 70.2%), // #b3b3b3
5000: hsl(0, 0%, 76.9%), // #c4c4c4
6000: hsl(0, 0%, 91%) // #e8e8e8
);
Above, I have map containing all my grays. I no longer need to worry about naming each a var for each one, as I can just do a map-get() for the color I want. The colors are ordered from lightest to darkest, so that someone looking at color: map-get( $color-map-gray, 4000 ); has a sense of both what that color is, and where it falls in the spectrum of grays used on the site. hsl() makes ordering the colors much easier than using a hex value. I'm also using a lot of space in between numbers, so that I can add new values in between the existing ones without disrupting anything (a similar approach for z-index is a good idea):
// Gray colors
$color-map-gray: (
1000: hsl(0, 0%, 20%), // #333333
2000: hsl(0, 0%, 34.1%), // #575757
2500: hsl(0, 0%, 41%), // #696969 <-- New value
3000: hsl(0, 0%, 50.2%), // #808080
4000: hsl(0, 0%, 70.2%), // #b3b3b3
5000: hsl(0, 0%, 76.9%), // #c4c4c4
6000: hsl(0, 0%, 91%) // #e8e8e8
);
You get the idea. I've not tried this on a project yet, but I'm going to—we'll see how it goes. Let me know what you think.
Nice, I like this idea. I always get into a tangle with my color variables, going from 'lightgrey, mediumgrey to darkgrey'. And then when a new one is added it might become 'lightestgrey, lightergrey, lightgrey, etc.'. I'm going to try this out and will report back. What I do think, is that this might only work well with a bunch of variations of the same color.
First of all, I think that this is awesome. Thanks for writing this up. These are issues that pop up all the time. I love the use of hsl. I think that hsl makes so much more sense once one figures out how it works. I'm a huge fan of just using color names. So instead of doing [dark grey, grey, light grey...] or [grey 10, grey 5, grey 1...], I just do [charcoal, lead, graphite, silver...]. I like to give colors names because they are easier to remember and easier for people to talk about. But there is still the problem of knowing how lead and graphite differ. And this is where I suggest using a plugin for your editor:
These plugins highlight color values in your editor so you know what they are. They even highlight the variables that store color values too. If you are an Atom user, the Pigments plugin is super rad.
Stay awesome!
Good article. I too have faced these issues several times. When you need to do Theaming though functional names are the only way to make it work, as $button-bg-colour may be red for one brand and green for another.
Curious as to why you would not just use the percentage as the parameter:
color: map-get( $color-map-gray, 70%);or even just use the SASS colour functionshsl(0,0,70),darken(),lighten()etc..I quite like this idea of colour maps. I thought I'd quick share how I do colour vars. Although in LESS but same goes for SASS.
Reused brand colours, that have a hierarchy. I use comments to give more details on the theory behind the names.
I normally do this for standard colours that will be reused everywhere.
Then for adhoc colours I do...
For utility colours I do...
For transparency I do, denoting the transparency... Same if I was setting rgba() manually.
Note
I've had people question if they can plop color:#fff; in the code as white is never going to change. I disagree. Although the points for putting as a variable are small collectively they're enough, I believe to warrant doing it in the vars file. So first point is you need to see all the available colours used in the site, to stop repetition. This is the one place to see everything used in perhaps 10 stylesheets and comments on why it's used and where. Point two, is that you can write the rule for white in at least 5 different ways, hex, rgb, rgba, hsl and the word white. We want to be consistent in our code. We want rgba for example to show the use of alpha transparency and we want vars to be useable within preprocessor colour functions like fadeOut(); or darken();
So it's not really an arbitrary choice. CSS is hard enough to get groups of dev using consistently this is one way.
The idea of associating color variants with "weights" is similar to what we're already used to with font weights. You should already be referencing font weights with numerical values (300, 700, etc.) instead of keywords (light, bold, etc.), so why not use the same system for colors?
Google built out their Material Design color palette in that manner, and I've found it to be infinitely more useful than a big block of semi-descriptive variable names. I created a resource pen that makes it quick and easy to get Material Design colors on the fly. The colors and variants are all split out into nested maps, and then you can use the optional function I wrote to call whatever color you want.
I really like this idea. Wrangling color variables always feels like a hassle especially when a new color gets introduced midway through and then my light, lighter, lightest, dark, darker, darkest structure gets all messed up. Thanks for this neat idea, definitely going to try it on my next project!
great idea ;-) i also spent a lot of time for naming the color-variables. i think i try to work with this work-around. thx for posting