Programming CSS to perform Sass colour functions
Tags: css, colours, styling, sass
Published: 2020-05-30 18:49:14

---
title: Programming CSS to perform Sass colour functions
published: true
date: 2020-05-30 10:40:05 UTC
tags: css,colours,styling,sass
canonical_url: https://adactio.com/journal/16960
---

I wrote recently about [moving away from Sass to using native CSS features](https://adactio.com/journal/16887). I had this to say on the topic of mixins in Sass:

> These can be very useful, but now there’s a lot that you can do just in CSS with `calc()`. The built-in `darken()` and `lighten()` mixins are handy though when it comes to colours.

I know [we will be getting these in the future](https://drafts.csswg.org/css-color-5/#colormodify) but we’re not there yet with CSS.

Anyway, I had all this in the back of my mind when I was reading Lea’s excellent feature in this month’s Increment: [<cite>A user’s guide to CSS variables</cite>](https://increment.com/frontend/a-users-guide-to-css-variables/). She’s written about a really clever technique of combining custom properites with `hsl()` colour values for [creating colour palettes](https://increment.com/frontend/a-users-guide-to-css-variables/#creating-color-palettes). (See also: Una’s post on [dynamic colour theming with pure CSS](https://una.im/css-color-theming/).)

As so often happens when I’m reading something written by Lea—or seeing her give a talk—light bulbs started popping over my head (my usual response to Lea’s knowledge bombs is either “I didn’t know you could do that!” or “I never thought of doing that!”).

I immediately set about implementing this technique over on [The Session](https://thesession.org). The trick here is to use separate custom properties for the hue, saturation, and lightness parts of `hsl()` colour values. Then, when you want to lighten or darken the colour—say, on hover—you can update the lightness part.

[I’ve made a Codepen to show what I’m doing](https://codepen.io/adactio/pen/xxwovRZ).

Let’s say I’m styling a `button` element. I make custom propertes for `hsl()` values:

```
button {
  --button-colour-hue: 19;
  --button-colour-saturation: 82%;
  --button-colour-lightness: 38%;
  background-color: hsl(
    var(--button-colour-hue),
    var(--button-colour-saturation),
    var(--button-colour-lightness)
  );
}

```

For my buttons, I want the borders to be slightly darker than the background colour. When I was using Sass, I used the `darken()` function to this. Now I use `calc()`. Here’s how I make the borders 10% darker:

```
border-color: hsl(
  var(--button-colour-hue),
  var(--button-colour-saturation),
  calc(var(--button-colour-lightness) - 10%)
);

```

That `calc()` function is substracting a percentage from a percentage: 38% minus 10% in this case. The borders will have a lightness of 28%.

I make the bottom border even darker and the top border lighter to give a feeling of depth.

<button style="box-sizing: border-box;white-space: nowrap;cursor: pointer;display: inline-block;font-size: 1rem;font-variant: small-caps;text-align: center;text-transform: lowercase;text-indent: 0.125em;text-shadow: 0 -0.0625em 0 rgba(0,0,0,0.25);letter-spacing: 0.125em;line-height: 1;border-width: 0.0625em;border-style: solid;border-radius: 0.25em;background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.2));padding: 0.375em 1em 0.5em;margin: 0;box-shadow: inset 0 0.0625em 0.0625em rgba(255,255,255,0.25);color: rgba(255,255,204,0.9);transition: border-color 0.1s, background-color 0.1s;background-color: rgba(170,68,17,1.0);border-color: rgba(100,40,10,1.0);border-bottom-color: rgba(77,31,8,1.0);border-top-color: rgba(170,68,17,1.0);--button-colour-hue: 19;--button-colour-saturation: 82%;--button-colour-lightness: 38%;--button-colour-opacity: 100%;background-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));border-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 10%), var(--button-colour-opacity));border-bottom-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 20%), var(--button-colour-opacity));border-top-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));">button</button>

On [The Session](https://thesession.org) there’s a “cancel” button style that’s deep red.

<button style="box-sizing: border-box;white-space: nowrap;cursor: pointer;display: inline-block;font-size: 1rem;font-variant: small-caps;text-align: center;text-transform: lowercase;text-indent: 0.125em;text-shadow: 0 -0.0625em 0 rgba(0,0,0,0.25);letter-spacing: 0.125em;line-height: 1;border-width: 0.0625em;border-style: solid;border-radius: 0.25em;background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.2));padding: 0.375em 1em 0.5em;margin: 0;box-shadow: inset 0 0.0625em 0.0625em rgba(255,255,255,0.25);color: rgba(255,255,204,0.9);transition: border-color 0.1s, background-color 0.1s;background-color: rgba(170,68,17,1.0);border-color: rgba(100,40,10,1.0);border-bottom-color: rgba(77,31,8,1.0);border-top-color: rgba(170,68,17,1.0);--button-colour-hue: 0;--button-colour-saturation: 100%;--button-colour-lightness: 40%;--button-colour-opacity: 100%;background-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));border-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 10%), var(--button-colour-opacity));border-bottom-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 20%), var(--button-colour-opacity));border-top-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));">cancel</button>

Here’s how I set its colour:

```
.cancel {
  --button-colour-hue: 0;
  --button-colour-saturation: 100%;
  --button-colour-lightness: 40%;
}

```

That’s it. The existing button declarations take care of assigning the right shades for the border colours.

Here’s another example. Site admins see buttons for some actions only available to them. I want those buttons to have their own colour:

```
.admin {
  --button-colour-hue: 45;
  --button-colour-saturation: 100%;
  --button-colour-lightness: 40%;
}

```

<button style="box-sizing: border-box;white-space: nowrap;cursor: pointer;display: inline-block;font-size: 1rem;font-variant: small-caps;text-align: center;text-transform: lowercase;text-indent: 0.125em;text-shadow: 0 -0.0625em 0 rgba(0,0,0,0.25);letter-spacing: 0.125em;line-height: 1;border-width: 0.0625em;border-style: solid;border-radius: 0.25em;background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.2));padding: 0.375em 1em 0.5em;margin: 0;box-shadow: inset 0 0.0625em 0.0625em rgba(255,255,255,0.25);color: rgba(255,255,204,0.9);transition: border-color 0.1s, background-color 0.1s;background-color: rgba(170,68,17,1.0);border-color: rgba(100,40,10,1.0);border-bottom-color: rgba(77,31,8,1.0);border-top-color: rgba(170,68,17,1.0);--button-colour-hue: 45;--button-colour-saturation: 100%;--button-colour-lightness: 40%;--button-colour-opacity: 100%;background-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));border-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 10%), var(--button-colour-opacity));border-bottom-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), calc(var(--button-colour-lightness) - 20%), var(--button-colour-opacity));border-top-color: hsla( var(--button-colour-hue), var(--button-colour-saturation), var(--button-colour-lightness), var(--button-colour-opacity));">admin</button>

You get the idea. It doesn’t matter how many differently-coloured buttons I create, the effect of darkening or lightening their borders is all taken care of.

So it turns out that the `lighten()` and `darken()` functions from Sass _are_ available to us in CSS by using a combination of custom properties, `hsl()`, and `calc()`.

I’m also using this combination to lighten or darken background and border colours on `:hover`. You can [poke around the Codepen](https://codepen.io/adactio/pen/xxwovRZ) if you want to see that in action.

I love seeing the combinatorial power of these different bits of CSS coming together. It really is a remarkably powerful programming language.