Tag: color

A Quick Look at the First Public Working Draft for Color Adjust Module 1

We’ve been talking a lot about Dark Mode around here ever since Apple released it as a system setting in MacOS 10.14 and subsequently as part of Safari. It’s interesting because of both what it opens up as as far as design opportunities as well as tailoring user experience based on actual user preferences.

This week, we got an Editor’s Draft for the Color Adjust Module Level 1 specification and the First Public Working Draft of it. All of this is a work-in-progress, but the progression of it has been interesting to track. The spec introduces three new CSS properties that help inform how much control the user agent should have when determining the visual appearance of a rendered page based on user preferences.

color-scheme is the first property defined in the spec and perhaps the centerpiece of it. It accepts light and dark values which — as you may have guessed — correspond to Light Mode and Dark Mode preferences for operating systems that support them. And, for what it’s worth, we could be dealing with labels other than “Light” and “Dark” (e.g. “Day” and “Night”) but what we’re dealing with boils down to a light color scheme versus a dark one.

Source: developer.apple.com

This single property carries some important implications. For one, the idea is that it allows us to set styles based on a user’s system preferences which gives us fine-grained control over that experience.

Another possible implication is that declaring the property at all enables the user agent to take some responsibility for determining an element’s colors, where declaring light or dark informs the user agent that an element is “aware” of color schemes and should be styled according to a preference setting matching the value. On the other hand, we can give the browser full control to determine what color scheme to use based on the user’s system preferences by using the auto value. That tells the browser that an element is “unaware” of color schemes and that the browser can determine how to proceed using the user preferences and a systems’s default styling as a guide.

It’s worth noting at this point that we may also have a prefers-color-scheme media feature (currently in the Editor’s Draft for the Media Queries Level 5 specification) that also serves to let us detect a user’s preference and help gives us greater control of the user experience based on system preferences. Robin has a nice overview of it. The Color Adjust Module Level 1 Working Draft also makes mention of possibly using a color scheme value in a <meta> element to indicate color scheme support.

There’s more to the property, of course, including an only keyword, chaining values to indicate an order of preference, and even an open-ended custom ident keyword. So definitely dig in there because there’s a lot to take in.

Pretty interesting, right? Hopefully you’re starting to see how this draft could open up new possibilities and even impacts how we make design decisions. And that’s only the start because there are two more properties!

  • forced-color-adjust: This is used when we want to support color schemes but override the user agent’s default stylesheet with our own CSS. This includes a note about possibly merging this into color-adjust.
  • color-adjust: Unlike forcing CSS overrides onto the user agent, this property provides a hint to browsers that they can change color values based on the both the user’s preferences and other factors, such as screen quality, bandwidth, or whatever is “deem[ed] necessary and prudent for the output device.” Eric Bailey wrote up the possibilities this property could open up as far as use cases, enhanced accessibility, and general implementations.

The current draft is sure to expand but, hey, this is where we get to be aware of the awesome work that W3C authors are doing, gain context for the challenges they face, and even contribute to the work. (See Rachel Andrew’s advice on making contributions.)

The post A Quick Look at the First Public Working Draft for Color Adjust Module 1 appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , , ,

Color contrast accessibility tools

Accessibility is all the rage these days, specifically when it comes to color contrast. I’ve stumbled upon a couple of tools this week that I think are pretty nifty for helping make sure that all of the text on our websites is legible regardless of what background color they might have.

First up is the Accessible Color Generator which happens to be a wonderful tool for picking alternative colors. Let’s say you’re working on a brand with color X. You can generate a host of other complimentary colors like this:

Next up is Contrast, a rather great MacOS app that sits in the menu bar at all times and helps identify accessible color pairings based on WCAG Guidelines. This one is particularly useful if you happen to be a designer:

This reminds me of a wonderful post about how the Lyft design team re-approached the way they use color in their app. Kevyn Arnott explains:

Color, at least on the surface, appears almost naively simple, yet as it scales across larger products it becomes unbelievably complex. You have thousands of people building products all at once, and those products are all heavily reliant on color. This puts a lot of pressure on the color system to ensure that all the products are being created consistently, but very hard to implement since it’s all too easy to apply colors on a one-off basis.

The team then went ahead and built ColorBox.io which lets you systematically build out a ton of colors for your design systems work. It’s pretty nifty!

Plus the folks over at GOV.UK made their own color accessibility tool called Contrast Checker which (as you have guessed by the name) helps check the contrast between the background of an element and the page itself:

And, of course, there’s the trusty WebAIM contrast checker, which is a go-to for many developers out there.

So far, we’ve looked at tools that check contrast. But there is a class of tooling that can automate accessible contrasts during development. Josh Bader wrote up an approach that enforces high contrast by pairing CSS custom properties with the calc() function. Facundo Corradini did something similar that switches font color based on the background color behind it.

Oh! And we may have something to look forward to with the color-adjust property. It is proposed in the CSS Color Module Level 4 specification and could give browsers more control to adjust color values that are declared in the stylesheet. It’s not really geared towards color contrast, but there’s something interesting about handing off the responsibility of rendering color values to the browser based on certain conditions.

The post Color contrast accessibility tools appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Change Color of SVG on Hover

There are a lot of different ways to use SVG. Depending on which way, the tactic for recoloring that SVG in different states or conditions — :hover, :active, :focus, class name change, etc. — is different.

Let’s look at the ways.

Inline SVG

Inline SVG is my favorite way to use SVG anyway, in part because of how easy it is to access and style the SVG.

See the Pen
bJXNyy
by Chris Coyier (@chriscoyier)
on CodePen.

If you’re used to working with icon fonts, one thing you might enjoy about them is how easy it is to change the color. You’re largely limited to a single color with icon fonts in a way that SVG isn’t, but still, it is appealingly easy to change that single color with color. Using inline SVG allows you to set the fill, which cascades to all the elements within the SVG, or you can fill each element separately if needed.

SVG Symbol / Use

There is such thing as an SVG sprite, which is a group of SVGs turned into <symbol> elements such that any given icon can be referenced easily with a <use> element.

See the Pen
Use SVG Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

You can still set the fill color from outside CSS rather easily this way, but there are caveats.

  • The internal SVG elements (like the <path>) can have no fill themselves. This allows the fill set from the parent SVG to cascade into the Shadow DOM created by <use>. As soon as you have something like <path fill="blue" ... /> in the <symbol>, you’ve lost outside CSS control.
  • Likewise, the fill of individual elements cannot be controlled within the SVG like you could with inline SVG. This means you’re pretty firmly in single-color territory. That covers most use cases anyway, but still, a limitation nonetheless.

SVG background images

SVG can be set as a background image just like PNG, JPG, or whatever other graphics format. At this point, you’ve sort of given up on being able to change the fill. One possibility, which I’d argue isn’t a particularly good one, is to have two versions of every icon, in the respective colors, and swap between them:

See the Pen
Background SVG Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

