Change your colour scheme

Javascript’s Proxy is neat


So last week I went to State of the Browser. I had a great time, met some really interesting people, and got to see some great talks.

One talk in particular was on Two-way data binding by Chris Ferdinandi. The general gist of it is that you can use the Proxy object, which can intercept operations when a value is set, to perform UI updates.

So I thought I’d use it to power the colour scheme picker on my blog. Here’s the code:

const defaultColourScheme = () => {
if (localStorage.getItem('color-scheme')) {
return localStorage.getItem('color-scheme');

if (window.matchMedia('(prefers-color-scheme: dark)')?.matches) {
return "dark";

return "light";

const data = new Proxy({
'color-scheme': defaultColourScheme(),
}, {
set: (obj, key, value) => {
obj[key] = value;
localStorage.setItem(key, value);
document.body.setAttribute(`data-${key}`, value);

return true;

const onRadioChange = e => {
data[e.target.name] = e.target.value;

document.querySelectorAll('[name="color-scheme"]').forEach(radio => {
radio.addEventListener('change', onRadioChange);
radio.checked = data[radio.name] == radio.value;
document.body.setAttribute(`data-color-scheme`, localStorage.getItem('color-scheme'));

The really important parts are between lines 20-30. Basically, when a property is updated, it gets synced to localStorage and set as a data- attribute on the body element (which I use for CSS targeting).

It works pretty well! It even initialises with the right values if the user is a first-time visitor. And the code is fairly short; originally I was going to use a WebComponent, but the state management ended up being quite verbose.


About the author

My face

I'm Lewis Dale, a software engineer and web developer based in the UK. I write about writing software, silly projects, and cycling. A lot of cycling. Too much, maybe.