The Complete Guide to CSS Box-Shadow Image Art
Everything you need to understand how images can be rendered in pure CSS — how it works under the hood, when it's a good idea, when it isn't, and how to get the best results.
What does "image in pure CSS" actually mean?
When most people think of putting an image on a web page, they think of the <img> tag or a CSS background-image rule. Both reference an actual image file — a JPEG, PNG, WebP, SVG, or Base64-encoded data URI. The file is downloaded or decoded, and the browser paints it.
"Pure CSS images" skip the file entirely. There is no image data being shipped. Instead, the picture is reconstructed from dozens, hundreds, or thousands of small coloured rectangles — each positioned and coloured by CSS alone. The technique most often used is the box-shadow property.
Understanding the CSS box-shadow property
The CSS box-shadow property was designed for adding shadows to elements — drop shadows, inner glows, and layered depth effects. But two specific features combine to enable pixel-level image rendering:
- It accepts multiple comma-separated shadow values on a single element.
- Each shadow accepts an explicit X/Y offset, blur, spread, and colour — effectively arbitrary positioning.
Each shadow value follows the syntax offset-x offset-y blur-radius spread-radius color. By setting blur to 0 and spread to the desired pixel size, each shadow becomes a crisp, solid-coloured square. Position thousands of these squares at exact pixel coordinates with the right colours, and you get a complete image, rendered entirely by the browser's CSS engine.
.pixel-image {
width: 1px;
height: 1px;
box-shadow:
0px 0px 0 1px #ff0000,
1px 0px 0 1px #00ff00,
2px 0px 0 1px #0000ff,
0px 1px 0 1px #ffff00,
1px 1px 0 1px #ff00ff,
2px 1px 0 1px #00ffff;
}That snippet draws a 3×2 coloured grid — six "pixels" rendered entirely by one element's box-shadow. Image2CSS scales the idea up to every pixel in your source image, automatically.
Performance considerations
While the file size of CSS shadow art can be smaller than the equivalent JPEG or PNG (especially at moderate resolutions), the rendering cost is higher. The browser must calculate and paint each shadow individually, which is more CPU-intensive than decoding a compressed image format. Roughly speaking:
- Up to ~10,000 shadows: renders instantly on any modern device including budget phones.
- 10,000–50,000 shadows: renders smoothly on desktops and flagship phones, may show a brief paint delay on older devices.
- 50,000–100,000 shadows: noticeable paint cost; fine for a hero demo or one-off showcase, but not ideal for production.
- Above 100,000 shadows: visible jank on mobile; scroll performance and animation both suffer.
For production use, CSS images work best for small decorative elements (icons, logos, pixel art) at resolutions under 150 pixels wide.
CSS art vs traditional image formats
Traditional formats like JPEG and PNG use sophisticated compression algorithms. CSS box-shadow art has no compression — each pixel is stored as a literal text string of coordinates and colour values. That means the format almost always produces more bytes per pixel than a compressed image.
The advantage of CSS images lies elsewhere:
- Zero HTTP requests — the image is part of the stylesheet.
- No CORS issues — nothing to fetch cross-origin.
- Works in restrictive environments that block external images.
- Individually animatable pixels using CSS transitions.
- Copy-pasteable into pens, demos, and tutorials with no image hosting step.
Browser support and compatibility
Multi-value box-shadow has been supported in every major browser since IE9 / 2011, so compatibility is effectively universal. Where it gets more complicated is in email clients, which have wildly inconsistent CSS support. Test generated HTML in Litmus or Email on Acid before relying on it in campaigns.
Tips for the best results
- Start with 64–128 pixels resolution for a good balance of quality and file size.
- Use grayscale mode for striking high-contrast art and slightly smaller files.
- Increase pixel size (4–8px) for a retro pixel-art aesthetic.
- Apply a B&W threshold for logos and icons to get clean, crisp edges.
- Test the HTML file in your target browser to verify rendering performance.
- Keep the generated element contained — use
overflow: hiddento prevent shadows overlapping content.
Try it yourself
The easiest way to get a feel for how box-shadow images behave is to convert one of your own. Head to the Image2CSS converter and drop in an image — a simple logo is the best starting point. Download the ZIP, open the index.html in any browser, and inspect the source to see the CSS.
