What CSS underline really is

When people say “CSS underline”, they usually mean “the underline drawn under text”. In CSS, that underline is part of the text-decoration system.

Here’s the key idea:

  • The underline itself is controlled by text-decoration-line (set it to underline).
  • The look of that underline is controlled by properties like text-decoration-color, text-decoration-thickness, text-decoration-style, and text-underline-offset.
  • You can also use the shorthand text-decoration to set multiple things at once.

Let’s start with the simplest underline possible, then we’ll level it up.

.demo {
  text-decoration-line: underline;
}
  
.demo {
  text-decoration-line: none;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 12px;
  max-width: 720px;
}

.demo {
  font-size: 28px;
  font-weight: 700;
  line-height: 1.2;
}

.note {
  font-size: 14px;
  opacity: 0.8;
}
  
Underlines are a text decoration.
Click the CSS snippets to toggle underline on and off.

CSS underline property: the main players

You’ll often see “CSS underline property” in searches, but there isn’t a single property named underline. Instead, you use one of these approaches:

  • Longhand (most explicit): text-decoration-line: underline;
  • Shorthand (most common): text-decoration: underline;

The shorthand is convenient, but longhand is easier to read and tweak when you care about thickness, offset, style, and color.

.demo {
  text-decoration: underline;
}
  
.demo {
  text-decoration-line: underline;
}
  
.demo {
  text-decoration: none;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 720px;
}

.demo {
  font-size: 28px;
  font-weight: 700;
  line-height: 1.25;
}

.small {
  font-size: 14px;
  opacity: 0.85;
}
  
Shorthand vs longhand underline.
Shorthand is quick. Longhand is great when you want fine control.

CSS underline text: the basic underline

The basic underline is just:

  • text-decoration-line: underline;

A very common real-world case is underlining links. You can underline any element containing text, not just <a>.

a {
  text-decoration-line: underline;
}
  
a {
  text-decoration-line: none;
}
  
.highlight {
  text-decoration-line: underline;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

p {
  font-size: 18px;
  line-height: 1.55;
  margin: 0;
}

a {
  color: #1a56db;
  font-weight: 650;
}

.highlight {
  font-weight: 750;
}
  

This is a link inside a paragraph, and this is a non-link underline.

CSS underline color

Underlines don’t have to match the text color. Use text-decoration-color. This is especially useful when you want a “highlight underline” style without changing the text itself.

  • text-decoration-color: hotpink;
  • text-decoration-color: currentColor; (forces it to match text color)
.demo {
  text-decoration-line: underline;
  text-decoration-color: hotpink;
}
  
.demo {
  text-decoration-line: underline;
  text-decoration-color: #22c55e;
}
  
.demo {
  color: #111;
  text-decoration-line: underline;
  text-decoration-color: currentColor;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 30px;
  font-weight: 800;
  line-height: 1.2;
}

.tip {
  font-size: 14px;
  opacity: 0.85;
}
  
Colorful underline, boring text.
Underline color can be independent from the text color.

CSS underline width (thickness)

People often say “underline width”, but what they usually mean is underline thickness. In CSS, that is text-decoration-thickness.

You can use keywords or a length:

  • Keywords: auto, from-font
  • Lengths: 2px, 0.12em, etc.

Tip: em thickness scales with font size, which often looks more consistent across responsive text.

.demo {
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 34px;
  font-weight: 850;
  line-height: 1.1;
}

.help {
  font-size: 14px;
  opacity: 0.85;
}
  
Thicker underline = louder underline.
Use the slider to change text-decoration-thickness.

CSS underline offset (spacing)

If your underline feels too close to the text (or crashes into descenders like g, y, p), you want text-underline-offset.

  • Small values: tight underline
  • Larger values: more breathing room
.demo {
  text-decoration-line: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 34px;
  font-weight: 850;
  line-height: 1.15;
}

.small {
  font-size: 14px;
  opacity: 0.85;
}
  
gyp jqy: watch the descenders.
Adjust text-underline-offset to control spacing.

CSS underline style

Want dotted underlines? Dashed? Wavy? That’s text-decoration-style.

  • solid (default)
  • dotted
  • dashed
  • wavy
.demo {
  text-decoration-line: underline;
  text-decoration-style: solid;
  text-decoration-thickness: 3px;
}
  
.demo {
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 3px;
}
  
.demo {
  text-decoration-line: underline;
  text-decoration-style: dashed;
  text-decoration-thickness: 3px;
}
  
.demo {
  text-decoration-line: underline;
  text-decoration-style: wavy;
  text-decoration-color: #ef4444;
  text-decoration-thickness: 2px;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 34px;
  font-weight: 850;
  line-height: 1.1;
}

.note {
  font-size: 14px;
  opacity: 0.85;
}
  
Underline styles are a vibe.
Try the different styles: solid, dotted, dashed, wavy.

Text-decoration shorthand: underline all-in-one

The text-decoration shorthand can set multiple parts in one line. For example:

  • text-decoration: underline;
  • text-decoration: underline wavy hotpink;

Shorthand is great for quick styling, but longhand is often easier to tweak later (especially when you start adding thickness and offset).

.demo {
  text-decoration: underline;
}
  
.demo {
  text-decoration: underline wavy #a855f7;
}
  
.demo {
  text-decoration: underline dashed #22c55e;
  text-decoration-thickness: 4px;
  text-underline-offset: 7px;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 34px;
  font-weight: 850;
  line-height: 1.1;
}

.note {
  font-size: 14px;
  opacity: 0.85;
}
  
Shorthand is quick, longhand is comfy.
Notice how thickness and offset are still controlled with their own properties.

CSS underline on hover

Hover underlines are popular because they make text feel interactive (especially links). We’ll do two versions:

  1. Animate underline thickness using text-decoration-thickness.
  2. Create a slick “draw-in” underline using a pseudo-element that animates left to right, and also animates out left to right.

Hover underline: animate text-decoration-thickness

This is the simplest “animated underline” trick:

  • Start with a thin underline.
  • On hover, increase text-decoration-thickness.

One small gotcha: not every browser animates every text-decoration detail perfectly. Still, this effect is often “good enough” and very easy to maintain.

a {
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  transition: text-decoration-thickness 200ms ease;
}

a:hover {
text-decoration-thickness: 5px;
} 
a {
  text-decoration-line: underline;
  text-decoration-color: #22c55e;
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
  transition: text-decoration-thickness 250ms ease, text-decoration-color 250ms ease;
}

a:hover {
  text-decoration-thickness: 8px;
  text-decoration-color: #ef4444;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 12px;
  max-width: 760px;
}

p {
  margin: 0;
  font-size: 18px;
  line-height: 1.55;
}

a {
  color: #1a56db;
  font-weight: 700;
}

.hint {
  font-size: 14px;
  opacity: 0.85;
}
  

Hover this link and watch the underline thicken.

Thickness + offset is a great combo for “premium” underlines.

Hover underline: pseudo-element draw-in (and draw-out) animation

If you want a super smooth underline animation (and consistent behavior), a pseudo-element is the classic move. The idea:

  • Wrap the text in a link.
  • Use ::after as the underline bar.
  • Animate it with transform: scaleX().

Let's go for an underline that:

  • Animates in from left to right.
  • Animates out from left to right.
.fancy-link {
  position: relative;
  text-decoration: none;
}
.fancy-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 6px;
  background: linear-gradient(90deg, #22c55e, #3b82f6);
  border-radius: 999px;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 260ms ease;
}
.fancy-link:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}
  
