In solving some of the problems needed to create my pure CSS progress bars with rounded corners, I ended up using a combination of two backgrounds: a background-color and a background-image. Unfortunately, images are rather static, inflexible things. They require you to make a new image every time you want to use a slightly different colour… or do they?
While playing with different colours in my design (I like to prototype in code), the inability to change the colour of the image on the fly didn’t sit well with me. After all, it’s just a bunch of bytes being parsed by the browser before being rendered on the screen. If we can provide that data in CSS as a data-uri, as I already showed in my previous article, then surely we can have some level of control over what that data represents. I got a bit obsessed about this and over the weekend I managed to get this insanity to work. By dynamically generating a data-uri, we can have a 1-pixel image of any colour we want.
The result is available on Github. Check out the demo to see it in action.
Dynamic CSS
The one thing this solution requires is some level of dynamic control over the styles that define the image. For all I care, this can be SASS, a perl script that generates static CSS on the server or even PHP. I used LESS for this, because I was already using it anyway and it allows me to write JavaScript in my stylesheets. You can easily stick with with whatever you are already using and use LESS on the side, but the ideas in this technique are not tied to LESS in any way; it shouldn’t be hard to port to other tools.
A Dynamic Image
For a solid, single-colour background, all you need is a single pixel image. In the previous article I used this one:
It’s the smallest possible (43 bytes) 1-pixel image that is valid and visible on the web. This one happens to be red, but as I will show it can be any colour. I chose GIF because it was smaller than PNG for my purposes, but it’s probably possible to generate a PNG as well. GIF is also a very old and well understood file format, and before you ask: it’s no longer trapped in intellectual property limbo.
The goal here is to take a colour (say, red) and generate a 1 pixel gif from that. To be able to do that, we need to understand the GIF file format a little bit. Fortunately Wikipedia does a pretty good job a that, so I am not going in too much detail. What is important is that in GIF, the image is split into two parts: the palette is stored as a global colour table, separate from the image data. This means, that in order to change the colour of a one pixel image, all we need to do is change that colour in the palette; there’s no need to touch anything else.
The simplest possible GIF for this has only 43 bytes: it has just one pixel and a 1-bit palette (two colours, of which only one is used).
If we want to have a 1-pixel GIF of another colour, all we need to do is find colour #0 in the palette. Let’s do this for red_pixel.gif:
00: 4749 4638 3961 GIF89a 06: 0100 0100 f000 ...... 0c: 00ff 0000 ffff ...... 12: ff21 f904 0000 .!.... 18: 0000 002c 0000 ...,.. 1e: 0000 0100 0100 ...... 24: 0002 0244 0100 ...D.. 2a: 3b ;
As expected, the first colour in the palette has a hexadecimal value of #FF0000, or 255,0,0 RGB. Changing this to #00FF00 will make it green and #0000FF blue. Just load up the file in a HEX editor of choice (Hex Fiend is nice) and try it.
Now, being able to change an image colour by changing some values in a HEX editor is nice, but it’s hardly flexible. To make this useful for anything, we need to be able to change the GIF from CSS. Yay for data URIs!
Data URIs allow content to be embedded in CSS, where it can be used for backgrounds etc. As we are generating CSS using LESS, all we need is a way to generate a data URI for the desired image!
The data URI for red_pixel.gif looks like this:
data:image/gif;base64,R0lGODlhAQABAPAAAP8AAP///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==
The 43 bytes have been turned into 61 ASCII characters using Base64 encoding, plus some stuff describing this is a data URI with an encoded GIF. If we want to change the GIF, all we need to do is change the data URI and we’re golden.
Now, it would be possible to just generate a whole GIF from an RGB bitmap and Base64 encode that, but that would be a lot of work. We really only need to change the 3 bytes representing the primary colour in the palette. The rest of the image is left untouched, so just swapping those three bytes out in the data URI is enough to get a 1-pixel GIF in a different colour.
Unfortunately, the way Base64 works makes this a bit harder. Base64, as the name suggest, encodes data in 64 bit chunks. Every 6 bits are represented by 1 ASCII character. With our luck, the first four bits of the primary RGB pair get encoded in one such 6-bit chunk and the last two in a separate 6-bit chunk. This means replacing the 6 bits in the palette, requires re-encoding the 12 surrounding bits into 5 characters.
Caring not too much about efficiency, this can be done in a few lines of JavaScript (try in jsFiddle):
Generating a GIF with the desired primary colour now becomes a simple matter of inserting these 5 characters in the right place in the Data URI:
"data:image/gif;base64,R0lGODlhAQABAPAAA" + encodeRGB(0,0,255) + "///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==)"
Doing this in LESS is a bit complicated, as Less is very picky about syntax, but it’s easy to just cram all of that ugly code into a mixin, so that when it is used, you can just forget about all of that:
This gives div.progress item a gif with the desired colour as a background. The trick only works in browsers that support data-URIs, so IE7 and below get no love.
As a bonus, I also liked to be able to specify the colour as a HEX value. Try that out in a jsFiddle here. The whole thing is available as a LESS module on Github. It really works, check the demo!
I would love to see someone do a PNG version of this hack. I didn’t need it myself, but a 24 bit PNG would allow specifing an Alpha (opacity) value as well!
Pingback: dmitry_f
Pingback: membrany dachowe
Pingback: infant monitor reviews 2014
Pingback: first aid kit
Pingback: mancare pentru caini
Pingback: Corey Meredith