CSS letter-spacing animation on a real WordPress site

Isometric machine with tiles sliding apart on a track near a ruler and loudspeaker, next to the words Animate Text Right.

Preethi at CSS-Tricks published Revealing Text With CSS letter-spacing, and at its core is a CSS letter-spacing animation I like a lot. letter-spacing: -1ch stacks every glyph in a word on the same spot, color: transparent hides the pile, and transitioning back to 0ch opens the word up like an accordion. It’s a clever use of a boring property. I would still change two things before it goes anywhere near a client site, and neither one shows up in the demos.

How the reveal works

The mechanics are simple. Set letter-spacing to a large negative value in ch units, where ch is the width of the zero glyph in the current font, and the letters collapse until they sit on top of each other. Transparent paint hides the stack. On a state change you animate the spacing back to 0ch or a small positive value and fade the color in, and the transition does the rest:

.bb-hero-line {
  letter-spacing: -1ch;
  color: transparent;
  transition: letter-spacing 0.6s, color 0.4s;
}

.bb-toggle:checked + .bb-hero-line {
  letter-spacing: 0ch;
  color: var(--bb-ink, #111);
}

Two details from the article are worth keeping. text-align decides where the collapsed bundle sits, so center-aligned text blooms outward from the middle and right-aligned text blooms from the right edge. The swap demo goes further and combines letter-spacing: -2ch with text-indent: -1.5ch and overflow: clip to slide one line out of the box while the next line expands in, with a bouncy cubic-bezier(.8, -.5, .2, 1.4) that overshoots on purpose.

Screen readers and reduced motion

Transparent text is still in the accessibility tree. The swap demo has two spans, one visible at a time to sighted users, and a screen reader gets both strings back to back. Someone tested this with VoiceOver in the comments on the article, and the article itself flags the gap in its own markup:

Simplified for brevity; additional accessibility considerations

So the shipped version needs aria-hidden=”true” on whichever span is decorative, and the meaning has to survive in the one that stays exposed. If both strings carry real meaning, this effect is the wrong tool and a plain swap is better. Collapsed text is also still selectable and still matches browser find-in-page. Whether that’s a feature or a bug depends on the design.

The other gap is motion. Collapsing and exploding a word is a big movement, and nothing in the demo checks prefers-reduced-motion. MDN documents the two values, no-preference and reduce, and the progressive pattern is to opt in rather than opt out:

/* Motion only for people who have not asked for less. */
@media (prefers-reduced-motion: no-preference) {
  .bb-hero-line {
    transition: letter-spacing 0.6s, color 0.4s;
  }
}

The transitions live inside that block. Outside it the text just switches state instantly, and people who asked for less motion still get the content.

The reflow cost of a CSS letter-spacing animation

MDN lists letter-spacing as animatable with animation type a length, applying to all elements, and inherited. That animation type is the expensive bit. Animating a length on text changes the width of every inline box on every frame, so the browser relayouts the line each time. With transform or opacity, frames can go to the compositor without touching layout at all. For one hero heading over 0.6s nobody will notice. For a grid of twenty product titles animating on hover, you’re paying for layout on every frame of every one of them.

The inherited part has its own failure mode. A negative letter-spacing on a container cascades into everything inside it, so one stray rule on a wrapper quietly ruins nested elements until somebody traces it back. And because ch tracks the zero glyph, -1ch fully overlaps the letters in one typeface and leaves slivers in a narrower one, which means a demo tuned on its font needs retuning on yours.

Where I would use it on a WordPress site

In a block theme I’d keep this in the theme stylesheet, scoped to a class the editor can’t easily break, rather than trying to express it as editor styles. Good placements are the single hero line on the homepage, a brand or acronym line that expands on hover, and a section label that reveals on scroll. I’ve written about keeping ambient animations away from your LCP before, and the same rule applies here. The compositor argument is also the one behind preferring rotateZ over rotate() for animation performance.

I’d keep it off nav links, buttons, form labels, and anything where the collapsed state hides information the user needs, and out of WooCommerce product loops entirely. The two-span swap is the most fragile demo of the three in a CMS, because an editor rearranging spans breaks the nth-of-type selectors, and content authors do rearrange things. Hover and scroll triggers survive editing better than sibling-position selectors do.

Before you ship it

If you want an effect like this on a store or a client theme, and it has to survive a screen reader pass and a performance review, that’s the kind of front-end work I take on. Send me the design and I’ll wire it into the theme.

Open a page with any letter-spacing transition, open DevTools, go to the Rendering panel, set Emulate CSS media feature prefers-reduced-motion to reduce, and reload. If the text still animates, you’ve found the bug described here, and the fix is the media query above.

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.

Leave a Comment