An easy notation for grayscale colors

These days, there is a lengthy discussion in the CSS WG about how to name a function that produces shades of gray (from white to black) with varying degrees of transparency, and we need your feedback about which name is easier to use.

The current proposals are:

1. gray(lightness [, alpha])

In this proposal gray(0%) is black, gray(50%) is gray and gray(100%) is white. It also accepts numbers from 0-255 which correspond to rgb(x,x,x) values, so that gray(255) is white and gray(0) is black. It also accepts an optional second argument for alpha transparency, so that gray(0, .5) would be equivalent to rgba(0,0,0,.5).

This is the naming of the function in the current CSS Color Level 4 draft.

2. white(lightness [, alpha])

Its arguments work in the same way as gray(), but it’s consistent with the expectation that function names that accept percentages give the “full effect” at 100%. gray(100%) sounds like a shade of gray, when it’s actually white. white(100%) is white, which might be more consistent with author expectations. Of course, this also accepts alpha transparency, like all the proposals listed here.

3. black(lightness [, alpha])

black() would work in the opposite way: black(0%) would be white, black(100%) would be black and black(50%,.5) would be semi-transparent gray. The idea is that people are familiar thinking that way from grayscale printing.

4. rgb() with one argument and rgba() with two arguments

rgb(x) would be a shorthand to rgb(x, x, x) and rgba(x, y) would be a shorthand to rgba(x, x, x, y). So, rgb(0) would be black and rgb(100%) or rgb(255) would be white. The benefit is that authors are already accustomed to using rgb() for colors, and this would just be a shortcut. However, note how you will need to change the function name to get a semi-transparent version of the color. Also, if in the future one needs to change the color to not be a shade of gray, a function name change is not needed.

I’ve written some SCSS to emulate these functions so you can play with them in your stylesheets and figure out which one is more intuitive. Unfortunately rgb(x)/rgba(x,a) cannot be polyfilled in that way, as that would overwrite the native rgb()/rgba() functions. Which might be an argument against them, as being able to polyfill through a preprocessor is quite a benefit for a new color format IMO.

You can vote here, but that’s mainly for easy vote counting. It’s strongly encouraged that you also leave a comment justifying your opinion, either here or in the list.

Vote now!

