Toon text typography in CSS instead of 4MB PNGs

A client came to me recently with a “branding emergency.” They wanted their new landing page to have that high-energy 1950s Hanna-Barbera look, and their solution was a 4MB transparent PNG for every header. The site crawled. Worse, none of that toon text typography was actual text, so search engines saw a page with no headings on it. I told them to put the Photoshop brushes away and let CSS do it.

My first instinct was a heavy font with a standard text-shadow on it. It looked okay and completely flat, nothing like the “punch” of a hand-painted title card. That depth comes from layering several shadows at different offsets rather than settling for one. I got into this after a series on Smashing Magazine about how classic cartoons feed into modern CSS.

Layered CSS for toon text typography

That “Yuk-Yuk Duck” look comes from combining several shadows with different blurs. One hard shadow on its own reads like a 2005 WordArt project, and there is no limit on how many you stack. I start with a solid sharp shadow for the lift, then add softer semi-transparent layers over it for light diffusion.

.bbioon-toon-header {
  color: #f7f76d;
  text-shadow: 
    5px 5px 0 #1e1904, 
    0 0 15px rgba(233, 206, 150, 0.5);
}

Then there is text-stroke. Anyone who has tried a thick outline on a font in CSS knows it eats the inside of the letters, and for a long time I assumed SVG filters were the only way out. Then I remembered paint-order. Set it to stroke and the browser draws the outline first, then paints the fill over it, which keeps the glyphs sharp even at a 10px border.

.bbioon-thick-outline {
  -webkit-text-stroke: 6px #7890b5;
  paint-order: stroke;
  color: #eff0cd;
}

Character splitting and the jiggle effect

To sell the effect the text has to “move” as if it were hand-drawn, which means every letter needs its own timing. I have watched devs wrap each letter in a <span> tag by hand, and it turns into a maintenance disaster. I use a small script, call it bbioon-splinter, to do the splitting and keep the markup readable to screen readers. Put aria-label on the parent and aria-hidden on the spans. Skip that and you have shipped a heading no assistive tech can read.

With the letters split, I add a subtle jiggle animation on staggered delays. Keep the rotation small. Past about 4 degrees, people start to feel seasick looking at your header.

@keyframes bbioon_jiggle {
  0%, 100% { transform: rotate(-2deg); }
  50% { transform: rotate(2deg); }
}

.bbioon-char:nth-child(even) {
  animation: bbioon_jiggle 0.5s infinite alternate;
}

Most of it is already in the spec

Giving a design “character” rarely needs a big library or a heavy asset. Most of what you want sits in the CSS spec already, and the work is knowing which properties to stack. Clipping a gradient background to your text, shifting baselines with nth-child, either way the browser does the rendering instead of your build pipeline.

CSS like this gets fiddly fast. If you are tired of debugging someone else’s mess and you just want the site to work, drop my team a line. We have probably seen it before.

If your branded headings are still a folder of PNGs, most of that can move into CSS.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.