
How to use text-decoration properties for better links?
- Sajjad
- Typography
- 11 Oct, 2026
To make better links with text-decoration properties, keep the underline but refine it: set text-decoration-thickness to control how heavy the line is, text-underline-offset to move it away from the letters, text-decoration-color to give it a softer or brand colour, and leave text-decoration-skip-ink: auto on so the line breaks around descenders. Together these turn the browser's default underline, which can look heavy and crowded, into one that's clearly visible but easier to read. Add a hover state that changes the thickness or colour, and a strong :focus-visible outline for keyboard users. Keeping an underline on links in body text is also the simplest way to meet WCAG's requirement that links aren't identified by colour alone.
For years, designers removed link underlines because the default looked clumsy and there was no way to adjust it. Modern CSS fixes that, so you no longer have to choose between a visible link and an attractive one. In this article you'll learn each text-decoration property and what it does, how to build a refined underline, how to design hover, focus and visited states, the accessibility rules that apply, how to animate underlines, and how to handle links in navigation and buttons.
The Text Decoration Properties
text-decoration is a shorthand for several longhand properties, and there are related properties that control underline position. Here's the full set:
| Property | What it controls | Example |
|---|---|---|
text-decoration-line | Which line to draw | underline, overline, line-through, none |
text-decoration-style | Line style | solid, double, dotted, dashed, wavy |
text-decoration-color | Line colour | currentColor, #2563eb |
text-decoration-thickness | Line weight | auto, from-font, 0.08em, 2px |
text-underline-offset | Distance from the text | auto, 0.2em |
text-underline-position | Where the underline sits | auto, under, from-font |
text-decoration-skip-ink | Whether the line skips glyph descenders | auto, none, all |
All of these are supported in current versions of Chrome, Edge, Firefox and Safari. The shorthand accepts line, style, colour and, in most browsers, thickness. Because older Safari versions didn't accept thickness inside the shorthand, it's safest to write the thickness as a separate longhand.
text-decoration-thickness
The default thickness depends on the font and browser and is often too heavy for body text or too thin for bold headings. Using em keeps it proportional to the text size:
a {
text-decoration-thickness: 0.08em;
}
from-font uses the thickness recommended by the font designer, if the font file includes one. Many well-made fonts do, so it's a reasonable choice if you trust the font.
text-underline-offset
The default underline sits close to the baseline and runs into descenders. Moving it down a little gives letters room to breathe:
a {
text-underline-offset: 0.18em;
}
Again, em keeps the gap proportional at every size. Very large offsets can make the underline look disconnected from the text, especially with a tight line height, so values between about 0.12em and 0.25em work well.
text-decoration-skip-ink
With auto, the default in all current browsers, the underline breaks where it would cross descenders such as g, j, p, q and y. This is usually what you want, because it keeps letter shapes clear. Set none only if a design needs a continuous line, and test that descenders remain readable.
text-decoration-color
Separating the underline colour from the text colour lets you soften the line without making the text lighter:
a {
color: #1d4ed8;
text-decoration-color: rgb(29 78 216 / 0.4);
}
Using a semi-transparent version of the link colour keeps everything consistent if the colour changes in a dark theme.
Building a Refined Underline
Here's a complete, production-ready link style for article text:
:root {
--link: #1d4ed8;
--link-hover: #1e3a8a;
}
.prose a {
color: var(--link);
text-decoration-line: underline;
text-decoration-thickness: 0.07em;
text-decoration-color: color-mix(in srgb, var(--link) 45%, transparent);
text-underline-offset: 0.18em;
text-decoration-skip-ink: auto;
transition:
text-decoration-color 150ms ease,
text-decoration-thickness 150ms ease;
}
.prose a:hover {
color: var(--link-hover);
text-decoration-color: currentColor;
text-decoration-thickness: 0.12em;
}
<p class="prose">
Read our <a href="/guides/getting-started">getting started guide</a> before
you configure your first project.
</p>
At rest, the link is blue with a lighter, thin underline. On hover, the underline becomes solid and thicker, giving clear feedback without any layout shift. The color-mix() function is supported in all current major browsers; if you need older browser support, use a fixed rgb() value with alpha instead.
Using the Shorthand
If you prefer fewer lines, the shorthand works for line, style and colour:
.prose a {
text-decoration: underline dotted currentColor;
text-decoration-thickness: 1.5px;
text-underline-offset: 3px;
}
A dotted underline is a common convention for abbreviations with a tooltip, so be careful about using it for ordinary links, where it can be confused with an abbr element.
Hover, Focus, Active and Visited States
Links need distinct states so users know where they are and what they've done.
Focus
Keyboard users navigate links with the Tab key, and they need to see which one is focused. WCAG 2.2 Success Criterion 2.4.7 (Focus Visible) requires a visible focus indicator. :focus-visible shows it for keyboard navigation without showing it on every mouse click:
.prose a:focus-visible {
outline: 2px solid var(--link);
outline-offset: 2px;
border-radius: 2px;
text-decoration-color: currentColor;
}
Never remove focus styles with outline: none unless you replace them with something equally visible.
Active
The active state appears while the link is being clicked or tapped. A small change is enough:
.prose a:active {
text-decoration-thickness: 0.16em;
}
Visited
Visited styles help users track which pages they've already read, which is especially useful in long lists of links such as search results or documentation indexes. For privacy reasons, browsers restrict :visited to colour-related properties, including color and text-decoration-color:
.prose a:visited {
color: #6d28d9;
text-decoration-color: rgb(109 40 217 / 0.45);
}
Thickness and offset changes won't apply to visited links, because browsers deliberately ignore them.
Accessibility Requirements for Links
WCAG 2.2 Success Criterion 1.4.1 (Use of Color) says colour must not be the only visual means of conveying information. For links in a block of text, that means a link that differs from surrounding text only by colour is a problem for people who can't distinguish the colours.
There are two ways to meet the requirement:
- Keep an underline, or another non-colour cue: an underline, bold weight or icon on links in body text satisfies it directly. This is the simplest and most robust approach.
- Use colour with sufficient contrast plus a cue on interaction: W3C's technique G183 describes links whose colour has at least a 3:1 contrast ratio with the surrounding text, and which gain an extra visual cue, such as an underline, on hover and focus.
The second approach is harder than it sounds. Finding a link colour that has 4.5:1 contrast with the background (for SC 1.4.3) and 3:1 contrast with black or dark grey body text is often impossible. An underline avoids the problem.
The underline itself doesn't have a strict contrast requirement under WCAG when the link text already has good contrast, but a very faint underline does little for users who rely on it. Keep its colour clearly visible against the background.
Links in Navigation and Buttons
The underline rule mainly applies to links within text. In places where it's obvious that items are links, such as a site navigation bar, a card that's entirely clickable or a button-styled link, removing the underline is common and usually fine:
.site-nav a {
color: inherit;
text-decoration-line: none;
}
.site-nav a:hover,
.site-nav a[aria-current="page"] {
text-decoration-line: underline;
text-decoration-thickness: 2px;
text-underline-offset: 0.4em;
}
The current page link keeps an underline, which gives a non-colour cue for location, and the large offset turns it into a neat indicator bar.
Animating Underlines
Several text-decoration properties are animatable: text-decoration-color, text-decoration-thickness and text-underline-offset can all be transitioned in current browsers. A subtle offset change makes a pleasant hover:
.prose a {
text-underline-offset: 0.15em;
transition: text-underline-offset 150ms ease;
}
.prose a:hover {
text-underline-offset: 0.28em;
}
The Background Gradient Technique
For effects that text-decoration can't produce, such as an underline that grows from left to right, use a background gradient instead:
.fancy-link {
color: inherit;
text-decoration: none;
background-image: linear-gradient(currentColor, currentColor);
background-size: 0% 0.08em;
background-position: 0 100%;
background-repeat: no-repeat;
transition: background-size 250ms ease;
padding-bottom: 0.05em;
}
.fancy-link:hover,
.fancy-link:focus-visible {
background-size: 100% 0.08em;
}
@media (prefers-reduced-motion: reduce) {
.fancy-link {
transition: none;
}
}
This works across multiple lines, because backgrounds on inline elements follow the line boxes. Note the trade-off: it doesn't skip descenders, and at rest there's no underline, so use it for navigation or headings rather than links in body text. The prefers-reduced-motion query respects users who have asked their system to minimise animation.
Other Uses for Text Decoration
The same properties are useful beyond links:
- Spelling and grammar hints: a wavy red underline,
text-decoration: underline wavy #dc2626, imitates the familiar editor convention for marking errors in an interface. - Deleted text:
delelements useline-through. You can soften it withtext-decoration-colorand keep the text readable. - Abbreviations:
abbr[title]elements get a dotted underline in most browsers. Adjust it withtext-underline-offsetto match your link style. - Inserted text:
inselements are underlined by default; you might give them a coloured background instead so they don't look like links.
del {
text-decoration-color: rgb(220 38 38 / 0.7);
text-decoration-thickness: 0.1em;
}
ins {
text-decoration-line: none;
background: #dcfce7;
}
abbr[title] {
text-decoration: underline dotted;
text-underline-offset: 0.2em;
cursor: help;
}
A Quick Audit Script
To find links in your content that have lost their underline, run this in the console on a content-heavy page:
const bare = [...document.querySelectorAll("main p a, main li a")].filter((a) => {
const style = getComputedStyle(a);
return !style.textDecorationLine.includes("underline");
});
console.log(`${bare.length} in-text links without an underline`);
bare.forEach((a) => console.log(a.textContent.trim(), a.href));
2 in-text links without an underline
pricing page https://example.com/pricing
contact us https://example.com/contact
Any link listed is relying on colour alone, so check it against the use of colour criterion.
FAQ: Text Decoration for Links
Links within paragraphs and lists should generally be underlined, because it's the clearest non-colour cue. Links in navigation menus, buttons and clearly clickable cards can go without an underline when their role is obvious from the design.
Use text-decoration-thickness with a small value such as 0.06em or 1px. Using em keeps the thickness proportional to the font size, so it looks right in both body text and headings.
Use text-underline-offset, for example 0.18em. It moves the underline further from the baseline so it no longer crowds descenders.
Yes. Text-decoration-color sets the underline colour independently. A semi-transparent version of the link colour is a common choice.
Browsers restrict the visited pseudo-class to colour properties to protect users' browsing history. You can change color and text-decoration-color on visited links, but not thickness or offset.
It can be. If links in a block of text are distinguished by colour alone, they may fail success criterion 1.4.1. You need either a non-colour cue such as an underline, or a link colour with at least 3:1 contrast against the surrounding text plus an extra cue on hover and focus.
Conclusion
Modern text-decoration properties make it easy to keep link underlines and still have them look good. A slightly thinner line set with text-decoration-thickness, a little more space from text-underline-offset, a softer text-decoration-color and the default skip-ink behaviour give you underlines that are clearly visible and comfortable to read. Hover, focus and visited states then build on that foundation with small, purposeful changes.
Keep underlines on links within text so you never rely on colour alone, give keyboard users a strong :focus-visible style, and save underline-free designs for navigation and buttons where the role is obvious. Use transitions sparingly and respect reduced motion preferences. The result is links that look considered, work for everyone and need very little CSS.