.fancy-link {
  position: relative;
  text-decoration: none;
}
.fancy-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 240ms ease;
}
.fancy-link:hover::after {
  transform: scaleX(1);
  transform-origin: left;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 12px;
  max-width: 760px;
}

p {
  margin: 0;
  font-size: 18px;
  line-height: 1.6;
}

.fancy-link {
  font-size: 28px;
  font-weight: 850;
  color: #111;
  display: inline-block;
}

.sub {
  font-size: 14px;
  opacity: 0.85;
}
  

Hover: Underline that draws in

It animates in left → right and collapses toward the left when hover ends.

Learn more about pseudo-elements in the CSS ::before and ::after Pseudo-Elements Interactive Tutorial.

Extra polish and common gotchas

Gotcha: underline is not a border

Underlines are not borders. That’s why “underline width” isn’t really a thing. You don’t set underline “width” like a line element. You style the decoration: text-decoration-thickness, text-underline-offset, text-decoration-style, and text-decoration-color.

Hover is mouse-only. Keyboard users rely on focus. If you remove default link underlines, add a clear :focus-visible style.

a {
  text-decoration: none;
  font-weight: 750;
}

a:focus-visible {
outline: 3px solid #111;
outline-offset: 4px;
text-decoration-line: underline;
text-decoration-thickness: 4px;
text-underline-offset: 6px;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

p {
  margin: 0;
  font-size: 18px;
  line-height: 1.6;
}

a {
  color: #1a56db;
}
  

Use Tab to focus this link and see the focus-visible underline + outline.

Learn more about link styling in the CSS Link Interactive Tutorial.

Extra: skipping ink on descenders

Sometimes you want the underline to “skip” through descenders (like the tail of a g). That behavior can be influenced by text-decoration-skip-ink.

  • auto often looks nicer for body text.
  • none forces the underline to draw straight through.
.demo {
  text-decoration-line: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 3px;
  text-decoration-skip-ink: auto;
}
  
.demo {
  text-decoration-line: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 3px;
  text-decoration-skip-ink: none;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrapper {
  font-family: ui-sans-serif, system-ui, sans-serif;
  padding: 18px;
  display: grid;
  gap: 10px;
  max-width: 760px;
}

.demo {
  font-size: 34px;
  font-weight: 850;
  line-height: 1.15;
}

.note {
  font-size: 14px;
  opacity: 0.85;
}
  
gyp jqy: skip ink or slice through?
Compare text-decoration-skip-ink: auto vs none.

Quick recap

  • Turn underline on with text-decoration-line: underline (or text-decoration: underline).
  • Set underline color with text-decoration-color.
  • Set underline thickness (the “underline width” people mean) with text-decoration-thickness.
  • Control spacing with text-underline-offset.
  • Change the underline’s style with text-decoration-style.
  • For really smooth hover animations, use a pseudo-element underline with transform: scaleX().

CSS underline conclusion

Underlines are a powerful text decoration that can be styled in many ways. With properties like text-decoration-thickness, text-underline-offset, and text-decoration-style, you can create underlines that perfectly fit your design.