CSS is the worst because it blocks page rendering. The bigger it grows, the more it blocks. And bigger is what CSS becomes if left unattended, as anyone who has worked on any project for more than a few days or with more than a few people can confirm.
So occasionally one must step back and look with disgust at what one hath done and refactor and delete, delete, delete...
But how do you know you didn't mess anything up? CSS grows out of proportion so easily because people are afraid to touch it, because they don't know what might break.
Even if not refactoring, even if you just fiddle with something on the product promo page, how do you know you didn't break the homepage by mistake?
How about you take a screenshot before and after the change and compare? And how about you let the machine compare the difference in the screenshots? And how about letting the machine compare in bulk - home page, about page... you get the point?
This post is about a quick script that shows an example of what you can do using phantomjs and imagemagick.
Setup
1. Install imagemagick (e.g. $ brew install imagemagick
)
2. Install nodejs and phantomjs (npm install phantomjs
)
url2png with phantomjs
First you need a little utility script that uses phantomjs to load a page and makes a screenshot, let's call it url2png.js
:
var system = require('system'); var url = system.args[1]; var png = system.args[2]; var page = require('webpage').create(); page.viewportSize = { width: 800, height: 600 }; page.open(url, function (status) { if (status !== 'success') { console.log('Unable to access the network!'); } else { page.render(png); } phantom.exit(); });
Running the script:
$ phantomjs url2png.js http://google.com goog.png
Result:
Actually the png part in url2png.js is a misnomer. You can pass a jpeg filename, a pdf... and they'll all work thanks to the magic of phantomjs.
Preparing test files
Let's test using design #1 from csszengarden - you need the CSS and the HTML that it applies to.
$ curl http://csszengarden.com > zen.html $ curl http://csszengarden.com/001/001.css > before.css
Now make a copy of before.css
and edit something, e.g. change the first property - make the margin
1px
instead of 0
.
$ cp before.css after.css
Finally, find the <link>
tag in the zen.html
and make it more template-y, so it can be scripted:
<link rel="stylesheet" media="screen" href="{{{FILE}}}">
Here's what you have so far: https://www.phpied.com/files/diffcss/source/, the three files neatly in a /source
directory.
The plan
Write a script that:
- Reads from the
/source
directory - Writes
tmp/before.html
(that<link>
s tosource/before.css
) - Writes
tmp/after.html
(that<link>
s tosource/after.css
) - Takes screenshots of loading both pages -
tmp/before.png
,tmp/after.png
- Runs ImageMagick's
compare
utility - If there's a difference between the two screenshots, make an animated
tmp/dif.gif
with the two so it's easier to compare by humans
diffcss.js
Here's one implementation, callback hell and all:
var read = require('fs').readFileSync; var write = require('fs').writeFileSync; var exec = require('child_process').exec; var tmp = process.cwd() + '/tmp/'; var html = read('source/zen.html', 'utf8'); var before = html.replace('{{{FILE}}}', '../source/before.css'); var after = html.replace('{{{FILE}}}', '../source/after.css'); write('tmp/before.html', before); write('tmp/after.html', after); exec('phantomjs url2png.js "file://'+ tmp +'before.html" tmp/before.png', function () { exec('phantomjs url2png.js "file://'+ tmp +'after.html" tmp/after.png', function () { exec('compare -metric PSNR tmp/before.png tmp/after.png tmp/diff.png', function(e, ste, result) { if (result !== 'inf') { console.log('bad, bad! See tmp/dif.gif'); exec('convert -delay 50 -loop 0 ' + 'tmp/before.png tmp/after.png '+ 'tmp/dif.gif'); } else { console.log('all good'); } } ); } ); } );
The imagemagick compare part (and the gif command) I lifted off from http://www.imagemagick.org/Usage/compare/. Here's what it has to say about the metric I chose:
PSNR .... Peak Signal to noise ratio (used in image compression papers)
The ratio of mean square difference to the maximum mean square
that can exist between any two images, expressed as a decibel
value.The higher the PSNR the closer the closer the images are, with
a maximum difference occurring at 1. A PSNR of 20 means
differences are 1/100 of maximum.
When the two images are identical I get "inf" (short for "infinity" I suppose). If not, I consider this a failed test and proceed to announcing that fact together with creating that cute little gif.
Results
Check them out here: https://www.phpied.com/files/diffcss/tmp/, animated GIF and all.
Other options
You see it's not hard to roll your own CSS diff helper/checker/unit testing utility. But there are also readily available other options, see for example PhantomCSS. This page also links to other existing tools.
Another thing is that you can also do the image diffing yourself (here's a php example) and make it run faster by bailing out as soon as one pixel is different. No need to compare the rest.
Happy diffing
Now let's bravely embark on cutting down some o' that CSS bloat! Arrrr!
UPDATE: Oh lookie - part 2
Comments? Find me on BlueSky, Mastodon, LinkedIn, Threads, Twitter