I don’t blame you if you’d rather not swap sources, so another possibility is to get gnarly with filters.

See the Pen
Background SVG Hovers with Filters
by Chris Coyier (@chriscoyier)
on CodePen.

Trying to finagle the right filters to get the color right is tricky stuff. Fortunately, Barrett Sonntag made a tool to calculate the filters for you! Turning black to red ends up a whacky combination like this: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);.

SVG also has object, which is kinda neat in that it had a built-in fallback back in the day — although browser support is so good these days, I honestly have never used it. But if you’re using it, you would probably have to use this filter technique to swap color on hover.

See the Pen
Background SVG Object Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

Use a mask instead of a background image

This way, the SVG is still in charge of essentially drawing the shape, but the color comes from the background-color (or image! or gradient!) behind it rather than the SVG itself.

See the Pen
Background SVG Hovers with Mask
by Chris Coyier (@chriscoyier)
on CodePen.

SVG background images as data URLs

This doesn’t change that much from above, but it does open up one interesting possibility: Using a variable for the internal fills. Here that is with Sass keeping the URLs as variables:

See the Pen
Background SVG Hovers with Data URL variables
by Chris Coyier (@chriscoyier)
on CodePen.

You can also do this with native CSS custom properties!

The post Change Color of SVG on Hover appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Re: Pleasing Color Palettes

There are so many tools out there to help you pick colors. I totally get it! It’s hard! When colors are done well, it’s like magic. It adds a level of polish to a design that can really set it apart.

Let’s look at some, then talk about this idea some more.

Here’s one I just saw called Color Koala:

It spits out five colors at ya and you’re off to the races.

Hue will give you some too.

There’s a billion more, and they vary in approach and features, of course. Here’s a handful:

Then there are tools that focus on gradients, like UI Gradients, Web Gradients, and Shapy.

Oh! And a site that helps with text color while keeping accessibility in mind.

There are even native apps like Sip, ColorSnapper, and Frank DeLoupe that help you select colors and sometimes keep your palettes right within them.

Colors can be programatically generated.

There is no native JavaScript API for it, but it’s still basically a one-liner:

See the Pen Generate New Random Hex Color with JavaScript by Chris Coyier (@chriscoyier) on CodePen.

Pleasing colors can be as well.

Generating random colors won’t guarantee pleasing palettes, especially if a bunch of random colors are paired together. PleaseJS can help build color schemes that work together. You provide it a base color and other options (like what type of color scheme) and it spits out colors for you.

See the Pen Generate Pleasing Colors by Chris Coyier (@chriscoyier) on CodePen.

Similarly, randomColor.js

gen­er­ates at­trac­tive col­ors by de­fault. More specif­i­cally, ran­dom­Color pro­duces bright col­ors with a rea­son­ably high sat­u­ra­tion. This makes ran­dom­Color par­tic­u­larly use­ful for data vi­su­al­iza­tions and gen­er­a­tive art.

It doesn’t claim to make multiple colors part of a cohesive theme aside from passing in a base hue or luminosity.

See the Pen Generate Pleasing Colors by Chris Coyier (@chriscoyier) on CodePen.

But the thing about just being handed colors is…

…they don’t exactly tell you how to use them. Steve Schoger makes a point of this, rather hilariously in a blog post. This is a perfectly lovely color palette:

But if you just pick those colors and plop them onto a design, you could end up with something like this:

You might like that, but you’d be in the minority. It’s not a refined design that gets out of the way and would be nice to use every day. Color usage is a bit more complicated than plopping five nice colors into a design. It’s variations on those and using them in tasteful ways, like this:

Picking up Steve Schoger and Adam Wathan’s book surely has some advice for you there!

The post Re: Pleasing Color Palettes appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Converting Color Spaces in JavaScript

A challenge I faced in building an image “emojifier” was that I needed to change the color spaces of values obtained using getImageData() from RGB to HSL. I used arrays of emojis arranged by brightness and saturation, and they were HSL-based for the best matches of average pixel colors with the emojis.

In this article, we’ll study functions that will be useful for converting both opaque and alpha-enabled color values. Modern browsers currently support the color spaces RGB(A), hex, and HSL(A). The functions and notations for these are rgb(), rgba(), #rgb/#rrggbb, #rgba/#rrggbbaa, hsl(), and hsla(). Browsers have always supported built-in names like aliceblue as well.

Balls with color values being inserted into a machine and coming out as HSL

