How to Underline Text with CSS & Customize the Style

Jon Lehman
9 min readNov 15, 2022

--

We’re most often used to a text underline to indicate a hyperlink. But, what if you’re after a super fancy styled underline? Or maybe not an underline at all, perhaps you want an overline or a strike-through? We’ll cover all of the options. Stayed tuned until the end where we combine all of the CSS styles together for some fun animations/interactions!

Really quickly before getting into it, if you find this article helpful it would mean the world to me if you follow me on Medium. This will help me be able to support more of these articles. 🙏 Thank you!

🤿 Let’s dive right in!

CSS Text-decoration Sticker Sheet

Feel free to reference this comprehensive “sticker sheet” that I put together on codepen:

Note that browsers and devices render text-decoration differently. Your results may differ. Check out the gifs included below to see the intended rendering.

How to underline text with CSS

You may be wondering “why not to use the HTML <u> tag?”. This tag is often referred to as the “underline tag”. However, it’s actually the “Unarticulated Annotation tag”. Using this tag purely to render the nested text with an underline is considered a misuse of the tag. Feel free to read more about it here: MDN Docs: HTML u tag.

<p>Try not to use the <u>Unarticulated Annotation Tag</u> purely for rendering text with an underline.</p>

Ok, so don’t use the <u> HTML tag, then how do I add an underline to my text? For a simple underline, it’s super easy. Just add the following CSS style:

text-decoration: underline;

That’s the shorthand version; the longer version would look something like:

/* "Solid" is the default value for the style */
text-decoration-line: underline;
text-decoration-style: solid;

Dotted underline with CSS

Want a dotted line instead of a solid one? No problem, that’s where using the text-decoration-style CSS style comes in handy.

/* Shorthand version: */
text-decoration: underline dotted;

/* Long version: */
text-decoration-line: underline;
text-decoration-style: dotted;

Dashed underline with CSS

How about a dashed line? Sure thing!

/* Shorthand version: */
text-decoration: underline dashed;

/* Long version: */
text-decoration-line: underline;
text-decoration-style: dashed;

Wavy underline with CSS

Feelin’ a bit groovy? Add some wave to your underline!

/* Shorthand version: */
text-decoration: underline wavy;

/* Long version: */
text-decoration-line: underline;
text-decoration-style: wavy;

Double underline with CSS

Maybe the basic (solid) underline is the style you’re after but you need more emphasis? Try the double underline!

/* Shorthand version: */
text-decoration: underline double;

/* Long version: */
text-decoration-line: underline;
text-decoration-style: double;

How to change underline color, placement, offset, and thickness with CSS

Turns out you can customize more than just the underline style! CSS gives us a bunch of options to tailor how the underline is rendered to fit most any use-case.

Change underline color with CSS

By default the underline will match the color of the text, but this may not be desired. There a couple of different ways to set color. Here’s an example of setting the underline to an orange-ish color in different ways.

/* Shorthand using HTML Color Name: */
text-decoration: underline tomato;

/* Long version with different ways to define color: */

text-decoration-line: underline;

/* Using HTML Color Names */
text-decoration-color: tomato;
/* Using Hex Color Codes */
text-decoration-color: #FF6347;
/* Using RGB Color Codes */
text-decoration-color: rgb(255, 99, 71);
/* Using HSL Color Codes */
text-decoration-color: hsl(9, 100%, 64%)

Change underline placement with CSS

Turns out an underline doesn’t actually have to be under the text either! You can change the placement of the “under”-line to be an overline or a line-through (aka strike-through).

One thing more is that you can use multiple of these together; I included a couple examples of this in the codepen stickersheet.

Here’s an example of an overline:

text-decoration-line: overline;

and an example of a line-through (or “strike-through”):

text-decoration-line: line-through

Change underline offset with CSS

You can also adjust how far underneath the underline is from the text. This is called the underline offset. Note that the offset will only work for the underline placement, not overline or line-through.

text-decoration-line: underline;
text-underline-offset: 8px;

/* You also use other units of space: */
text-underline-offset: 100%;
text-underline-offset: 2rem;

/* Auto is the default */
text-underline-offset: auto;

/* From-font is similar to auto but instead of the browser choosing the underline offset, the font does. If the font does not specify, it will behave like auto */
text-underline-offset: from-font;

Change underline thickness with CSS

Last but not least, you can also control the thickness of the line. Contrary to offset this works for underline, overline, and line-through. It also works for all the different line styles (dotted, dashed, wavy, etc).

text-decoration-line: underline;
text-decoration-thickness: 8px;

/* You also use other units of space: */
text-decoration-thickness: 100%;
text-decoration-thickness: 2rem;

/* Auto is the default */
text-decoration-thickness: auto;

/* From-font is similar to auto but instead of the browser choosing the underline offset, the font does. If the font does not specify, it will behave like auto */
text-decoration-thickness: from-font;

Now let’s have some fun combining all that we covered!

Taking what we have learned with the text-decoration styles and sprinkling in some basic CSS animations and interactions can create really fun results!

It’s worth mentioning that all of these examples would be more simple using a CSS preprocessor like SASS or utilizing some javascript. To keep things consistent, I am just using vanilla CSS.

Meteor Shower

For this first animation let’s animate the underline color to make a meteor or shooting star effect. To do this, I nested each letter in its own <span>. Next, I setup the basic color animation using CSS keyframes. Lastly, I incremented each span with +100ms of animation delay.

