Creating a JavaScript Color Theme Animation

27th of September

As you might know, I run another blog mainly for my marathon running hobby. It can be found at www.teesches-marathons.eu. Design-wise, I keep it as simple as possible – not unlike this blog –, but from time to time I like to throw in some minor changes like little quirky experiments. 

This post is a description of how one of these came into existence. 


I really like the hex code color of #3399ff, or #39f in short form. It’s a bright blue with a hint more green than red in it. Whenever I need an accent color for anything, like for example a link color or header background color, I use this first. 

#3399ff

I use it at www.teesches-marathons.eu as well, but after spending quite a bit of time on the site it got a bit boring for my own eyes. As the posts over there are typically rather long and require around 15 to 30 minutes of reading time on average, I thought it might make sense to change the color of the header, buttons, and links, for the benefit of the readers as well. But not just once to a different static color, that would be too easy and lead to the same kind of visual boredom. 

One of those thoughts that come right before falling asleep at night was to change the color dynamically, in correspondence to the scrolling progress of the reader. Smoothly, of course, in a non-distracting way. 

After a bit of experimentation, which I usually do at the same time as writing the code, I arrived at the conclusion that it would be wise to use a set of pre-determined, good-looking colors as a basis, as opposed to generating the colors at random. So I created a set of seven colors I liked, RGB formatted them for easier calculating, and these look like this: 

var availableColors = ['51,153,255', '243,152,2', '227,0,11', '101,163,79', '0,94,181', '202,0,126', '129,0,202']; 
// '51,153,255' is my beloved #39f in decimal RGB representation 

Or rather, look like this:

A pretty little rainbow

So, I got some colors now. The next question to answer would be how and when to use these colors. With some testing I found out that it seemed smoothest if I take a new random color out of the array for every 2,000 pixels of scroll progress. So, next I need to build an array of colors, taken random from the defined ones, with as many color steps total as the website’s height divided by 2,000 and rounded to the next full number would be. 

Creating the Array of Colors

var changeColorEveryPixels = 2000;
var documentHeight = document.documentElement.scrollHeight;
var availableColors = ['51,153,255', '243,152,2', '227,0,11', '101,163,79', '0,94,181', '202,0,126', '129,0,202'];

// start with the base color #39f at the top of every page
var colorArray = ['51,153,255']; 

// calculate the amount of steps necessary for this page
var colorSteps = ((documentHeight - documentHeight % changeColorEveryPixels) / changeColorEveryPixels) + 1;

for(var i = 0; i < colorSteps; i++) {

	// what’s the previous color in the array?
	var lastColor = colorArray[colorArray.length-1]; 

	// getting the new random color from the defined ones
	var newColor = availableColors[Math.floor(Math.random() * availableColors.length)]; 

	// making sure no color will be inserted twice in a row
	while(lastColor == newColor) {
		newColor = availableColors[Math.floor(Math.random() * availableColors.length)]; 
	}
	
	// add the new random color to the array
	colorArray.push(newColor);
}

So, that’s some progress. But it does nothing yet. We need to invoke the real changing of colors now!

// every 200 milliseconds (five times per second) we’ll call a function to do the actual color changing
window.setInterval(function() {
	colorChange();
}, 200);

With some playing around I found out that 200 milliseconds is a good time interval between update calls. It’s often enough to seem smooth (more on optimizing that later), and little enough to avoid older CPUs breaking down and crashing the site. 

The Function to Do the Actual Work

function colorChange() {
// getting scroll position from the top in pixels var currentOffset = window.pageYOffset; // find out the previous color from our array var prevColor = colorArray[(currentOffset - (currentOffset % changeColorEveryPixels)) / changeColorEveryPixels]; // find out the next color from our array var nextColor = colorArray[((currentOffset - (currentOffset % changeColorEveryPixels)) / changeColorEveryPixels) + 1]; // find out the progress between both colors as a number between 0.00 and 1.00 var progress = (((currentOffset % changeColorEveryPixels) / changeColorEveryPixels)).toFixed(2); // splitting both colors (previous and next) into their RGB parts [prevColorR, prevColorG, prevColorB] = prevColor.split(','); [nextColorR, nextColorG, nextColorB] = nextColor.split(','); // calculate the current color using the progress between both steps – adding numbers in javascript is fun! var newR = +prevColorR + +((nextColorR - prevColorR) * progress); var newG = +prevColorG + +((nextColorG - prevColorG) * progress); var newB = +prevColorB + +((nextColorB - prevColorB) * progress); // we need to round the numbers to integers because Safari doesn’t like floating point RGB values var newColor = parseInt(newR) + ',' + parseInt(newG) + ',' + parseInt(newB); // and now, the actual color changing! find all links that match specific css rules and change their css color values var links = document.querySelectorAll('a:not(.blog-item h3 a, .blog-item p a), .sharingbuttons a:not(.like-counter), .sharingbutton-opener').forEach(function(el) { el.style.color = 'rgb(' + newColor + ')'; }); // same thing for the elements that need the new color as css background instead of css color var backgrounds = document.querySelectorAll('header, .blog-item p a.read-on-button, .blog-item p a.read-on-button, .read-on-button').forEach(function(el) { el.style.backgroundColor = 'rgb(' + newColor + ')'; }); // and we’re done! return true; }

In the above code you can see how weird adding two numbers with javascript works. The + operator concatenates variables instead of mathematically adding them, which is unlike most other programming languages. See Thomas Fuchs’ tweet for more info on the topic.

The beautiful thing about this implementation is that it’s subtle and new for every sub-site, but consistent within a site. So when you scroll down and the color slowly turns from blue to red, scrolling up will also turn it slowly back into blue. Simple.

Tweaking with CSS Transitions

Implementing that code does the whole trick. A little tweak to make the color changes a bit smoother, is this additional CSS rule, added to the elements that are affected by color changing: 

transition: all .2s;

This will transition each color change via CSS over .2 seconds, i.e. 200 milliseconds, so the time between function calls will be met exactly and look smoothed out. 

So, Does This Look Any Good?

Find out for yourself at www.teesches-marathons.eu. What do you think?

Credits: for the pretty code syntax highlighting I used Lea Verou’s free tool PrismJS

More Posts to Read

loading