Available For Front-End & Design Systems Contracts – Get In Touch

Always Twisted

Underlining Links With CSS

Every time I need to style link underlines beyond the default browser styling, I find myself searching MDN for the same properties. I always think "surely I know this by now?" but then it slips my mind and off I go searching again. So, I've decided to write this down as my personal reference guide. Hopefully, it'll save you the same trip to the documentation!

For years, styling underlines on links meant either accepting the default look or hiding underlines entirely with text-decoration: none; and relying on other styling methods like borders or box-shadows to indicate interactivity. But CSS now gives us a whole set of text-decoration properties that let us get really specific about how our underlines should look and behave.

The Text Decoration Properties

The "modern approach" to link underlines can use a set of individual text-decoration properties. These properties give us granular control over every part of an underline.

Let's walk through each one.

Historically, text-decoration let you choose the type of decoration (none, underline, overline, line-through), but you couldn't style it. The underline would always be the browser's default—solid, in the text color, with a fixed thickness. Now, with the individual text-decoration properties, you get control over the appearance. Using these properties with progressive enhancement ensures that all browsers should get a usable underline, while newer browsers get the enhanced styling.

text-decoration

The text-decoration shorthand lets you set multiple underline properties in one declaration. For example:

Code languagecss
a {
  text-decoration: underline solid red 2px;
}

In this example, we're saying: add an underline, make it solid, color it red, and give it a thickness of 2 pixels.

text-decoration-line

Use text-decoration-line when you want to control what type of line appears. You've got a few options beyond just underline:

Code languagecss
a {
  text-decoration-line: underline;
}

You can also use overline (line above the text), line-through (for strikethrough), none (no decoration), or even combine them like underline overline for multiple lines.

text-decoration-color

Want your underlines to be a different color than your text? That's where text-decoration-color comes in.

Code languagecss
a {
  text-decoration-line: underline;
  text-decoration-color: #ea215a;
}

This is particularly useful when you want a more subtle or contrasting underline color. For instance, you might want muted underlines on dark text that pop on hover.

text-decoration-style

The text-decoration-style property controls how the line is drawn. Beyond the standard solid line, you can make it:

Code languagecss
a {
  text-decoration-line: underline;
  text-decoration-style: wavy;
}

A wavy underline can add personality, or you might use dotted for a more playful feel.

The available styles are: solid (default), double, dotted, dashed, and wavy.

text-decoration-thickness

By default, browsers choose a thickness for underlines. But text-decoration-thickness lets you be precise. You can use any length unit (px, em, rem) or percentages:

Code languagecss
a {
  text-decoration-line: underline;
  text-decoration-thickness: 0.125em;
}

Using relative units like em is good here because the underline thickness can scale with the font size.

text-underline-offset

One of the cool new properties, text-underline-offset controls the space between your text and the underline itself. This is great for giving underlines more "breathing room":

Code languagecss
a {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

You can also use negative values to bring the underline closer or above the baseline if you really wanted something unconventional.

The text-underline-offset property only affects underlines, not overlines or line-through decorations.

text-underline-position

The text-underline-position property is mostly useful if you're working with text that has descenders (like g, j, p, q, y). By default, browsers may skip over these descenders to keep underlines readable. You can force different behaviour:

Code languagecss
a {
  text-decoration: underline;
  text-underline-position: under;
}

The under value forces the underline to sit below all the descenders, giving you a consistent baseline.

text-decoration-skip-ink

Speaking of descenders, text-decoration-skip-ink controls whether underlines skip over letter descenders:

Code languagecss
a {
  text-decoration: underline;
  text-decoration-skip-ink: auto;
}

Most of the time, auto is fine, it lets the browser skip over descenders for better readability. But if you want the underline to go through everything, use none.

Before You Ship

A couple of important things to keep in mind:

text-underline-offset, text-underline-position, and text-decoration-skip-ink are separate properties and aren't included in the text-decoration shorthand. If you use the shorthand, define them separately.

Also, when using the shorthand, one gotcha: setting text-decoration resets any previously set text-decoration-thickness unless you include the thickness value in the shorthand itself. It's easier to avoid the shorthand if you're using multiple properties.

Modern CSS text-decoration properties have solid support in current browsers, though some variations may need checking. Always test compatibility and think about progressive enhancement so that anyone on any browser gets as good an experience as possible.

🦋 - likes

    🔄 - reposts

      Like this post on Bluesky

      Is your CSS architecture sustainable and scalable for your current and future projects?

      I can restructure your CSS for better maintainability, consistency, and scalability.

      get in touch!