Ashley Sheridan​.co.uk

Single Div CSS Mona Lisa

Posted on

In the world of CSS, there is little as fun and challenging as the single <div> challenge. It's a creative idea that's been taken to some impressive lengths by Lynn Fisher of the 'A Single Div' website.

The technique has been picked up by many front-end developers, who have created some amazing works with it, like Sasha's Mike Wazowski, and the wonderfully festive Christmas tree by Ari. It's the sort of thing that can't but help inspire others to do the same.

My Previous Efforts

Even without going to the extreme of limiting yourself to a single HTML element, there is some amazing things that can be achieved with only a few. I've even had a go at making my own things, from my CSS Zoo, a collection of charming animals built using only a handful of elements, right up to my animated Star Wars BB8. These used the classic combination of border-radius, background gradients, and box shadows, the staple triangle of tools for any CSS artist.

Box Shadows as Pixels

One technique I saw quite some time ago by Geoff Graham, uses only the box-shadow to generate pixel art. To do this manually, however, is a very time consuming process, requiring quite a bit of trial and error.

There is a quote often attributed to Bill Gates (although that looks like it is most likely unfounded.):

I will always choose a lazy person to do a difficult job because a lazy person will find an easy way to do it.

Regardless of who the quote originated from, it has a nugget of truth: developers are the sort of people who will look for better, more efficient ways of doing something, and I've been no exception in this regard.

So I created a pixel art generator in PHP that reads in an image, picks out every nth pixel (by row and column) and turns it into a box-shadow.

The key with this method is that each box shadow can have a horizontal and vertical offset, and with the third parameter set to 0 for each shadow, the blur radius is effectively gone, leaving nice sharp edges, like a pixel.

How it Works

The brunt of the work exists in the nested loops within the pixel_art PHP script:

for($x = 0; $x < $width / $image_block_size; $x ++) { for($y = 0; $y < $height / $image_block_size; $y ++) { $rgb = $image->get_rgb_at($x * $image_block_size, $y * $image_block_size); $pixel = new \Entities\Pixel($rgb, $x, $y); $canvas->add_box_shadow($pixel); } }

Beyond this, the rest of the work is done with the helpers, and the entities are used to hold everything else together. The CLI script accepts one parameter: the filename, which is uses to generate the name of the HTML file with the embedded stylesheet that is the final art piece.

Examples

The main example I've added to the readme.md file in the repository is the famous Mona Lisa:

Leonardo Da Vinci's Mona Lisa

To avoid performance issues in some browsers (which can sometimes have issues with too many box shadows on a single element), I've used quite pixelated settings to create this in a single <div>:

The margins to the left and the bottom of the single HTML element ensure that the full set of shadows are visible even if there is surrounding content. If you're using a decent modern browser, you can push the pixels even smaller and have a lot more of them, to create images that look like they were never created with CSS at all:

Still to Come

There are still a few things that I am yet to improve on with the script, like error handling, as at the moment, it will completely fail on any error without any kind of useful information. Also on the list are better CLI argument usage, and support for more than JPEGs.

The performance of this is quite bad, as expected really, but there may be some small improvements that can be made in that area too, such as reducing the colour palette of the image and using one large rectangle of one colour for the most common colour. This would mean that only the pixels that were a different colour would need to be drawn. This could reduce the number of box shadows required by more than half for some images.

Conclusion

While this is certainly a fun approach to the single div challenge, it's completely impractical, and wouldn't ever be useful on a production website. It has terrible browser performance and creates absolutely huge amounts of code (nearly 27,000 lines of CSS for the four examples on this page alone!) It's still great as an example of how far you can push CSS to do things that it was never intended to do.