Inter Caetera

1/12/2020

Proof of concept: user defined themes in web applications

If you’ve been following me for a while, you’ll know that I’m a big fan of having a unified colour scheme across all of my system applications. I use a lot of different web applications and also develop them during my day job so having a way to theme them would be quite nice. Unfortunately, web apps are notoriously resistant to any sort of user customization in the name of some ephemeral and to me incomprehensible concept called “branding”.

I use a tool called wpg to generate and manage my colour schemes. wpg in turn behind the scenes uses a smaller program called pywal, which lets you automatically generate terminal and graphical colour schemes from wallpapers.

One significant advantage of pywal is that it generates colour schemes in a variety of different formats, including CSS, JSON and many others. This allows these files to be directly plugged into other applications to theme them.

I looked for a way to theme web applications using the CSS file but I haven’t found a good way to do it. The method described in this article certainly works, but would have to be streamlined and standardized if we were to use it in any sort of a production environment.

Abstract

Sounds easy enough so let’s investigate some possible options. Using CSS custom properties is easy. However, we would require two different types of fallbacks when considering this sort of design: one for users who come to our app but haven’t got a custom colour scheme defined, and another for users who come with a browser which does not support CSS custom properties.

I briefly toyed with the idea of always keeping an up-to-date fallback of the colour scheme I’m currently using on my main workstation. This could perhaps be accomplished by amending the wallpaper change script that I use with a git command to push the new colour scheme to a repository, from where it would be read and compiled by a preprocessor (because pywal also exports the colour scheme in a JSON format as well as SASS variables). However that’s a bit outside of the scope of this project.

Some sort of a fallback would be nice, though. While I didn’t bother implementing it in the finished product that you’ll see at the end of this article, we could do something like this:

$t-background = #222
// Fallback for if there is no theme.
$background = var(--background, $t-background)
body
  // Fallback for if the browser does not support CSS custom properties.
  background $t-background
  background $background

Note that the exported colours file contains a path to the image used to generate it so with some clever scripting we could probably expose that file.

So that’s really all we have to worry about on the developer side. Just use colours from the variables when designing your page, provide fallbacks and voilà.

On the user side

…things get slightly trickier. First of all, we have to decide how we’re going to serve our colour scheme to the app.

My first instinct was to use a browser extension like Stylish. I first just copy-pasted the colors.css file generated by pywal, enabled the theme everywhere and see if it would work. Sure enough it did.

However immediately we will face the problem of our colour scheme not being dynamic. After all, we don’t really want to paste it every time we change our wallpaper. I first turned to our good friend the @import statement.

@import url("file:///home/justinian/.config/wpg/formats/colors.css");

Sounds good, doesn’t work.

Discovering it was frustrating at first, but then became painfully obvious — we can’t just load arbitrary files over the file:// protocol, that’d be a massive security problem.

Well then, if not that, then what?

We could create a static file http server that would serve the folder containing the colour scheme files and load it over localhost. I used serve because I had it around and it just works™ but if this were to became a mainstream project, it would be better to use something more lightweight, like suckless’ quark or lighttpd.

If we serve the directory and replace our import statement with this:

@import url("http://localhost:5000/colors.css");

Then sure enough, our assets will load properly.

Now that of course begs the question why use Stylish at all. And it’s a good question, because instead of using Stylish, we could simply request that resource in an @import statement inside our application’s CSS stylesheet or a link tag.

There are two advantages to Stylish that I can see:

The first item could be handled with some sort of customization option on our page instead of delegating it to a browser plugin and the second is irrelevant to everyone but power users (though on the other hand if one uses pywal then probably one can already be considered a power user).

And really that’s kind of just it.

Next steps

I think this is a really interesting idea to further investigate. Of course to make this more user friendly, there would have to be a package that does the grunt work for the user, like setting up the http server, running it on startup, etc. This can definitely be accomplished, and having custom theme-enabled web apps would be a next-step towards more customization.

Divider Divider
Back to top