Multiple Palettes
How to let your website have different appearances based on visitors’ preferences
I usually separate styling declarations into 2 parts:
styleswhich include components, responsive spacing, uniform typography etc.palettewhich does just the coloring and the choice of font
So styles use SCSS preprocessors ($variable) for all their values so that
their are hardcoded in the resulting stylesheet - but the ones that are available
to the palette which are CSS custom properties (var(--variable)) forming an
API to styling the website.
This article is about leveraging this API to create multiple palettes and let
the user choose one according to his preferences.
Note that each palette has to cover both themes (dark and light).
Palette composition #
A palette is comprised of the following elements:
fonts/*: font imports (fonts themselves are located instatic/fonts) and overrides based on 3 SCSS preprocessors defined inmain:font-text: default is system-uifont-head: default is system-uifont-mono: default is monospace-ui
lightanddark: reference to colors and fonts (and optional adjustments of font-size and spacing) for both themes using only CSS custom properties and SCSS preprocessorschromafor syntax highlight styles (only CSS custom properties)mainwith all the variables to be imported
Available palettes #
| Palette | Color | Font | Light | Dark |
|---|---|---|---|---|
pico | system-ui / monospace-ui | ☐ | ☐ | |
ayu | 34 bright colors | Inter / Open Sans | ☐ | ☑ |
nord | 16 dimmed pastel colors | Rubik / Inter / Source Code Pro | ☐ | ☐ |
naked | black and white | system-ui / monospace-ui | ☐ | ☐ |
solarized | 16 colors | DejaVu or Cousine | ☐ | ☐ |
gnome | 45 colors | cantarell | ☐ | ☐ |
Implementation #
- Include all the stylesheets in your pages with a unique identifer (
#id). - Place a toggler in the page findable with
.paletteclass name (like as follow). This feature being only available for people who have JS enabled, make sure your toggler is hidden is this is not the case. - Add the
palettevariable to your site’s parameters in theconfigfile. It expects a list of available palettes, the default one being the first. If this varible is missing, the fallback palette is nameddefault. - Insert the following script to activate the toggler.
- Done! 🎉
file: layouts/partials/palette_toggler.html
1{{ with site.Params.palette }}
2 {{ if gt (len .) 1 }}
3 <span class="palette" title="{{ i18n "palette_toggle" | default "Change color" }}">
4 {{ partial "icon" "palette" }}
5 </span>
6 {{ end }}
7{{ end }}
file: assets/scripts/togglePalette.js
1// Retreive the palette choices
2{{ $default := "default" }}
3{{ $palettes := slice $default }}
4{{ with site.Params.palette }}
5 {{ $palettes = . }}
6 {{ $default = index $palettes 0 }}
7{{ end }}
8
9// Enable a specific palette
10function enablePalette(targetPalette) {
11 let currentPalette = document.documentElement.getAttribute("data-palette");
12 if (currentPalette !== targetPalette) {
13 let targetStyleSheet = document.getElementById(targetPalette);
14 let currentStyleSheet = document.getElementById(currentPalette);
15 targetStyleSheet && targetStyleSheet.removeAttribute("disabled");
16 currentStyleSheet && currentStyleSheet.setAttribute("disabled", true);
17 document.documentElement.setAttribute("data-palette", targetPalette);
18 window.localStorage && window.localStorage.setItem("palette", targetPalette);
19 }
20 let targetPaletteFancy = targetPalette.charAt(0).toUpperCase() + targetPalette.slice(1);
21 const message = '🎨 ' + targetPaletteFancy + ' {{ i18n "palette_on" | default "palette enabled"}}';
22 console.log(message);
23}
24
25// Switch from one palette to the next one
26function togglePalette() {
27 const palettes = ['{{ delimit $palettes "', '" }}'];
28 let currentPalette = document.documentElement.getAttribute("data-palette");
29 let currentIndex = palettes.indexOf(currentPalette);
30 let targetIndex = ++currentIndex % palettes.length;
31 let targetPalette = palettes[targetIndex];
32 enablePalette(targetPalette);
33}
34
35// Set inital Palette based on user's preferences
36function initPalette(adminPref) {
37 // When visitors doesn't have JS, palette feature is unavailable so let's no clutter the DOM unnecessarily
38 // With JS we force palette substitution and the attribute must not be left empty
39 document.documentElement.setAttribute("data-palette", adminPref);
40 const userPref = window.localStorage && window.localStorage.getItem("palette");
41 const targetPalette = !!userPref ? userPref : adminPref;
42 enablePalette(targetPalette);
43 document.addEventListener("DOMContentLoaded", event => {
44 const toggle = document.querySelector(".palette");
45 toggle.addEventListener("click", togglePalette, false);
46 });
47}
48
49// Initialize Palette
50initPalette("{{ $default }}");
Even more personalization #
If you want to provide the visitors with a supplementary level of customization, you can use further implement a variation of the primary/accent color with a color picker like:
That’s it for today!
Comments