Programmatically adjusting color variables with SASS

(Kris) #1

So right now the simple themer on the customize page is a bit off for dark themes. Some things don’t look right because in the default theme there are colors that are lightened via SASS functions… and in the dark theme they should be darkened.

I’m trying to figure out a way to make all colors selected in the themer scale appropriately based on their context. I think I’m getting close to a solution using SASS functions and I’m just cataloguing my thoughts here.

Brightness & Contrast

So the first step is that the contrast relationship between two colors needs to be calculated, and then if that difference is too low… we need to adjust the brightness of a color (without changing the hue).

I was using SASS’ lightness function to do this before, but lightness comparison isn’t always the best. So I found this little W3C snippet on contrast here

Color brightness is determined by the following formula:
((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
Note: This algorithm is taken from a formula for converting RGB values to YIQ values. This brightness value gives a perceived brightness for a color.

Now using this I can easily find the brighness of a color on a more reliable scale than lightness

``````@function brightness(\$color) {
@return ((red(\$color) * .299) + (green(\$color) * .587) + (blue(\$color) * .114));
}
``````

black = 0, white = 255; great.

Now I can set a desired contrast value between colors (100 or greater in this example), and find out if two colors are within the a usable contrast range of each other.

``````@function contrast(\$color1, \$color2) {
@if (abs(brightness(\$color1) - brightness(\$color2)) < 100 )   {
@return // do something to color1
}
@else {
@return \$color1  // don't do anything, use color1
}
}
``````

ok! progress

So the next big step is figuring out how to adjust a color if the contrast is too low. SASS has a great function that we already use called `scale-color`. You can use this to adjust a color’s lightness based on its existing value… so if you `scale-color(yellow, \$lightness: 50%);` you get a light yellow scaled proportionate to its existing lightness versus the disproportionate `lighten(yellow, 50%)` which creates white.

So how do we figure out how much to adjust `\$color1`? Well, lets say the difference between two colors’ brightness is 15. If our contrast goal is 100 that’s waaaay too low. So, if I take the resulting difference (15) and subtract it from our desired outcome (100) I get 85. That’s not a bad place to start.

So now based on the theme colors I need to contextually figure out if it makes more sense to brighten or darken `\$color1`.

This will largely depend on the overall theme lightness. If the background of a theme is white and a button on that background doesn’t have enough contrast… then I need to darken that button.

`scale-color(\$color1, \$lightness: -85%)`

If the theme’s background is a dark color and the contrast is too low, I need to lighten the button.

`scale-color(\$color1, \$lightness: 85%)`

Applying the function!

So now I think I can create something like this:

``````alert-info {
background-color: contrast(\$tertiary, \$secondary);
color:  contrast(\$primary, contrast(\$tertiary, \$secondary));
}
``````

What this does is:

background color: compares contrast between the desired background color (`\$tertiary`) and the background color `.alert-info` sits on (`\$secondary` in this case) - then it adjusts the color if needed.

color: compares the contrast of `.alert-info`'s text color (`\$primary`) versus the outcome of the color comparison of the background of `.alert-info` to the background of the page, then adjusts as necessary.

Here’s a proof of concept.
`\$secondary: #333`
`\$tertiary: orangered`
`\$primary: orange`

difference between `#333` and `orangered` = 122

difference between `orange` and `orangered` = 56
so adjust `orange`'s lightness on the `orangered` background by 56 `scale-color(orange, \$lightness: 56%)` and get `#ffd78f`… the contrast between `orangered` and `#ffd78f` is 103

usable colors

I haven’t tried this idea on a large scale yet as it’s not 100% there, but I feel like it could do the trick (if you have suggestions based on this, they’d be helpful!). You should probably get a badge just for reading this entire ramble.

Battle Axe - A free theme by the Tappara.co hockey community
(Kris) #2

Pushed an incremental change that should better dark theme support.

https://github.com/discourse/discourse/pull/2621

Still haven’t quite figured out the best way to use what I discussed above - but I used pieces of it to give us a better brightness comparison, as well as the ability to adjust scale-color on a per-element basis depending on whether the theme is dark or light.

``````@function brightness(\$color) {
@return ((red(\$color) * .299) + (green(\$color) * .587) + (blue(\$color) * .114)); //w3c definition of color brightness
}

@function dark-light-diff(\$adjusted-color, \$comparison-color, \$lightness, \$darkness) {
} @else {
}
}
``````

a bit more flexible than what was in there before, implemented like:

``````    color: dark-light-diff(\$primary, \$secondary, 90%, -60%);
``````

In the above example: \$primary color gets compared to \$secondary color, if \$secondary is brighter than \$primary, then \$primary is scaled lighter 90%. If \$primary is darker, it’s darkened 60%.

(Erlend Sogge Heggen) #3

Is there a simple “off switch” for this feature? It’s usually very handy, but sometimes you just want complete control.

(Sam Saffron) #4

not following, if you want granular control you can just override CSS in a site customization.

(Erlend Sogge Heggen) #5

Continued here: