I recently worked with a boutique owner who cared about typography more than any client I have had. They sent me a screenshot of their new landing page with the link underlines circled. The underlines ran a hair past the vertical stems of the letters, maybe 2px of misalignment. To them it was a “total mess.”
My first move was the one I have made a thousand times: kill the native underline with text-decoration: none, then rebuild it as an ::after with a custom background. That held up until the client bumped the font size on mobile and the pseudo-element drifted out of position. Then they added a second line of text and the whole thing collapsed, because a pseudo-element has no native sense of where a line break falls. Maintaining that across an entire WooCommerce shop was not something I wanted to sign up for.
Using text-decoration-inset instead
The fix is a fairly new CSS property called text-decoration-inset. It behaves like padding, except it applies to your text decorations rather than the box. Give it a value and the underline gets clipped from the left or the right side, with no extra spans and no absolute positioning. There is a good write-up of the vertical misalignment problem it solves over at css-tricks.com.
The syntax takes two values, one for the left inset and one for the right. Use em units so the effect scales with your font size. Hardcoded pixels in a responsive WordPress theme will come back to bite you the first time a breakpoint changes the type scale.
/* bbioon-custom-link-styles */
.bbioon-fancy-link {
text-decoration-line: underline;
text-decoration-thickness: 2px;
text-underline-offset: 0.2em;
/* Clipping the underline 0.5em from each side */
text-decoration-inset: 0.5em 0.5em;
}
Animating the underline natively
Transitions are where the property earns its keep. A “shooting star” underline normally sends you back to pseudo-elements and transform: scaleX(). With text-decoration-inset you animate the inset values directly, and the underline stays tied to the baseline metrics of the font while it moves. That is why it looks better than the pseudo-element version.
.bbioon-animated-link {
text-decoration: underline;
text-decoration-inset: 0.1em 0.1em;
transition: text-decoration-inset 0.3s ease-in-out;
}
.bbioon-animated-link:hover {
/* Underline shrinks toward the center on hover */
text-decoration-inset: 2em 2em;
}
Browser support
Here is the catch. Support today is basically Firefox 146 and up. Try it in Chrome and you get nothing at all. I still use it, because this is exactly the kind of thing progressive enhancement is for. Most visitors see a normal, clean underline. The ones on a browser that implements the property get the tighter version.
Balancing a designer’s demands against code you still want to maintain gets messy fast. If you are tired of debugging someone else’s CSS and you just want the site to work and look right while it does, drop my team a line. We have probably seen your particular mess before.
What to take away
- Native
text-decoration-insetis less code to maintain than a pseudo-element underline, and it performs better. - Use
emunits so the decoration scales with the typography. - New CSS is fine to ship as long as you treat it as an enhancement rather than a requirement.
- Clients who care about typography do notice details at this level.