Modern CSS gives us fine-grained control over link underlines via text-decoration-thickness and text-underline-offset. These longhand properties are especially useful when you want to override browser defaults, such as thinning out the heavy underline that ships with the Ubuntu web font:

:any-link {
  text-decoration-thickness: 0.08em;
}
Showing two links, a default and one that decreases the text-decoration-thickness.

But these properties can stop working unexpectedly once you introduce hover-based underlines. A common pattern—removing underlines by default and showing them on hover, as seen in navigation and footers—can silently break your custom thickness setting.

Why Thickness Gets Reset

Consider this typical hover-underline implementation for header links:

header :any-link {
  text-decoration: none;
}

header :any-link:hover {
  text-decoration: underline;
}

Testing this in a browser reveals a problem: the hover underlines render at the default thickness instead of the thinner style you declared earlier. The root cause is that text-decoration is now a shorthand property. Setting it to none or underline resets all four of its component longhands—text-decoration-line, text-decoration-thickness, text-decoration-style, and text-decoration-color—to their initial values, as defined in the CSS Text Decoration module:

The text-decoration property is a shorthand for setting text-decoration-line, text-decoration-thickness, text-decoration-style, and text-decoration-color in one declaration. Omitted values are set to their initial values.

You can verify this behavior in DevTools by inspecting a hyperlink and expanding the text-decoration property in the CSS pane.

DevTools screenshot showing text-decoration styles on the :any-link pseudo-selector.

The fix isn't a single obvious one. You have three main options:

  • Declare text-decoration-thickness after text-decoration in the same rule.
  • Include the thickness value directly in the text-decoration shorthand.
  • Use text-decoration-line instead of the full shorthand.

Evaluating the Options

The quickest fix is simply repeating the thickness declaration in the :hover state:

/* OPTION A */

header :any-link {
  text-decoration: none;
}

header :any-link:hover {
  text-decoration: underline;
  text-decoration-thickness: 0.08em; /* set thickness again */
}

This works, but it's redundant. Since text-decoration is a shorthand, you should be able to bundle the line and thickness together in one declaration:

/* OPTION B */

header :any-link {
  text-decoration: none;
}

header :any-link:hover {
  text-decoration: underline 0.08em; /* set both line and thickness */
}

This syntax is relatively new—text-decoration was only converted into a shorthand in the CSS Text Decoration module. In CSS 2, it was a simple property. And therein lies the problem: Safari hasn't fully caught up. The WebKit engine still requires the prefixed -webkit-text-decoration for the shorthand syntax, and it doesn't support thickness values at all (see WebKit bug 230083). That rules out the shorthand approach for cross-browser code.

Fortunately, there's a cleaner workaround. When text-decoration became a shorthand, the new text-decoration-line longhand took over its old job. Using this property to toggle the underline on hover avoids touching the other three components:

/* OPTION C */

header :any-link {
  text-decoration-line: none;
}

header :any-link:hover {
  text-decoration-line: underline;
}

Because this only updates the line component, the previously declared thickness remains intact. This is the most robust way to implement hover underlines.

Shorthand Side Effects Beyond Underlines

The same principle applies to other shorthand properties. Setting text-decoration: underline re-initializes missing values, just as background: url(flower.jpg) will undo a previously declared background-repeat: no-repeat. When you use a shorthand, always consider which longhand values you might be silently resetting.