Along the way, we’ll encounter use of some color syntaxes provided by a new Level 4 of the CSS Colors Module. For example, we now have hex with alpha as we mentioned (#rgba/#rrggbbaa) and RGB and HSL syntaxes no longer require commas (values like rgb(255 0 0) and hsl(240 100% 50%) became legal!).

Browser support for CSS Colors Level 4 isn’t universal as of this writing, so don’t expect new color syntaxes to work in Microsoft browsers or Safari if trying them in CSS.

RGB to Hex

Converting RGB to hex is merely a change of radices. We convert the red, green, and blue values from decimal to hexadecimal using toString(16). After prepending 0s to single digits and under, we can concatenate them and # to a single return statement.

function RGBToHex(r,g,b) {   r = r.toString(16);   g = g.toString(16);   b = b.toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;    return "#" + r + g + b; }

RGB in String

Alternatively, we can use a single string argument with the red, green and blue separated by commas or spaces (e.g. "rgb(255,25,2)", "rgb(255 25 2)"). Substring to eliminate rgb(, split what’s left by the ), then split that result’s first item by whichever the separator (sep) is. r, g, and b shall become local variables now. Then we use + before the split strings to convert them back to numbers before obtaining the hex values.

function RGBToHex(rgb) {   // Choose correct separator   let sep = rgb.indexOf(",") > -1 ? "," : " ";   // Turn "rgb(r,g,b)" into [r,g,b]   rgb = rgb.substr(4).split(")")[0].split(sep);    let r = (+rgb[0]).toString(16),       g = (+rgb[1]).toString(16),       b = (+rgb[2]).toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;    return "#" + r + g + b; }

In addition, we can allow strings with channel values as percentages by adding the loop after redefining rgb. It’ll strip the %s and turn what’s left into values out of 255.

function RGBToHex(rgb) {   let sep = rgb.indexOf(",") > -1 ? "," : " ";   rgb = rgb.substr(4).split(")")[0].split(sep);    // Convert %s to 0–255   for (let R in rgb) {     let r = rgb[R];     if (r.indexOf("%") > -1)       rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255);       /* Example:       75% -> 191       75/100 = 0.75, * 255 = 191.25 -> 191       */   }    ... }

Now we can supply values like either of these:

  • rgb(255,25,2)
  • rgb(255 25 2)
  • rgb(50%,30%,10%)
  • rgb(50% 30% 10%)

RGBA to Hex (#rrggbbaa)

Converting RGBA to hex with the #rgba or #rrggbbaa notation follows virtually the same process as the opaque counterpart. Since the alpha (a) is normally a value between 0 and 1, we need to multiply it by 255, round the result, then convert it to hexadecimal.

function RGBAToHexA(r,g,b,a) {   r = r.toString(16);   g = g.toString(16);   b = b.toString(16);   a = Math.round(a * 255).toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;   if (a.length == 1)     a = "0" + a;    return "#" + r + g + b + a; }

To do this with one string (including with percentages), we can follow what we did earlier. Also note the extra step of splicing out a slash. Since CSS Colors Level 4 supports the syntax of rgba(r g b / a), this is where we allow it. Alpha values can now be percentages! This removes the 0-1-only shackles we used to have. Therefore, the for loop cycling through rgba shall include a part to wipe the % from the alpha without multiplying by 255 (when R is 3 for alpha). Soon we can use values like rgba(255 128 0 / 0.8) and rgba(100% 21% 100% / 30%)!

function RGBAToHexA(rgba) {   let sep = rgba.indexOf(",") > -1 ? "," : " ";   rgba = rgba.substr(5).split(")")[0].split(sep);                    // Strip the slash if using space-separated syntax   if (rgba.indexOf("/") > -1)     rgba.splice(3,1);    for (let R in rgba) {     let r = rgba[R];     if (r.indexOf("%") > -1) {       let p = r.substr(0,r.length - 1) / 100;        if (R < 3) {         rgba[R] = Math.round(p * 255);       } else {         rgba[R] = p;       }     }   } }

Then, where the channels are converted to hex, we adjust a to use an item of rgba[].

function RGBAToHexA(rgba) {   ...        let r = (+rgba[0]).toString(16),       g = (+rgba[1]).toString(16),       b = (+rgba[2]).toString(16),       a = Math.round(+rgba[3] * 255).toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;   if (a.length == 1)     a = "0" + a;    return "#" + r + g + b + a; }

Now the function supports the following:

  • rgba(255,25,2,0.5)
  • rgba(255 25 2 / 0.5)
  • rgba(50%,30%,10%,0.5)
  • rgba(50%,30%,10%,50%)
  • rgba(50% 30% 10% / 0.5)
  • rgba(50% 30% 10% / 50%)

Hex to RGB

We know that the length of hex values must either be 3 or 6 (plus #). In either case, we begin each red (r), green (g), and blue (b) value with "0x" to convert them to hex. If we provide a 3-digit value, we concatenate the same value twice for each channel. If it’s a 6-digit value, we concatenate the first two for red, next two for green, and last two for blue. To get the values for the final rgb() string, we prepend the variables with + to convert them from strings back to numbers, which will yield the decimals we need.

function hexToRGB(h) {   let r = 0, g = 0, b = 0;    // 3 digits   if (h.length == 4) {     r = "0x" + h[1] + h[1];     g = "0x" + h[2] + h[2];     b = "0x" + h[3] + h[3];    // 6 digits   } else if (h.length == 7) {     r = "0x" + h[1] + h[2];     g = "0x" + h[3] + h[4];     b = "0x" + h[5] + h[6];   }      return "rgb("+ +r + "," + +g + "," + +b + ")"; }

Output RGB with %s

If we want to return rgb() using percentages, then we can modify the function to utilize an optional isPct parameter like so:

function hexToRGB(h,isPct) {   let r = 0, g = 0, b = 0;   isPct = isPct === true;    if (h.length == 4) {     r = "0x" + h[1] + h[1];     g = "0x" + h[2] + h[2];     b = "0x" + h[3] + h[3];        } else if (h.length == 7) {     r = "0x" + h[1] + h[2];     g = "0x" + h[3] + h[4];     b = "0x" + h[5] + h[6];   }        if (isPct) {     r = +(r / 255 * 100).toFixed(1);     g = +(g / 255 * 100).toFixed(1);     b = +(b / 255 * 100).toFixed(1);   }      return "rgb(" + (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")"; }

Under the last if statement, using +s will convert r, g, and b to numbers. Each toFixed(1) along with them will round the result to the nearest tenth. Additionally, we won’t have whole numbers with .0 or the decades old bug that produces numbers like 0.30000000000000004. Therefore, in the return, we omitted the +s right before the first r, g, and b to prevent NaNs caused by the %s. Now we can use hexToRGB("#ff0",true) to get rgb(100%,100%,0%)!

Hex (#rrggbbaa) to RGBA

The procedure for hex values with alpha should again be similar with the last. We simply detect a 4- or 8-digit value (plus #) then convert the alpha and divide it by 255. To get more precise output but not long decimal numbers for alpha, we can use toFixed(3).

function hexAToRGBA(h) {   let r = 0, g = 0, b = 0, a = 1;    if (h.length == 5) {     r = "0x" + h[1] + h[1];     g = "0x" + h[2] + h[2];     b = "0x" + h[3] + h[3];     a = "0x" + h[4] + h[4];    } else if (h.length == 9) {     r = "0x" + h[1] + h[2];     g = "0x" + h[3] + h[4];     b = "0x" + h[5] + h[6];     a = "0x" + h[7] + h[8];   }   a = +(a / 255).toFixed(3);    return "rgba(" + +r + "," + +g + "," + +b + "," + a + ")"; }

Output RGBA with %s

For a version that outputs percentages, we can do what we did in hexToRGB()—switch r, g, and b to 0–100% when isPct is true.

function hexAToRGBA(h,isPct) {   let r = 0, g = 0, b = 0, a = 1;   isPct = isPct === true;        // Handling of digits   ...    if (isPct) {     r = +(r / 255 * 100).toFixed(1);     g = +(g / 255 * 100).toFixed(1);     b = +(b / 255 * 100).toFixed(1);   }   a = +(a / 255).toFixed(3);    return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a : +r + "," + +g + "," + +b + "," + a) + ")"; }

Here’s a quick fix if the alpha ought to be a percentage, too: move the statement where a is redefined above the last if statement. Then in that statement, modify a to be like r, g, and b. When isPct is true, a must also gain the %.

function hexAToRGBA(h,isPct) {   ...        a = +(a / 255).toFixed(3);   if (isPct) {     r = +(r / 255 * 100).toFixed(1);     g = +(g / 255 * 100).toFixed(1);     b = +(b / 255 * 100).toFixed(1);     a = +(a * 100).toFixed(1);   }    return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + "," + +g + "," + +b + "," + a) + ")"; }

When we enter #7f7fff80 now, we should get rgba(127,127,255,0.502) or rgba(49.8%,49.8%,100%,50.2%).

RGB to HSL

Obtaining HSL values from RGB or hex is a bit more challenging because there’s a larger formula involved. First, we must divide the red, green, and blue by 255 to use values between 0 and 1. Then we find the minimum and maximum of those values (cmin and cmax) as well as the difference between them (delta). We need that result as part of calculating the hue and saturation. Right after the delta, let’s initialize the hue (h), saturation (s), and lightness (l).

function RGBToHSL(r,g,b) {   // Make r, g, and b fractions of 1   r /= 255;   g /= 255;   b /= 255;    // Find greatest and smallest channel values   let cmin = Math.min(r,g,b),       cmax = Math.max(r,g,b),       delta = cmax - cmin,       h = 0,       s = 0,       l = 0; }

Next, we need to calculate the hue, which is to be determined by the greatest channel value in cmax (or if all channels are the same). If there is no difference between the channels, the hue will be 0. If cmax is the red, then the formula will be ((g - b) / delta) % 6. If green, then (b - r) / delta + 2. Then, if blue, (r - g) / delta + 4. Finally, multiply the result by 60 (to get the degree value) and round it. Since hues shouldn’t be negative, we add 360 to it, if needed.

function RGBToHSL(r,g,b) {   ...   // Calculate hue   // No difference   if (delta == 0)     h = 0;   // Red is max   else if (cmax == r)     h = ((g - b) / delta) % 6;   // Green is max   else if (cmax == g)     h = (b - r) / delta + 2;   // Blue is max   else     h = (r - g) / delta + 4;    h = Math.round(h * 60);        // Make negative hues positive behind 360°   if (h < 0)       h += 360; }

All that’s left is the saturation and lightness. Let’s calculate the lightness before we do the saturation, as the saturation will depend on it. It’s the sum of the maximum and minimum channel values cut in half ((cmax + cmin) / 2). Then delta will determine what the saturation will be. If it’s 0 (no difference between cmax and cmin), then the saturation is automatically 0. Otherwise, it’ll be 1 minus the absolute value of twice the lightness minus 1 (1 - Math.abs(2 * l - 1)). Once we have these values, we must convert them to values out of 100%, so we multiply them by 100 and round to the nearest tenth. Now we can string together our hsl().

function RGBToHSL(r,g,b) {   ...   // Calculate lightness   l = (cmax + cmin) / 2;    // Calculate saturation   s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));        // Multiply l and s by 100   s = +(s * 100).toFixed(1);   l = +(l * 100).toFixed(1);    return "hsl(" + h + "," + s + "%," + l + "%)"; }

RGB in String

For one string, split the argument by comma or space, strip the %s, and localize r, g, and b like we did before.

function RGBToHSL(rgb) {   let sep = rgb.indexOf(",") > -1 ? "," : " ";   rgb = rgb.substr(4).split(")")[0].split(sep);    for (let R in rgb) {     let r = rgb[R];     if (r.indexOf("%") > -1)       rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255);   }    // Make r, g, and b fractions of 1   let r = rgb[0] / 255,       g = rgb[1] / 255,       b = rgb[2] / 255;    ... }

RGBA to HSLA

Compared to what we just did to convert RGB to HSL, the alpha counterpart will be basically nothing! We just reuse the code for RGB to HSL (the multi-argument version), leave a alone, and pass a to the returned HSLA. Keep in mind it should be between 0 and 1.

function RGBAToHSLA(r,g,b,a) {   // Code for RGBToHSL(r,g,b) before return   ...    return "hsla(" + h + "," + s + "%," +l + "%," + a + ")"; }

RGBA in String

For string values, we apply the splitting and stripping logic again but use the fourth item in rgba for a. Remember the new rgba(r g b / a) syntax? We’re employing the acceptance of it as we did for RGBAToHexA(). Then the rest of the code is the normal RGB-to-HSL conversion.

function RGBAToHSLA(rgba) {   let sep = rgba.indexOf(",") > -1 ? "," : " ";   rgba = rgba.substr(5).split(")")[0].split(sep);    // Strip the slash if using space-separated syntax   if (rgba.indexOf("/") > -1)     rgba.splice(3,1);    for (let R in rgba) {     let r = rgba[R];     if (r.indexOf("%") > -1) {       let p = r.substr(0,r.length - 1) / 100;        if (R < 3) {         rgba[R] = Math.round(p * 255);       } else {         rgba[R] = p;       }     }   }    // Make r, g, and b fractions of 1   let r = rgba[0] / 255,       g = rgba[1] / 255,       b = rgba[2] / 255,       a = rgba[3];    // Rest of RGB-to-HSL logic   ... }

Wish to leave the alpha as is? Remove the else statement from the for loop.

for (let R in rgba) {   let r = rgba[R];   if (r.indexOf("%") > -1) {     let p = r.substr(0,r.length - 1) / 100;      if (R < 3) {       rgba[R] = Math.round(p * 255);     }   } }

HSL to RGB

It takes slightly less logic to convert HSL back to RGB than the opposite way. Since we’ll use a range of 0–100 for the saturation and lightness, the first step is to divide them by 100 to values between 0 and 1. Next, we find chroma (c), which is color intensity, so that’s (1 - Math.abs(2 * l - 1)) * s. Then we use x for the second largest component (first being chroma), the amount to add to each channel to match the lightness (m), and initialize r, g, b.

function HSLToRGB(h,s,l) {   // Must be fractions of 1   s /= 100;   l /= 100;    let c = (1 - Math.abs(2 * l - 1)) * s,       x = c * (1 - Math.abs((h / 60) % 2 - 1)),       m = l - c/2,       r = 0,       g = 0,       b = 0; }

The hue will determine what the red, green, and blue should be depending on which 60° sector of the color wheel it lies.

Color wheel
The color wheel divided into 60° segments

Then c and x shall be assigned as shown below, leaving one channel at 0. To get the final RGB value, we add m to each channel, multiply it by 255, and round it.

function HSLToRGB(h,s,l) {   ...    if (0 <= h && h < 60) {     r = c; g = x; b = 0;   } else if (60 <= h && h < 120) {     r = x; g = c; b = 0;   } else if (120 <= h && h < 180) {     r = 0; g = c; b = x;   } else if (180 <= h && h < 240) {     r = 0; g = x; b = c;   } else if (240 <= h && h < 300) {     r = x; g = 0; b = c;   } else if (300 <= h && h < 360) {     r = c; g = 0; b = x;   }   r = Math.round((r + m) * 255);   g = Math.round((g + m) * 255);   b = Math.round((b + m) * 255);    return "rgb(" + r + "," + g + "," + b + ")"; }

HSL in String

For the single string version, we modify the first few statements basically the same way we did for RGBToHSL(r,g,b). Remove s /= 100; and l /= 100; and we’ll use the new statements to wipe the first 4 characters and the ) for our array of HSL values, then the %s from s and l before dividing them by 100.

function HSLToRGB(hsl) {   let sep = hsl.indexOf(",") > -1 ? "," : " ";   hsl = hsl.substr(4).split(")")[0].split(sep);    let h = hsl[0],       s = hsl[1].substr(0,hsl[1].length - 1) / 100,       l = hsl[2].substr(0,hsl[2].length - 1) / 100;    ... }

The next handful of statements shall handle hues provided with a unit—degrees, radians, or turns. We multiply radians by 180/π and turns by 360. If the result ends up over 360, we compound modulus divide to keep it within the scope. All of this will happen before we deal with c, x, and m.

function HSLToRGB(hsl) {   ...    // Strip label and convert to degrees (if necessary)   if (h.indexOf("deg") > -1)     h = h.substr(0,h.length - 3);   else if (h.indexOf("rad") > -1)     h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));   else if (h.indexOf("turn") > -1)     h = Math.round(h.substr(0,h.length - 4) * 360);   // Keep hue fraction of 360 if ending up over   if (h >= 360)     h %= 360;        // Conversion to RGB begins   ... }

After implementing the steps above, now the following can be safely used:

  • hsl(180 100% 50%)
  • hsl(180deg,100%,50%)
  • hsl(180deg 100% 50%)
  • hsl(3.14rad,100%,50%)
  • hsl(3.14rad 100% 50%)
  • hsl(0.5turn,100%,50%)
  • hsl(0.5turn 100% 50%)

Whew, that’s quite the flexibility!

Output RGB with %s

Similarly, we can modify this function to return percent values just like we did in hexToRGB().

function HSLToRGB(hsl,isPct) {   let sep = hsl.indexOf(",") > -1 ? "," : " ";   hsl = hsl.substr(4).split(")")[0].split(sep);   isPct = isPct === true;    ...    if (isPct) {     r = +(r / 255 * 100).toFixed(1);     g = +(g / 255 * 100).toFixed(1);     b = +(b / 255 * 100).toFixed(1);   }    return "rgb("+ (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")"; }

HSLA to RGBA

Once again, handling alphas will be a no-brainer. We can reapply the code for the original HSLToRGB(h,s,l) and add a to the return.

function HSLAToRGBA(h,s,l,a) {   // Code for HSLToRGB(h,s,l) before return   ...    return "rgba(" + r + "," + g + "," + b + "," + a + ")"; }

HSLA in String

Changing it to one argument, the way we’ll handle strings here will be not too much different than what we did earlier. A new HSLA syntax from Colors Level 4 uses (value value value / value) just like RGBA, so having the code to handle it, we’ll be able to plug in something like hsla(210 100% 50% / 0.5) here.

function HSLAToRGBA(hsla) {   let sep = hsla.indexOf(",") > -1 ? "," : " ";   hsla = hsla.substr(5).split(")")[0].split(sep);    if (hsla.indexOf("/") > -1)     hsla.splice(3,1);    let h = hsla[0],       s = hsla[1].substr(0,hsla[1].length - 1) / 100,       l = hsla[2].substr(0,hsla[2].length - 1) / 100,       a = hsla[3];            if (h.indexOf("deg") > -1)     h = h.substr(0,h.length - 3);   else if (h.indexOf("rad") > -1)     h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));   else if (h.indexOf("turn") > -1)     h = Math.round(h.substr(0,h.length - 4) * 360);   if (h >= 360)     h %= 360;    ... }

Furthermore, these other combinations have become possible:

  • hsla(180,100%,50%,50%)
  • hsla(180 100% 50% / 50%)
  • hsla(180deg,100%,50%,0.5)
  • hsla(3.14rad,100%,50%,0.5)
  • hsla(0.5turn 100% 50% / 50%)

RGBA with %s

Then we can replicate the same logic for outputting percentages, including alpha. If the alpha should be a percentage (searched in pctFound), here’s how we can handle it:

  1. If r, g, and b are to be converted to percentages, then a should be multiplied by 100, if not already a percentage. Otherwise, drop the %, and it’ll be added back in the return.
  2. If r, g, and b should be left alone, then remove the % from a and divide a by 100.
function HSLAToRGBA(hsla,isPct) {   // Code up to slash stripping   ...        isPct = isPct === true;        // h, s, l, a defined to rounding of r, g, b   ...        let pctFound = a.indexOf("%") > -1;        if (isPct) {     r = +(r / 255 * 100).toFixed(1);     g = +(g / 255 * 100).toFixed(1);     b = +(b / 255 * 100).toFixed(1);     if (!pctFound) {       a *= 100;     } else {       a = a.substr(0,a.length - 1);     }            } else if (pctFound) {     a = a.substr(0,a.length - 1) / 100;   }    return "rgba("+ (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + ","+ +g + "," + +b + "," + +a) + ")"; }

Hex to HSL

You might think this one and the next are crazier processes than the others, but they merely come in two parts with recycled logic. First, we convert the hex to RGB. That gives us the base 10s we need to convert to HSL.

function hexToHSL(H) {   // Convert hex to RGB first   let r = 0, g = 0, b = 0;   if (H.length == 4) {     r = "0x" + H[1] + H[1];     g = "0x" + H[2] + H[2];     b = "0x" + H[3] + H[3];   } else if (H.length == 7) {     r = "0x" + H[1] + H[2];     g = "0x" + H[3] + H[4];     b = "0x" + H[5] + H[6];   }   // Then to HSL   r /= 255;   g /= 255;   b /= 255;   let cmin = Math.min(r,g,b),       cmax = Math.max(r,g,b),       delta = cmax - cmin,       h = 0,       s = 0,       l = 0;    if (delta == 0)     h = 0;   else if (cmax == r)     h = ((g - b) / delta) % 6;   else if (cmax == g)     h = (b - r) / delta + 2;   else     h = (r - g) / delta + 4;    h = Math.round(h * 60);    if (h < 0)     h += 360;    l = (cmax + cmin) / 2;   s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));   s = +(s * 100).toFixed(1);   l = +(l * 100).toFixed(1);    return "hsl(" + h + "," + s + "%," + l + "%)"; }

Hex (#rrggbbaa) to HSLA

There aren’t too many lines that change in this one. We’ll repeat what we recently did to get the alpha by converting the hex, but won’t divide it by 255 right away. First, we must get the hue, saturation, and lightness as we did in the other to-HSL functions. Then, before the ending return, we divide the alpha and set the decimal places.

function hexAToHSLA(H) {   let r = 0, g = 0, b = 0, a = 1;    if (H.length == 5) {     r = "0x" + H[1] + H[1];     g = "0x" + H[2] + H[2];     b = "0x" + H[3] + H[3];     a = "0x" + H[4] + H[4];   } else if (H.length == 9) {     r = "0x" + H[1] + H[2];     g = "0x" + H[3] + H[4];     b = "0x" + H[5] + H[6];     a = "0x" + H[7] + H[8];   }    // Normal conversion to HSL   ...            a = (a / 255).toFixed(3);                    return "hsla("+ h + "," + s + "%," + l + "%," + a + ")"; }

HSL to Hex

This one starts as a conversion to RGB, but there’s an extra step to the Math.round()s of converting the RGB results to hex.

function HSLToHex(h,s,l) {   s /= 100;   l /= 100;    let c = (1 - Math.abs(2 * l - 1)) * s,       x = c * (1 - Math.abs((h / 60) % 2 - 1)),       m = l - c/2,       r = 0,       g = 0,       b = 0;    if (0 <= h && h < 60) {     r = c; g = x; b = 0;   } else if (60 <= h && h < 120) {     r = x; g = c; b = 0;   } else if (120 <= h && h < 180) {     r = 0; g = c; b = x;   } else if (180 <= h && h < 240) {     r = 0; g = x; b = c;   } else if (240 <= h && h < 300) {     r = x; g = 0; b = c;   } else if (300 <= h && h < 360) {     r = c; g = 0; b = x;   }   // Having obtained RGB, convert channels to hex   r = Math.round((r + m) * 255).toString(16);   g = Math.round((g + m) * 255).toString(16);   b = Math.round((b + m) * 255).toString(16);    // Prepend 0s, if necessary   if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;    return "#" + r + g + b; }

HSL in String

Even the first few lines of this function will be like those in HSLToRGB() if we changed it to accept a single string. This is how we’ve been obtaining the hue, saturation, and lightness separately in the first place. Let’s not forget the step to remove the hue label and convert to degrees, too. All of this will be in place of s /= 100; and l /= 100;.

function HSLToHex(hsl) {   let sep = hsl.indexOf(",") > -1 ? "," : " ";   hsl = hsl.substr(4).split(")")[0].split(sep);    let h = hsl[0],       s = hsl[1].substr(0,hsl[1].length - 1) / 100,       l = hsl[2].substr(0,hsl[2].length - 1) / 100;            // Strip label and convert to degrees (if necessary)   if (h.indexOf("deg") > -1)     h = h.substr(0,h.length - 3);   else if (h.indexOf("rad") > -1)     h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));   else if (h.indexOf("turn") > -1)     h = Math.round(h.substr(0,h.length - 4) * 360);   if (h >= 360)     h %= 360;    ... }

HSLA to Hex (#rrggbbaa)

Adding alpha to the mix, we convert a to hex and add a fourth if to prepend a 0, if necessary. You probably already familiar with this logic because we last used it in RGBAToHexA().

function HSLAToHexA(h,s,l,a) {   // Repeat code from HSLToHex(h,s,l) until 3 `toString(16)`s   ...    a = Math.round(a * 255).toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;   if (a.length == 1)     a = "0" + a;    return "#" + r + g + b + a; }

HSLA in String

Finally, the lines of the single argument version up to a = hsla[3] are no different than those of HSLAToRGBA().

function HSLAToHexA(hsla) {   let sep = hsla.indexOf(",") > -1 ? "," : " ";   hsla = hsla.substr(5).split(")")[0].split(sep);        // Strip the slash   if (hsla.indexOf("/") > -1)     hsla.splice(3,1);        let h = hsla[0],       s = hsla[1].substr(0,hsla[1].length - 1) / 100,       l = hsla[2].substr(0,hsla[2].length - 1) / 100,       a = hsla[3];                ... }

Built-in Names

To convert a named color to RGB, hex, or HSL, you might consider turning this table of 140+ names and hex values into a massive object at the start. The truth is that we really don’t need one because here’s what we can do:

  1. Create an element
  2. Give it a text color
  3. Obtain the value of that property
  4. Remove the element
  5. Return the stored color value, which will be in RGB by default

So, our function to get RGB will only be seven statements!

function nameToRGB(name) {   // Create fake div   let fakeDiv = document.createElement("div");   fakeDiv.style.color = name;   document.body.appendChild(fakeDiv);    // Get color of div   let cs = window.getComputedStyle(fakeDiv),       pv = cs.getPropertyValue("color");    // Remove div after obtaining desired color value   document.body.removeChild(fakeDiv);    return pv; }

Let’s go even further. How about we change the output to hex instead?

function nameToHex(name) {   // Get RGB from named color in temporary div   let fakeDiv = document.createElement("div");   fakeDiv.style.color = name;   document.body.appendChild(fakeDiv);    let cs = window.getComputedStyle(fakeDiv),       pv = cs.getPropertyValue("color");    document.body.removeChild(fakeDiv);    // Code ripped from RGBToHex() (except pv is substringed)   let rgb = pv.substr(4).split(")")[0].split(","),       r = (+rgb[0]).toString(16),       g = (+rgb[1]).toString(16),       b = (+rgb[2]).toString(16);    if (r.length == 1)     r = "0" + r;   if (g.length == 1)     g = "0" + g;   if (b.length == 1)     b = "0" + b;    return "#" + r + g + b; }

Or, why not HSL? 😉

function nameToHSL(name) {   let fakeDiv = document.createElement("div");   fakeDiv.style.color = name;   document.body.appendChild(fakeDiv);    let cs = window.getComputedStyle(fakeDiv),       pv = cs.getPropertyValue("color");    document.body.removeChild(fakeDiv);    // Code ripped from RGBToHSL() (except pv is substringed)   let rgb = pv.substr(4).split(")")[0].split(","),       r = rgb[0] / 255,       g = rgb[1] / 255,       b = rgb[2] / 255,       cmin = Math.min(r,g,b),       cmax = Math.max(r,g,b),       delta = cmax - cmin,       h = 0,       s = 0,       l = 0;    if (delta == 0)     h = 0;   else if (cmax == r)     h = ((g - b) / delta) % 6;   else if (cmax == g)     h = (b - r) / delta + 2;   else     h = (r - g) / delta + 4;    h = Math.round(h * 60);    if (h < 0)     h += 360;    l = (cmax + cmin) / 2;   s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));   s = +(s * 100).toFixed(1);   l = +(l * 100).toFixed(1);    return "hsl(" + h + "," + s + "%," + l + "%)"; }

In the long run, every conversion from a name becomes a conversion from RGB after cracking the name.

Validating Colors

In all these functions, there haven’t been any measures to prevent or correct ludicrous input (say hues over 360 or percentages over 100). If we’re only manipulating pixels on a <canvas> fetched using getImageData(), validation of color values isn’t necessary before converting because they’ll be correct no matter what. If we’re creating a color conversion tool where users supply the color, then validation would be much needed.

It’s easy to handle improper input for channels as separate arguments, like this for RGB:

// Correct red if (r > 255)   r = 255; else if (r < 0)   r = 0;

If validating a whole string, then a regular expression is needed. For instance, this is the RGBToHex() function given a validation step with an expression:

function RGBToHex(rgb) {   // Expression for rgb() syntaxes   let ex = /^rgb((((((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?)){2}|((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s)){2})((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]))|((((([1-9]?d(.d+)?)|100|(.d+))%,s?){2}|((([1-9]?d(.d+)?)|100|(.d+))%s){2})(([1-9]?d(.d+)?)|100|(.d+))%)))$ /i;    if (ex.test(rgb)) {     // Logic to convert RGB to hex     ...    } else {     // Something to do if color is invalid   } }

To test other types of values, below is a table of expressions to cover both opaque and alpha-enabled:

Color Value RegEx
RGB /^rgb((((((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?)){2}|((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s)){2})((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]))|((((([1-9]?d(.d+)?)|100|(.d+))%,s?){2}|((([1-9]?d(.d+)?)|100|(.d+))%s){2})(([1-9]?d(.d+)?)|100|(.d+))%)))$ /i
RGBA /^rgba((((((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?)){3})|(((([1-9]?d(.d+)?)|100|(.d+))%,s?){3}))|(((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s){3})|(((([1-9]?d(.d+)?)|100|(.d+))%s){3}))/s)((0?.d+)|[01]|(([1-9]?d(.d+)?)|100|(.d+))%))$ /i
Hex /^#([da-f]{3}){1,2}$ /i
Hex (with Alpha) /^#([da-f]{4}){1,2}$ /i
HSL /^hsl(((((([12]?[1-9]?d)|[12]0d|(3[0-5]d))(.d+)?)|(.d+))(deg)?|(0|0?.d+)turn|(([0-6](.d+)?)|(.d+))rad)((,s?(([1-9]?d(.d+)?)|100|(.d+))%){2}|(s(([1-9]?d(.d+)?)|100|(.d+))%){2}))$ /i
HSLA /^hsla(((((([12]?[1-9]?d)|[12]0d|(3[0-5]d))(.d+)?)|(.d+))(deg)?|(0|0?.d+)turn|(([0-6](.d+)?)|(.d+))rad)(((,s?(([1-9]?d(.d+)?)|100|(.d+))%){2},s?)|((s(([1-9]?d(.d+)?)|100|(.d+))%){2}s/s))((0?.d+)|[01]|(([1-9]?d(.d+)?)|100|(.d+))%))$ /i

Looking at the expressions for RGB(A) and HSL(A), you probably have big eyes right now; these were made comprehensive enough to include most of the new syntaxes from CSS Colors Level 4. Hex, on the other hand, doesn’t need expressions as long as the others because of only digit counts. In a moment, we’ll dissect these and decipher the parts. Note that case-insensitive values (/i) pass all these.

RGB

/^rgb((((((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?)){2}|((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s)){2})((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]))|((((([1-9]?d(.d+)?)|100|(.d+))%,s?){2}|((([1-9]?d(.d+)?)|100|(.d+))%s){2})(([1-9]?d(.d+)?)|100|(.d+))%)))$ /i

Because rgb() accepts either all integers or all percentages, both cases are covered. In the outmost group, between the ^rgb( and )$ , there are inner groups for both integers and percentages, all comma-spaces or spaces only as separators:

  1. (((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?){2}|(((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s){2})((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]))
  2. ((((([1-9]?d(.d+)?)|100|(.d+))%,s?){2}|((([1-9]?d(.d+)?)|100|(.d+))%s){2})(([1-9]?d(.d+)?)|100|(.d+))%)

In the first half, we accept two instances of integers for red and green from 0–99 or 111-199 ((1?[1-9]?d)), 100–109 (10d), 200-249 ((2[0-4]d)), or 250–255 (25[0-5]). We couldn’t simply do d{1,3} because values like 03 or 017 and those greater than 255 shouldn’t be allowed. After that goes the comma and optional space (,s?). On the other side of the |, after the first {2} (which indicates two instances of integers), we check for the same thing with space separators if the left side is false. Then for blue, the same should be accepted, but without a separator.

In the other half, acceptable values for percentages, including floats, should either be 0–99, explicitly 100 and not a float, or floats under 1 with the 0 dropped. Therefore, the segment here is (([1-9]?d(.d+)?)|100|(.d+)), and it appears three times; twice with separator (,s?){2}, %s){2}), once without.

It is legal to use percentages without space separators (rgb(100%50%10%) for instance) in CSS, but the functions we wrote don’t support that. The same goes for rgba(100%50%10%/50%), hsl(40 100%50%), and hsla(40 100%50%/0.5). This could very well be a plus for code golfing and minification!

RGBA

/^rgba((((((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?)){3})|(((([1-9]?d(.d+)?)|100|(.d+))%,s?){3}))|(((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5])s){3})|(((([1-9]?d(.d+)?)|100|(.d+))%s){3}))/s)((0?.d+)|[01]|(([1-9]?d(.d+)?)|100|(.d+))%))$ /i

The next expression is very similar to the pervious, but three instances of integers (((((1?[1-9]?d)|10d|(2[0-4]d)|25[0-5]),s?){3})) or percentages ((((([1-9]?d(.d+)?)|100|(.d+))%,s?){3})), plus comma optional space are checked. Otherwise, it looks for the same thing but with space separators, plus a slash and space (/s) after the blue. Next to that is ((0?.d+)|[01]|(([1-9]?d(.d+)?)|100|(.d+))%) where we accept floats with or without the first 0 ((0?.d+)), 0 or 1 ([01]) on the dot, or 0–100% ((([1-9]?d(.d+)?)|100|(.d+))%).

Hex with Alpha

// #rgb/#rrggbb /^#([da-f]{3}){1,2}$ /i // #rgba/#rrggbbaa /^#([da-f]{4}){1,2}$ /i

For both hex—with and without alpha—instances of numbers or letters a–f ([da-f]) are accepted. Then one or two instances of this are counted for either short or longhand values supplied (#rgb or #rrggbb). As an illustration, we have this same short pattern: /^#([da-f]{n}){1,2}$ /i. Simply change n to 3 or 4.

HSL and HSLA

// HSL /^hsl(((((([12]?[1-9]?d)|[12]0d|(3[0-5]d))(.d+)?)|(.d+))(deg)?|(0|0?.d+)turn|(([0-6.d+)?)|(.d+))rad)((,s?(([1-9]?d(.d+)?)|100|(.d+))%){2}|(s(([1-9]?d(.d+)?)|100|(.d+))%){2}))$ /i // HSLA /^hsla(((((([12]?[1-9]?d)|[12]0d|(3[0-5]d))(.d+)?)|(.d+))(deg)?|(0|0?.d+)turn|(([0-6.d+)?)|(.d+))rad)(((,s?(([1-9]?d(.d+)?)|100|(.d+))%){2},s?)|((s(([1-9]?d(.d+)?)|100|(.d+))%){2}s/s))((0?.d+)|[01]|(([1-9]?d(.d+)?)|100|(.d+))%))$ /i

After the ( in both expressions for HSL and HSLA, this large chunk is for the hue:

((((([12]?[1-9]?d)|[12]0d|(3[0-5]d))(.d+)?)|(.d+))(deg)?|(0|0?.d+)turn|(([0-6.d+)?)|(.d+))rad)

([12]?[1-9]?d) covers 0–99, 110–199, and 210–299. [12]0d covers 110–109 and 200–209. Then (3[0-5]d) takes care of 300–359. The reason for this division of ranges is similar to that of integers in the rgb() syntax: ruling out zeros coming first and values greater than the maximum. Since hues can be floating point numbers, the first (.d+)? is for that.

Next to the | after the aforementioned segment of code, the second (.d+) is for floats without a leading zero.

Now let’s move up a level and decipher the next small chunk:

(deg)?|(0|0?.d+)turn|(([0-6.d+)?)|(.d+))rad

This contains the labels we can use for the hue—degrees, turns, or radians. We can include all or none of deg. Values in turn must be under 1. For radians, we can accept any float between 0–7. We do know, however, that one 360° turn is 2π, and it stops approximately at 6.28. You may think 6.3 and over shouldn’t be accepted. Because 2π is an irrational number, it would be too messy for this example to try to satisfy every decimal place provided by the JavaScript console. Besides, we have this snippet in our HSLTo_() functions as a second layer of security if hues 360° or over were to happen:

// Keep hue fraction of 360 if ending up over if (h >= 360)   h %= 360;

Now let’s move up a level and decipher the second chunk:

(,s?(([1-9]?d(.d+)?)|100|(.d+))%){2}

We’re counting two instances of comma-space-percentages for the saturation and lightness (space optional). In the group after the ,s?, we test for values 0–99 with or without decimal points (([1-9]?d(.d+)?)), exactly 100, or floats under 1 without the leading 0 ((.d+)).

The last part the HSL expression, before the ending ()$ /i), is a similar expression if spaces are the only separator:

(s(([1-9]?d(.d+)?)|100|(.d+))%){2}

s is in the beginning instead of ,s?. Then in the HSLA expression, this same chunk is inside another group with ,s? after its {2}.

((,s?(([1-9]?d(.d+)?)|100|(.d+))%){2},s?)

That counts the comma-space between the lightness and alpha. Then if we have spaces as separators, we need to check for a space-slash-space (s/s) after counting two instances of space and a percentage.

((s(([1-9]?d(.d+)?)|100|(.d+))%){2}s/s))

After that, we have this left to check the alpha value:

(((0?.d+)|[01])|(([1-9]?d(.d+)?)|100|(.d+))%)

Matches for (0?.d+) include floats under 1 with or without the leading 0, 0 or 1 for [01], and 0–100%.

Conclusion

If your current challenge is to convert one color space to another, you now have some ideas on how to approach it. Because it would be tiresome to walk through converting every color space ever invented in one post, we discussed the most practical and browser-supported ones. If you’d like to go beyond supported color spaces (say CMYK, XYZ, or CIE L*a*b*), EasyRGB) provides an amazing set of code-ready formulas.

To see all the conversions demonstrated here, I’ve set up a CodePen demo that shows inputs and outputs in a table. You can try different colors in lines 2–10 and see the complete functions in the JavaScript panel.

See the Pen Color Conversion by Jon Kantner (@jkantner) on CodePen.

The post Converting Color Spaces in JavaScript appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

What do you name color variables?

I remember the very first time I tried Sass on a project. The first thing I wanted to do was variablize my colors. From my naming-things-in-HTML skillz, I knew to avoid classes like .header-blue-left-bottom because the color and position of that element might change. It’s better for the to reflect it what it is than what it looks like.

So, I tried to make my colors semantic, in a sense — what they represent not what they literally are:

$ mainBrandColor: #F060D6; $ secondaryFocus: #4C9FEB; $ fadedHighlight: #F1F3F4;

But I found that I absolutely never remembered them and had to constantly refer to where I defined them in order to use them. Later, in a “screw it” moment, I named colors more like…

$ orange: #F060D6; $ red: #BB532E; $ blue: #4C9FEB;  $ gray-1: #eee; $ gray-2: #ccc; $ gray-3: #555;

I found that to be much more intuitive with little if any negative side effects. After all, this isn’t crossing the HTML-CSS boundary here; this is all within CSS and developer-only-facing, which puts more of a narrow scope on the problem.

In a similar fashion, I’ve tried keeping colors within a Sass map, like:

$ colors: (   light: #ccc,   dark: #333 );

But the only vague goal there was to clean up the global namespace, which probably isn’t worth the hassle of needing to map-get all the time. Namespacing like $ -c-orange is probably an easier approach if you need to do anything at all.

I’ve largely stuck with that just-use-color-names approach today in Sass. As the shift toward CSS custom properties happens, I think having a --c-orange and --c-gray-5 is similarly appropriate.

:root {   -c-orange: #F060D6;   -c-red: #BB532E;   -c-blue: #4C9FEB;    -c-gray-1: #eee;   -c-gray-2: #ccc;   -c-gray-3: #555; }

You could get a little more specific with those names with staying abstract, like Marcus Ortense says:

$ color-primary: $ color-primary-dark: $ color-primary-light: 

And variations on each base like Mike Street says:

$ primary: $ primaryLight:  $ primaryDark:  $ secondary: $ secondaryLight: $ secondaryDark:  $ neutralDarker: $ neutrayDark: $ neutral: $ neutralLight: $ neutralLighter:  $ neutralLightest: 

Silvestar Bistrović recently wrote about using abstract Greek numbering:

$ color-alpha: #12e09f; $ color-beta: #e01258; $ color-gamma: #f5f5f5; $ color-psi: #1f1f1f; $ color-omega: #fff;

I’ve used that kind of thing for media query breakpoints before, as the numbering seems to make sense there (i.e. low numbers are smaller screens, big numbers are bigger screens). I could also see that being nice for tints or shades of the same color, but then why not regular numbers?

Another approach I’ve often seen is to combine named colors with abstracted names. Geoff does that and John Carroll lists that here:

$ color-danube: #668DD1; $ color-cornflower: $ 6195ED; $ color-east-bay: $ 3A4D6E;  // theme1.scss $ color-alpha: $ color-danube; $ color-bravo: $ color-cornflower; $ color-charlie: $ color-east-bay;  // theme2.scss $ color-alpha: $ color-cornflower; $ color-bravo: $ color-danube; $ color-charlie: $ color-east-bay;

That can get as verbose as you need it to, even adding variations as you call from the palette.

$ table-row-background: lighten($ color-background, 10%);

Stuart Robson even gets a bit BEM-y with the names, including the namespace:

$ ns-color__blue—dark: rgb(25,25,112);  $ ns-brand__color—primary: $ ns-color__blue—dark;  // component.scss $ ns-component__color—text: $ ns-brand__color—primary;

Material Design uses values that are similar to font-weight! That means you’d end up with something like a base range plus alternates:

$ light-green-100: $ light-green-200: $ light-green-300: // etc $ light-green-900: $ light-green-A200: $ light-green-A400:  $ deep-purple-100: $ deep-purple-200: $ deep-purple-300: // etc $ deep-purple-A900:

How might you pick names for colors? You might get a kick out of what to call a sunny yellow versus a sunflower yellow, or you might just want some help. Here’s one project for that, and here’s another:

See the Pen Color Namer by Maneesh (@maneeshc) on CodePen.

There is even a Sublime Text plugin for converting them (to whatever syntax you want):

And since we’re on the topic of naming:

The post What do you name color variables? appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]