<p class="ms">
<span>M</span><span>e</span><span>t</span><span>e</span><span>o</span><span>r</span><span> </span><span>s</span><span>h</span><span>o</span><span>w</span><span>e</span><span>r</span>
<p>
.ms span {
text-decoration: underline 2px black;
text-underline-offset: 8px;
animation: meteor-shower-anim linear 1400ms infinite forwards
}

.ms span:nth-of-type(1) {animation-delay: 0ms}
.ms span:nth-of-type(2) {animation-delay: 100ms}
.ms span:nth-of-type(3) {animation-delay: 200ms}
.ms span:nth-of-type(4) {animation-delay: 300ms}
.ms span:nth-of-type(5) {animation-delay: 400ms}
.ms span:nth-of-type(6) {animation-delay: 500ms}
.ms span:nth-of-type(7) {animation-delay: 600ms}
.ms span:nth-of-type(8) {animation-delay: 700ms}
.ms span:nth-of-type(9) {animation-delay: 800ms}
.ms span:nth-of-type(10) {animation-delay: 900ms}
.ms span:nth-of-type(11) {animation-delay: 1000ms}
.ms span:nth-of-type(12) {animation-delay: 1100ms}
.ms span:nth-of-type(13) {animation-delay: 1200ms}

@keyframes meteor-shower-anim {
0% { text-decoration-color: DarkOrange }
10% { text-decoration-color: LightYellow }
70% { text-decoration-color: black }
}

Rainbow Rings

Let’s see what we can do if we hide the text all together. For this one we’re using a dotted line-through decoration with a thickness of 100%. The animation is similar to Meteor Shower where we’re animating the decoration color.

<p class="rr">
<span>o</span><span>o</span><span>o</span><span>o</span><span>o</span><span>o</span><span>o</span><span>o</span><span>o</span><span>o</span>
<p>
.rr span {
text-decoration: line-through 100% dotted Salmon;
text-underline-offset: 8px;
animation: rainbow-rings-anim linear 4000ms infinite;
}

.rr span:nth-of-type(1) {animation-delay: 0ms}
.rr span:nth-of-type(2) {animation-delay: 100ms}
.rr span:nth-of-type(3) {animation-delay: 200ms}
.rr span:nth-of-type(4) {animation-delay: 300ms}
.rr span:nth-of-type(5) {animation-delay: 400ms}
.rr span:nth-of-type(6) {animation-delay: 500ms}
.rr span:nth-of-type(7) {animation-delay: 600ms}
.rr span:nth-of-type(8) {animation-delay: 700ms}
.rr span:nth-of-type(9) {animation-delay: 800ms}
.rr span:nth-of-type(10) {animation-delay: 900ms}

@keyframes rainbow-rings-anim {
0% { text-decoration-color: Salmon }
12.5% { text-decoration-color: LightSalmon }
25% { text-decoration-color: Gold }
37.5% { text-decoration-color: PaleGreen }
50% { text-decoration-color: Turquoise }
62.5% { text-decoration-color: MediumPurple }
75% { text-decoration-color: Violet }
87.5% { text-decoration-color: MediumVioletRed }
}

Wavy waves

This animation is very similar to both of the previous animations but with a couple of additions. On top of changing the text-decoration-color, we’re also changing the text-decoration-style and text-underline-offset over time to create a wave-like effect.

<p class="ww">
<span>W</span><span>a</span><span>v</span><span>y</span><span> </span><span>w</span><span>a</span><span>v</span><span>e</span><span>s</span>
<p>
.ww span {
display: inline-block;
text-decoration: underline 3px RoyalBlue;
text-underline-offset: 12px;
animation: wavy-waves-anim linear 3000ms infinite;
}

.ww span:nth-of-type(1) {animation-delay: 0ms}
.ww span:nth-of-type(2) {animation-delay: 100ms}
.ww span:nth-of-type(3) {animation-delay: 200ms}
.ww span:nth-of-type(4) {animation-delay: 300ms}
.ww span:nth-of-type(5) {animation-delay: 400ms}
.ww span:nth-of-type(6) {animation-delay: 500ms}
.ww span:nth-of-type(7) {animation-delay: 600ms}
.ww span:nth-of-type(8) {animation-delay: 700ms}
.ww span:nth-of-type(9) {animation-delay: 800ms}
.ww span:nth-of-type(10) {animation-delay: 900ms}

@keyframes wavy-waves-anim {
0% { text-decoration-style: wavy; text-underline-offset: 8px; text-decoration-color: PowderBlue }
5% { text-decoration-style: wavy; text-underline-offset: 0px; text-decoration-color: RoyalBlue }
25% { text-decoration-style: solid; text-underline-offset: 12px }
37.5% { text-decoration-color: PowderBlue }
45% { text-decoration-color: RoyalBlue }
87.5% { text-decoration-color: RoyalBlue }
98% { text-decoration-color: PowderBlue}
}

Redacted Text

Ok, last silly example. Instead of an animation, what if we added interactivity to reveal on text on hover? This one is more simple, similar to the Rainbow Rings using a thickness of 100%, but we remove all text-decoration on hover.

<p class="rt">Hover me for <span>shocking</span> redacted text.</p>
.rt span {
text-decoration: line-through solid black 100%;
}

.rt span:hover {
text-decoration: none
}

Closing thoughts

Whew. That was probably more than anyone wants to read about CSS text-decoration. I just wanted to mention again that text-decorations are rendered quite a bit differently depending on your device and browser. I recommend testing any text-decoration use (especially the fancy implementations) before publishing.

👋 If you’ve made it this far, and you found this article helpful please consider following me on Medium! Following me on Medium will help support more articles like this one!

--

--