Underlines From the Font
SHOULD: Take underline geometry from the font: `text-underline-position: from-font`, `text-decoration-thickness: from-font`, `text-decoration-skip-ink: auto` — the browser's default position slices through the descenders of g, y, p and j. Colour is the only part of a real `text-decoration` that animates reliably, so transition `text-decoration-color` for a colour hover and build anything richer (a wipe, a grow, a slide) as a separate element animated on `transform: scaleX()`.
Take underline position and thickness from the font's own metrics, and build any animated underline as a separate element
a { text-underline-position: from-font; text-decoration-thickness: from-font; text-decoration-skip-ink: auto; transition: text-decoration-color 150ms; }Bad
Good
Why it matters
Two halves, and the second is the one people miss. The first is placement: a default underline is drawn at a position the browser picks, which at large sizes and in serif faces slices straight through the descenders of g, y, p and j. A well-made font ships its own underline position and thickness in its metrics, and `text-underline-position: from-font` plus `text-decoration-thickness: from-font` tells the browser to use them; `text-decoration-skip-ink: auto` then breaks the line around any descender that still crosses it.
The second half is animation. `text-decoration-thickness`, `text-underline-offset` and `text-decoration-line` are not reliably animatable — a hover transition on them either does nothing or steps in visible jumps. Color is the exception, and the only exception. So the rule is conditional, not absolute: if the hover effect is a color change, use a real `text-decoration` and transition `text-decoration-color`; the moment the effect is a wipe, a grow, or a slide, the underline has to be a separate element (a `::after` box, or a `background-image` line) that you can animate on `transform: scaleX()` — which is compositor-friendly, unlike anything `text-decoration` could have given you.
A dotted underline via `text-decoration-style` remains the conventional hint that a word carries extra information, which is why `abbr` gets `text-decoration: underline dotted` by default.