{"id":3659,"date":"2020-07-16T18:40:44","date_gmt":"2020-07-17T00:40:44","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3659"},"modified":"2020-07-16T18:40:44","modified_gmt":"2020-07-17T00:40:44","slug":"a-programmable-color-scheme","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2020\/07\/16\/a-programmable-color-scheme\/","title":{"rendered":"A programmable color scheme"},"content":{"rendered":"<p>I'm not a designer, and I've never been happy with the color scheme for <a href=\"http:\/\/kavanot.name\">kavanot.name<\/a>, so I decided to try something new. I use <a href=\"https:\/\/github.com\/scssphp\/scssphp\">scssphp<\/a> with <a href=\"https:\/\/github.com\/scssphp\/server\">scssphp server<\/a> (and yes, you need both) to generate the CSS, and it allows variables, so I can do:<\/p>\n<pre><code class=\"language-css\" >\r\n$hue = 250;\r\n$saturation = '40%';\r\n$textcolor: hsl($hue, $saturation, 10%);\r\n$displaytextcolor: hsl($hue, $saturation, 40%);\r\n$backcolor: hsl($hue, $saturation, 95%);\r\n$bordercolor: hsl($hue, $saturation, 90%);\r\n$deepbackcolor: hsl($hue, $saturation, 85%);\r\n<\/code><\/pre>\n<p>to make all the colors a variation on one single color. And I like that. But what color? Fortunately, scssphp allows for importing variables, so my `styles.php` includes:<\/p>\n<pre><code class=\"language-php\" >\r\nsetcookie('hue', $_GET['hue'], time()+12*60*60*24*30, '\/'); \/\/ one year expiration\r\nsetcookie('saturation', $_GET['saturation'], time()+12*60*60*24*30, '\/'); \/\/ one year expiration\r\n\r\n$scss = new Compiler();\r\n$scss->setVariables(array(\r\n\t'hue' => intval($_GET['hue']),\r\n\t'saturation' => intval($_GET['saturation']).'%',\r\n));\r\n<\/code><\/pre>\n<p>instead of the in-file <code class=\"language-css\" >$hue = 250; $saturation = '40%';<\/code>. Now I can include those values in the URL for the stylesheet itself,<br \/>\nand the cookies mean that I don't have to write it each time.<\/p>\n<p>To get the query variables to the stylesheet, I have to pass them from the main page:<\/p>\n<pre><code class=\"language-php\" >\r\n\t&lt;link rel=\"stylesheet\" id=mainstylesheet type=\"text\/css\" href=\"\/css\/style.php\/style.scss?hue=&lt;?=$_GET['hue']?&gt;&amp;saturation=&lt;?=$_GET['saturation']?&gt;\" \/&gt;\r\n<\/code><\/pre>\n<p>(I am ignoring the input sanitization you need to avoid XSS. <code class=\"language-php\" >intval<\/code> is your friend here!)<\/p>\n<p>But retyping <code>http:\/\/kavanot.name?hue=250&saturation=40<\/code> with minor variations is a pain. Can I make it interactive? Yes, with a Javascript<br \/>\ncolor picker. <code class=\"language-html\" >&lt;input type=color \/&gt;<\/code> is supported in all the major browsers, so I can do:<\/p>\n<pre><code class=\"language-javascript\" >\r\nvar colorPicker = $('input[type=color]');\r\nlet baseHSL = getBaseHSL(); \/\/ get the body color as [hue, saturation, lightness]\r\nbaseHSL[2] = 50; \/\/ start with a color that can be easily seen, 50% lightness\r\ncolorPicker.val(hsl2hex(...baseHSL)); \/\/ set the initial value\r\n\r\nconst stylesheet = $('#mainstylesheet')[0];\r\n\r\ncolorPicker.on('input', function(){\r\n\tlet [h, s] = hex2hsl(this.value);\r\n\t\/\/ change the stylesheet href and the styles change on the fly!\r\n\tstylesheet.href = stylesheet.href.replace(\/hue=\\d+\/, `hue=${h}`).replace(\/saturation=\\d+\/, `saturation=${s}`);\r\n});\r\n\r\nfunction getBaseHSL(){\r\n\t\/\/ getComputedStyle.color is rgb(r,g,b)\r\n\tlet [, r, g, b] = \/(\\d+),\\s*(\\d+),\\s*(\\d+)\/.exec(window.getComputedStyle(document.body).color);\r\n\treturn rgb2hsl (r, g, b);\r\n}\r\n<\/code><\/pre>\n<p>Now I need the functions <code class=\"language-javascript\" >rgb2hsl<\/code>, <code class=\"language-javascript\" >hex2hsl<\/code> and <code class=\"language-javascript\" >hsl2hex<\/code> to convert between the various color spaces. Luckily,<br \/>\n<a href=\"https:\/\/css-tricks.com\/converting-color-spaces-in-javascript\/\">Jon Kantner has done the work for me<\/a>.<\/p>\n<pre><code class=\"language-javascript\" >\r\nfunction hex2hsl(hex){\r\n\tlet [, r, g, b] = \/#(..)(..)(..)\/.exec(hex);\r\n\treturn rgb2hsl (+('0x'+r), +('0x'+g), +('0x'+b));\r\n}\r\n\t\r\nfunction rgb2hsl (r, g, b){\r\n\t\/\/ convert to fractions\r\n\tr = r\/255;\r\n\tb = b\/255;\r\n\tg = g\/255;\r\n\r\n\t\/\/ find greatest and smallest channel values\r\n\tconst cmin = Math.min(r,g,b);\r\n\tconst cmax = Math.max(r,g,b);\r\n\tconst delta = cmax - cmin;\r\n\tlet h;\r\n\r\n\t\/\/ calculate hue\r\n\tif (delta == 0){ \/\/ no difference\r\n\t\th = 0;\r\n\t}else if (cmax == r){ \/\/ red is max\r\n\t\th = ((g - b) \/ delta) % 6;\r\n\t}else if (cmax == g){\r\n\t\th = (b - r) \/ delta + 2; \/\/ green is max\r\n\t}else{\r\n\t\th = (r - g) \/ delta + 4; \/\/ blue is max\r\n\t}\r\n\th = Math.round(h * 60);\r\n\tif (h < 0) h += 360;\r\n\r\n\t\/\/ calculate lightness\r\n\tlet l = (cmax + cmin) \/ 2;\r\n\r\n\t\/\/ calculate saturation\r\n\tlet s = delta == 0 ? 0 : delta \/ (1 - Math.abs(2 * l - 1));\r\n\r\n\t\/\/ make s and l percents\r\n\treturn [h, Math.round(s*100), Math.round(l*100)];\r\n}\r\n\r\nfunction hsl2hex(h, s, l){\r\n\tlet c = (1 - Math.abs(2 * l\/100 - 1)) * s\/100;\r\n\tlet x = c * (1 - Math.abs((h \/ 60) % 2 - 1));\r\n\tlet m = l\/100 - c\/2;\r\n\tlet r, g, b;\r\n\t\r\n\tif (0 <= h && h < 60) {\r\n\t\tr = c; g = x; b = 0;\r\n\t} else if (60 <= h && h < 120) {\r\n\t\tr = x; g = c; b = 0;\r\n\t} else if (120 <= h && h < 180) {\r\n\t\tr = 0; g = c; b = x;\r\n\t} else if (180 <= h && h < 240) {\r\n\t\tr = 0; g = x; b = c;\r\n\t} else if (240 <= h && h < 300) {\r\n\t\tr = x; g = 0; b = c;\r\n\t} else if (300 <= h && h < 360) {\r\n\t\tr = c; g = 0; b = x;\r\n\t}\r\n\r\n\tr = Math.round((r + m) * 255);\r\n\tg = Math.round((g + m) * 255);\r\n\tb = Math.round((b + m) * 255);\r\n\t\r\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`;\r\n}\r\n\r\nfunction toHex(d) {\r\n\td = d.toString(16);\r\n\tif (d.length == 1) d = `0${d}`;\r\n\treturn d.toUpperCase();\r\n}\r\n<\/code><\/pre>\n<p>and now I can play with colors to my heart's content!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I'm not a designer, and I've never been happy with the color scheme for kavanot.name, so I decided to try something new. I use scssphp with scssphp server (and yes, you need both) to generate the CSS, and it allows variables, so I can do: $hue = 250; $saturation = '40%'; $textcolor: hsl($hue, $saturation, 10%); [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3659"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=3659"}],"version-history":[{"count":5,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3659\/revisions"}],"predecessor-version":[{"id":3664,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3659\/revisions\/3664"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3659"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3659"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3659"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}