v154 · css · text decoration
CSS text-decoration-inset
An underline runs the full width of its text run, and there was no way to say otherwise. text-decoration-inset pulls the ends in — or pushes them out — with a length, a percentage, or two values for the start and end independently. Underline animations stop being background gradients pretending to be underlines.
concepts
-
Inset lab
Both ends of a real underline, driven independently, with the drawn width measured off the rendered box. Negative values extend past the text, which is where the margin-note and callout patterns come from.
-
The reveal effect
The hover underline everyone builds with a background gradient, built instead with a real
text-decoration. Side by side with the gradient version, so you can see what the gradient gets wrong — wrapping, descenders, andtext-decoration-skip-ink. -
Beyond underlines
The property applies to every decoration line. Insetting a line-through makes a strike that stops short of the punctuation; insetting an overline gives a header rule that aligns to the text rather than the box.
why it shipped
Animated underlines are one of the most-implemented effects on the web and almost none of them are underlines. They are a background-image gradient positioned near the baseline, or a pseudo-element with a border, because those are the only things whose width you could control. Both lose everything the real decoration knows: where the baseline is at this font size, how to skip descenders, what happens when the text wraps to two lines, and how to follow the text colour.
text-decoration-inset makes the real thing controllable. One value insets both ends, two values set start and end separately, and negative values extend the line beyond the text.
the API
/* Pull both ends in by 4px. */
a { text-decoration-inset: 4px; }
/* Start and end independently. */
.pull-quote { text-decoration-inset: 0 2em; }
/* Percentages resolve against the text run. Animate to zero for a
reveal that is a real underline the whole way. */
a { text-decoration-inset: 100% 0; transition: text-decoration-inset .25s; }
a:hover { text-decoration-inset: 0; }