Also tl;dr If you can’t be bothered to read the post and understand the proposals well, please, refrain from voting.

  • Tab Atkins Jr.

    Re: #4, rgba(x,y). The WG decided not to fold “optional alpha” into the rgb() function, so we wouldn’t do so here either.

    • Lea Verou

      Man, that sucks. Gonna update the post now.

      • Tab Atkins Jr.

        Agreed. But whatever, we won’t repeat the mistake for future colors – if we do end up doing hwb(), it’s definitly optional-alpha.

        • Lea Verou

          This also sort of invalidates the poll: People were voting for rgb() with 1-2 args by majority, now it’s all under “Other” as I changed the label :(

        • Emil Stenström

          Maybe we could do another poll?

    • Christoph Päper

      And that has been a mistake (like not using percentages for alpha values), which also applies to hsl()/hsla(). However, since digits are repeated in hash notation (#xyz = #xxyyzz), it makes sense to repeat component values if they’re of the same type, i.e. rgb(x) = rgb(x,x,x). For hsl(x) you need differentiated behavior: hsl(xdeg) = hsl(xdeg, 100%, 50%) maybe, since most CSS1 colors are fully saturated and lit halfway, whereas hsl(x%) = hsl(0deg, x%, x%), which are not grays. It would not be intuitive to have single-value HSLs mean grays, i.e. not hsl(x%) = hsl(0deg, 0%, x%).

  • Scott

    It would be great if there was a way to do this with shorthand notation.

    #90 could mean #909090, and #90bc could mean #909090bc (the new alpha channel syntax).

    Would that be too confusing with the current #123 syntax?

    • Lea Verou

      This has been discussed and rejected dozens of times in the group. The reason is that #xyz notation expands by digit (#xxyyzz), whereas the 2 digit one would expand by repetition (#xyxyxy), so it’s inconsistent.

      • Sebastian Zartner

        Would be inconsistent in the operation but though expected by the majority of authors, I’d guess, as an RGB color consists of three components. So an author expects to be able to set the color of each component individually. Expanding by digit – i.e. #xy to #xxxyyy – breaks that logic.

        Anyway, what about a one-digit shorthand notation like #9? Of course you can’t cover all colors, though at least a few common shades of gray.

      • Scott

        I feel like expanding ‘n’ to ‘nn’ as in the 3-digit syntax is more inconsistent with expectation.

        Honestly I’d prefer that the 3-digit syntax was deprecated and this 2 and 4 digit syntax was implemented. 99% of 3 digit usage I’ve seen is for grays anyway.

        • Tab Atkins Jr.

          Expanding per channel might not be as consistent with a totally naive expectation, but it’s got a long history, and it’s more useful (as it lets you provide values to each channel, while repetition spams the same color to all channels and only allows grays).

          It’s interesting that you claim the majority of 3-digit usage you see is for grays. While most of my grays are indeed written with 3-digit syntax, most of my 3-digit syntax usage is not gray. For example, my previous company’s primary website colors were #06c, #006, and #c06.

          Really, though, the 3-digit syntax is just a remnant of the “web-safe colors” that existed before monitors with decent color support became widespread. That set was even more limited – it was all the 3-digit colors that you could write with some combination of 0369cf.

  • adamLaughlin

    Why not just call it saturation? I’ll admit that I skimmed the article…

    • Tab Atkins Jr.

      Because all the grays are equal (zero) saturation, for one thing. ^_^

      But assuming you meant “lightness()”, yeah, that’s possible, but it doesn’t seem to describe a color as well as some of the other options. It sounds like you’re describing the lightness of something else, like it’s a filter function or something.

  • Lisa

    Why don’t we just use hsl(0,0,value) ?

    • Tab Atkins Jr.

      More verbose, and not as immediately obvious what you’re doing. Requiring grays, a very common set of colors, to either be written with repetition (rgb()) or with some dummy arguments (hsl()) is not very user-friendly.

      • Andrée Hansson

        The verbose-argument is true, but on the other hand we might see a plethora of unused CSS functions instead (increasing the verbosity of the CSS API’s). Do we see a real issue in real-life development situations where it’s troublesome to repeat hsl(0, 0, value) instead of gray(value)? It would be better to perhaps look at allowing developers to define functions, just like pre-processors, and the ones having problems with repeating hsl/rgba syntaxes can define their own gray() or white() or whathaveyou() functions.

  • fiinixdesign

    1. No. Inconsistent with author expectations. The idea that gray(100%) is actually white seems counter-intuitive.
    2. Makes the most sense from an additive color model point of view and this gets my 2nd place vote (for whatever that’s worth). :)
    3. No. I think we should refrain from a mindset uses the subtractive model of printing.
    4. No. This seems prone to human error, difficult to read (when looking for those errors).

    5. I want to suggest Luma, where luma(100%) is white, luma(0%) is black and luma(50%) would equal to the perceived brightness in between because I expect a gray that is the middle of white and black to be perceived as being in the middle, which using a non-weighted calculation doesn’t achieve.

    But maybe this brings up a whole new set of problems I haven’t considered.

    Love that you are bringing this up for discussion.

    • TheSisb

      I agree with your point that white/black/gray(%) isn’t at all very clear.

  • David Moore

    Why not grayscale(lightness [, alpha])?

    • JonRimmer

      +1, I don’t understand why the most intuitive name for this concept isn’t one of the options under consideration?

      • Robin Nixon

        Indeed: grayscale is the term designers are accustomed to using in graphic editors.

        • JonRimmer

          Ah, I guess it’s because a greyscale() function is already defined by Filter Effects:

        • David Moore

          That was my thought also, but I thought I’d ask just in case, especially as the proposed keywords also already exist in the CSS spec too.

  • Chris Milway

    Coming to this a little late I suprised no one has suggested the name that sprang immediately to mind: tint()

    Sure it’s a little print-oriented, which I realise is a little unfashionable, but it will be immediately understandable to anyone who has used commercial design software.

  • Mike Mai

    In scss, I do:

    /* Shades of Solid Gray */

    /* Transparent Gray/Black */

    Depending on if I want cool gray or warm gray, I’d create custom $white variable first.

  • growdigital

    Black, every time; I find it the most intuitive.

    > If you can’t be bothered to read the post and understand the proposals well, please, refrain from voting.

    Can we implement this for national elections as well? ;)

  • Tigt

    If we do end up with gray(), will grey() be an alias?

  • Klim Lee

    1. gray(), because, on the 0–100% scale, both white and black are gray extremes. Again, grayscale palette is called that for a reason.

  • Joel Shinness

    I vote for grayscale(value), grayscale(value, alpha), but I also think it would be cool to have hex values like #f and #fe that become grayscale values.

  • kotla

    Hi Lea Verou,, when i subscribed you on facebook, it’s telling that ” the link is broken ” , can you find this issue ?

  • Luuk Lamers

    How about luma, lightness, value, brightness, intensity or key? All taken from the print or photography worlds. Key would have my preference because of its understandable roots: CMYK.

    It would work the same as white(%) but use a name and result more in line with expectations; monochrome(% or floating point) or key(% or floating point)

  • markbrown4

    What I’ve settled on with sass is rgba(#000, .5), rgba(#fff, .5)

    • Luuk Lamers

      Sounds counter-intuitive; parsing a hex value into a decimal function with a floating point value…

      • markbrown4

        Yeah I know it looks odd – but being able to use hex and rgb interchangeably in sass is actually a great thing. Their both just different forms of the same thing. I’ve been using #000/#fff as black/white since the beginning of time. Much simpler for me than 255,255,255

  • J

    either white() or black()
    gray(100%) is illogical – you have no way of telling just by looking at the code whether it means black or white

  • Ony T

    It is interesting

    wordpress business themes

  • Chris Geirman

    Seems like I’ll mostly be standing alone, but I like the rgb()/rgba() shorthand notation. It feels like it would fall in alignment with other shorthand notations. Though, I could get behind grayscale(lightness [,alpha]) too if we’re going to declare a new function

  • Aaron Cicali

    Why the need for this function at all? Seems unnecessary. Black and white belong to a “special set of colors” as well, but we don’t use special functions to produce them.

    I vote that we remove this from the draft and continue to use rgb() and rgba() for grey, gray, and anything else between black and white with a saturation of zero.