Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

The CNS Color Naming System

Last updated:

I'm doing some research for CSS Colors 4. One of the things I've wanted to do for a while is create a good color naming scheme (picking up some work that was last done ten years ago, when CSS Colors 3 was first written). But I've run into some problems...

To start, the entire purpose of a color naming scheme is to provide people with a simple and intuitive way to indicate a decent spread of colors, when the precise shade isn't vitally important. There are multiple numeric ways of indicating a color in CSS, such as the hex notation, the rgba() function, and the hsla() function, but none of them are very intuitive. (HSL is slightly better than RGB, but still not great.) The 16 HTML color names are reasonable for this - everyone knows them, so when you want a "red", you don't have to remember that it's written as #f00; you just write red and you're done.

The purpose of the extended color keywords was to continue this with many more colors, but it failed miserably. It was based on a version of the old X11 rgb.txt file, defining the named colors for unix terminals. I'm not sure who came up with this, but I'll just be brash and call them stupid. This is a horrible list. It's non-uniform, uses contradictory naming schemes, and the names it uses are completely arbitrary. Without any pattern to them, you have to remember all 147 color names individually.

Much better naming schemes have existed in the past. The best one I've discovered so far is the CNS scheme. CNS uses 7 base color names (red, orange, brown, yellow, green ,blue, and purple), allows you to combine them for various shades, and lets you use intuitive adjectives to indicate the lightness and saturation of the color. It produces colors like "very light red" and "vivid purplish blue". From what I understand, the base colors chosen are spaced relatively uniformly based on the way our eyes see color, which is why the 'cyan' RGB primary is missing (we can't see difference in blue/green colors as well as we can see others).

I'd like to introduce this into CSS as a way to create colors, perhaps in a color() function (it needs to be a function to avoid ambiguity, because colors can be up to five keywords, like "very light grayish yellow green"). I've found two definitions of the syntax: one slightly simpler, and one slightly more complex, but perhaps better (the grammar is on the second page of the PDF, numbered 256). †

The problem is in mapping these color names back to RGB. I haven't found any algorithms or tables at all for the simpler scheme. The latter scheme does include an algorithm in the PDF, but I'm not certain it holds some invariants that I want. In particular, I want to make sure that all colors using a particular lightness adjective have the same perceptual lightness (this invariant is extremely violated by existing schemes like HSL, which claim that "blue" and "yellow" have the same lightness). I'm going to start playing with it and see how it looks, but ultimately I can't trust my own subjective judgement because I'm green-colorblind.

So, now we come to the point of this post. Are any of you, dear readers, familiar with any algorithms (or preferably tables) mapping these sorts of color names to a numeric scheme? It doesn't have to be RGB - I can convert anything to anything. I'd just prefer to reuse some existing work, if it's of sufficient quality, than spend a decent amount of time trying to do it myself, and then having to get a sufficiently interested non-colorblind person check it over. Hit me up on Twitter or Plus, please!


In CSS grammar, the complex one would look something like:

<named-color> = color( [ <gray-color> | <chromatic-color> ] [ , <opacity>]? )

<gray-color> = black | white | <lightness> gray

<chromatic-color> = <tint> <hue> | <shade> <hue> | 
                    [ <lightness> || <saturation> ]? <hue>

<tint> = whitish | pale | brilliant | vivid

<shade> = blackish | dim | deep | vivid

<saturation> = grayish | moderate | strong | vivid

<lightness> = very dark | dark | moderate | light | very light

<hue> = <base-color> | <base-color>{2} | <splash-color> <base-color>

<base-color> = red | orange | brown | yellow | green | blue | purple

<splash-color> = reddish | orangish | brownish | yellowish | greenish | bluish | purplish

<opacity> = <number> | <percentage>

You can only mix colors that are next to each other in the list, with purple obviously wrapping around to be next to red. The odd exception is brown - it's in the "same spot" as orange, so both it and orange can mix with red and yellow, but not with each other. Using two base-colors evenly mixes them; using a splash color mixes them unevenly, with the base color dominating. So, there's a smooth transition from red => orangish red => orange red / red orange => reddish orange => orange.

(a limited set of Markdown is supported)