When investigating the technology that would power my new blog (the one that you are reading) I had two key objectives in mind:
- I wanted the blog to be beautiful and in order to achieve this I needed technology that had easily customizable styles
- I wanted the blog to be easy, meaning that I wanted to be able to write, distraction free, and not have to muck around with settings or styles after the initial setup
I chose to go with Ghost—the blogging platform. Ghost’s theming system gave me the flexibility that I needed to customize this blog’s style, and Ghost’s API provided a sensible structure that allowed for a beautiful publishing experience.
When considering the look and feel of the blog I had an idea: to give every post its own unique color. In my mind, having a unique color for each post would keep the theme interesting and provide a visual connection with any given post. Rather than using cheesy stock photos, I’d use colors—simple. My initial approach to this problem was to manually specify colors for each post and then have the theme render them. There were two problems with this approach:
- Ghost doesn’t currently allow for custom attributes on posts, so I would have had to find another way to store and retrieve the color associated with a post—that would complicate things.
- I’m terrible at finding colors that work together. Over time, the task of choosing a color for every post I write would be time-consuming and boring (at least for me). In other words, selecting colors manually would go against the second reason that I went with Ghost: it’s easy.
I considered whether or not I should just drop the idea of seperate colors for different posts. Then my engineering side got the best of me and I decided to develop a solution. Here is what I set out to build:
- A stylesheet that can randomly generate a pastel color for each blog post
- The colors that are generated are unique to each post and are consistent for everyone, meaning they won’t change between page loads
- The colors will be used in the header of the blog post (scroll to the top of this page to see what I mean) and also on the homepage
What follows is a description of how I built a random color generator into the stylesheet of the Ghost blog that you are currently reading.
The Color Space
When playing around with colors in Sketch I found a pattern to the colours that I wanted to use.
In the HSL color space I could keep saturation at 100%, luminance at 93%, and could adjust the hue (between 0 and 360) to generate a range of colors that worked well with the theme of my blog. The equation here is quite simple, fix saturation at 100%, fix luminance at 93%, and randomly select a value for hue and voilà, we have a candidate color.
The Random Generator
It's pretty clear at this point that all we need to do to create a color is to generate a random number between 0 and 360 and tell CSS to do its thing.
function generateColor() {
var randomValue = Math.round(Math.random() * 360);
var colorString = "hsl(" + randomValue + ", 100%, 93%)";
document.body.style.backgroundColor = colorString;
}
You can play around with this in the CodePen below.
The problem is that we don't want our colors to be randomly generated every time the user reloads the page, we want each post to have its own color that will always be the same. In order to do this we need to associate our "random" color selection with some sort of seed value. What we need is a hash function that can map from a post, to an integer value for our hue.
A simple Google search revealed a StackOverflow answer that contained a simple and fast hashing function implemented in Javascript. I'll paste the code below, not because you need to understand it, but because you may be interested in how a hash function works under the hood.
String.prototype.hashCode = function() {
var hash = 0;
if (this.length == 0) {
return hash;
}
for (var i = 0; i < this.length; i++) {
var char = this.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
What this handy piece of code does for us is add a method to all strings that will generate a hash code for that given string.
"hello".hashCode() //99162322
"hello!".hashCode() //-1220935281
"hello".hashCode() //99162322 (notice that this didn't change)
We can then convert our outputs from our hash function to fall into the range between 0 and 360. To do this we use Javascript's Math.abs function to convert to a positive integer and then use the modulo operator to limit the range of the integer to our required range.
var getHashedHue = function(value) {
return Math.abs(value) % 361;
}
getHashedHue("hello".hashCode()) //315
getHashedHue("hello!".hashCode()) //69
getHashedHue("hello".hashCode()) //315 (notice that this didn't change)
We are now at a point where we can use a string to generate a random color, with the same string always generating the same color. Try it out in the pen below. Also, try copying and pasting the title of this post below and check that you get the same color that appears in the header of this page.
The final thing to do is to tie this back to the Ghost theme so that parts of the user interface change depending on the seed that is provided. I decided to use the title of a blog post to be the seed that will define the color that is generated. I implemented this using a custom data attribute called data-post-color-background-seed (yeah, perhaps that could have been shorter). Here's how it works. First, you can add the data attribute to any html element. In the example below, I've applied it to a h2 element on my homepage and have provided the post title as a seed.
<h2
class="post-title"
data-post-color-background-seed="{{title}}">
{{{title}}}
</h2>
On every page render a Javascript function runs that looks for elements with the custom data attribute and sets their background-color depending on the seed that is passed in. Note that below I am using JQuery, but this can easily be done in vanilla Javascript too.
function stylePage() {
var elements = document.querySelectorAll("[data-post-color-background-seed]");
$.each(elements, function(index, element) {
var seed = element.dataset.postColorBackgroundSeed;
var hash = getHashedHue(seed.hashCode());
var hslString = "hsl(" + hash + ", 100%, 93%)";
$(element).css("background-color", hslString);
})
};
stylePage();
I'm not convinced that this is the best way that I could be tying the color-generating logic into the hashing logic. Currently, Ghost's theming system does not allow for custom helpers (JS code) to be used (at least not easily), so this is something that is best handled on the frontend. In the future, when Ghost provides more customisation with themes, I'd like to move this logic to the backend. For now though, this rendering method seems to work.
Summary
This solution is not perfect. An obvious issue is that the probability of any two posts having the same colour (a hash collision) is relatively high. In this case that's a tradeoff that I am willing to make as having two posts with the same color will not impact user experience. Also, when playing around with the CodePen above (the second one), I noticed that similar words have similar colors. For example, try the words "cat", "can", "cap" and "cab", they are all very similar colors. The same pattern is true for longer strings where all characters, apart from the final one, are identical. Again, I'm not too worried about this; I don't anticipate having too many blog post titles that are close to being identical.
As previously mentioned I'm not sold on the rendering logic required on the frontend. This is especially problematic when it comes to AMP versions of my posts, where custom JS code can not be executed. This is part of the reason that I've disabled AMP for the time being. Once Ghost has better support for adding custom logic to theme rendering I'll be moving this logic there.
Reflecting on my goals that I had when starting out: "I wanted the blog to be beautiful" and "I wanted the blog to be easy", I believe that this approach to randomized styling has lead to an outcome that both looks great and is incredibly easy for me to maintain. I simply write a post and the title of the post is used to generate a color that I can be confident will look good. I challenge you to think about how your stylesheet could be enhanced with a touch of randomness.