A simple Color-Picker using CSS5 color-contrast() and color-mix()
Tags: css, html, codepen, webdev
Published: 2021-08-23 15:46:30

Safari is often blamed for being “the new IE of web-browsers”. That's not fair, because Safari has recently been a “first-mover” in a lot of areas. For instance, *Safari Technology Preview* has implemented a lot of the stuff from the [CSS Color Module Level 5](https://www.w3.org/TR/css-color-5) specification – `color-contrast()` and `color-mix()` among them.

Let's build a simple *Color Picker* using these cool new features! We'll add some JavaScript, using `CSS.supports`, to make it work in other browsers as well.

The markup is a simple `fieldset` with radio-buttons:

```html
<fieldset class="color-wrapper">
 <!-- start iterate colors -->
  <label class="color" style="--bgc:hsl(168, 41%, 65%)">
    <input type="radio" name="cp" value="hsl(168, 41%, 65%)"><i></i>
  </label>
<!-- end iterate colors -->
</fieldset>
```

We'll set a custom property, `--bgc`, for each color, and with a dash of CSS, this it how it renders in Chrome:

![Chrome Basic](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5nhjnvntv0l34qluacfl.png) 

---

## Using a `mask()` for selected color

We'll add a mask with a checkmark-icon to the `<i></i>`-element:

```css
.color i {
  aspect-ratio: 1;
  background-color: transparent;
  display: block;
  mask: no-repeat center center/var(--ico-w) var(--ico);
  -webkit-mask: no-repeat center center/var(--ico-w) var(--ico);
  width: 100%;
}
```

In Chrome, it now looks like this:
![Chrome checked](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cy46gylh4s3cs4vairsy.png)
 
Better – but the icon will have the **same color**, even if the `background-color` is dark, like the example above. It's also a bit annoying, that the `border-color` is the same for all colors.

---
## color-mix()

With the `color-mix`-function, we can add a colored border, based on the custom property, `--bgc`, mixing in 10% of black:

```css
.color {
  border: var(--bdw, 0.125rem) solid color-mix(in hsl, #000 10%, var(--bgc));
}
```

---
## color-contrast()

With the `color-contrast`-function, we can change the color of the icon, based on the custom property, `--bgc`:

```css
.color input:checked + i {
  background-color: color-contrast(var(--bgc) vs white, black);
}
```

In Safari, it now looks like this:

![Safari](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jp82gi6zu4200q3qwfl8.png)

Cool! See those beautiful, dynamic border-colors! The checkmark-icon is white on dark colors, and black on light colors. 

And absolutely **no JavaScript** is required!

---
## Fixing issues in Chrome & Co.
In non-Safari browsers, we'll have to use some JavaScript in order to achieve the same:

```js
if (CSS.supports('not (color: color-contrast(red vs black, white))')) {
/* code here */
}
```

We'll add a method that'll iterate the labels of the `fieldset`, and set a custom property, `--ico-c`, to either *black* or *white*, depending on the brightness of the iterated color:

```js
function setLuminance(elements) {
  elements.forEach(element => {
    const rgb = window.getComputedStyle(element).getPropertyValue('background-color'); 
    if (rgb) {
      const [r,g,b] = rgb.replace(/[^\d,]/g, '').split(',');
      const brightness = (299 * r + 587 * g + 114 * b) / 1000;
      element.style.setProperty('--ico-c', brightness <= 127 ? '#FFF' : '#111')
    }
  })
}
```

The snippet will return the color of the `label` as `rgb`, no matter if it's hex, hsl or rgb to start with, then check it's brightness, and set the `--ico-c`-property.

In Chrome, it now looks like this:
![Chrome final](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpim57uausfaomg5ag0x.png)

Much better! The border-colors are still a bit dull in “non-Safari”-browsers ... In the `setLuminance`-method, we can fix that, by deducting `20` from each chanel (r, g and b):

```js
element.style.setProperty('--bdc', `rgb(${r-20},${g-20},${b-20})`);
```

![chromecolors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nslvm9duqize23wk365n.png)

---

## Enabling `color-contrast` and `color-mix`

In *Safari Technology Preview*, go to `Develop > Experimental Features` and enable them:

![Safari Tech Preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emw5rvm8t3a42dh1z9by.png)

---
## Demo
{% codepen https://codepen.io/stoumann/pen/OJgJXZP %}

---
## Closing thoughts
I tried to use `color-mix` **within** `color-contrast` – but that didn't work!

I guess the implementation is not completely done, and thus a little buggy – but I'm looking forward to being able to mix in 80% white or black with the background-color, so the icon-color blends in more “softly” with the background-color.