Underline learned the hard way
I love typography, beautiful typography. This is one of the most important points , if not the most when designing a site. There is nothing better than a proper font that makes reading text fluid and easy.
In the age of the Internet, there is a player to take account : underline.
The problem is that each browser has its own way of displaying an underlined. Chrome displays thick lines intersecting feet letters. And Safari displays fine lines that stops before each foot.
How can we manage to have the same underline’s style everywhere ? Why this is not the fonts who manages the display of underlining ? That way, they would be all the same across all browsers.
Well, this is a legacy of the era of font case and paper. At that time there was simply no underline. And it is considered as too distracting.
Professional Western typesetting usually does not employ lines under letters for emphasis within running text, because it is considered too distracting. Underlining is, however, often used with typewriters, in handwriting and with some non-alphabetic scripts. It is also used for secondary emphasis, i.e. marks added by the reader and not the author.
— Emphasis (typography) on Wikipedia
In printed documents underlining is generally avoided, with italics or small caps often used instead, or (especially in headings) using capitalization or bold type.
— Underline on Wikipedia
Medium as done a really great job to unify and beautify their underlines. After reading how Medium is crafting link underlines, I wanted to do the same for this site.
As I am picky, CSS should not depend on any library or framework like Compass . As you can imagine, I had to face some problems.
Problems
Browser doesn’t like dimension thinner than 1px
At the beginning, I wanted to use em
as the calculation unit.
But when testing, the underline doesn’t appear on some browser.
Due to the fact I wanted extra thin lines, I was tempted to use some width like 0.04em
. The problem is that 0.04em
is less than 1px. Actually, it’s 0.64px
width. And some browser does not like that.
Some browser will not show line that are under 1px. Have a look at this little test:
Yeah, Firefox is the only browser who show a line thinner than 1px.
The conclusion is that 1px
is the smallest value that the line can have. Best thing you can do about that, is adding a warning.
The first step is to convert em
to px
.
For that, you have to multiply the em
size by the browsers default font size wich is 16px
.
0.04em * 16px = 0.64px
If you haven’t set the font size anywhere on the page, then it is the browser default, which is probably 16px.
‐font-size
property
Then, add some conditions to display a warning if the underline thickness is thinner than 1px.
// warn if width is thinner than 1px
@if unit($width)==px {
@if $width < 1px {
@warn "Underline is thinner than 1px. Some browsers may not display it."
}
}
@if unit($width)==em {
@if px($width) < 1px {
@warn "Underline is thinner than 1px. Some browsers may not display it."
}
}
px
or em
?
This is more a personal choice.
I have choose to do all the calculation in em
because it let me deal with proportion. It means that big text like headings will have a thicker underlines than basic text. It’s more elegant than having identical thicknesses for all fonts sizes.
There is not « one style that ruled them all »
Each font must have is own underline style.
If you want to apply this kind of nice underlines and use more than one font, be careful. You may have to set a style for each fonts.
Almost bulletproof line cutting with text-shadow
The purpose of the text-shadow
is to draw a zone around the text. This zone will hide the underline so it does not cut the fonts feet.
One shadow in each eight directions
To draw this zone we set a shadow in eight directions.
- 1px to top
- 1px to top-left
- 1px to left
- 1px to bottom-left
- 1px to bottom
- 1px to bottom-right
- 1px to right
- 1px to top-right
Here is the CSS:
text-shadow:
0 1px 0 $bg-color,
0 -1px 0 $bg-color,
1px 0 0 $bg-color,
-1px 0 0 $bg-color,
-1px -1px 0 $bg-color,
-1px 1px 0 $bg-color,
1px -1px 0 $bg-color,
1px 1px 0 $bg-color;
As you can see, the result may not be perfect with every font. We need something more accurate.
Multis shadows on the eights axis
The second test was to draw more than one shadow on each of the eights axis.
text-shadow:
0 $cut-distance*1/5 0 $bg-color,
0 $cut-distance*2/5 0 $bg-color,
0 $cut-distance*3/5 0 $bg-color,
0 $cut-distance*4/5 0 $bg-color,
0 $cut-distance 0 $bg-color,
0 (-$cut-distance)*1/5 0 $bg-color,
0 (-$cut-distance)*2/5 0 $bg-color,
0 (-$cut-distance)*3/5 0 $bg-color,
0 (-$cut-distance)*4/5 0 $bg-color,
0 (-$cut-distance) 0 $bg-color,
$cut-distance*1/5 0 0 $bg-color,
$cut-distance*2/5 0 0 $bg-color,
$cut-distance*3/5 0 0 $bg-color,
$cut-distance*4/5 0 0 $bg-color,
$cut-distance 0 0 $bg-color,
(-$cut-distance)*1/5 0 0 $bg-color,
(-$cut-distance)*2/5 0 0 $bg-color,
(-$cut-distance)*3/5 0 0 $bg-color,
(-$cut-distance)*4/5 0 0 $bg-color,
(-$cut-distance) 0 0 $bg-color,
(-$cut-distance)*1/5 $cut-distance*1/5 0 $bg-color,
(-$cut-distance)*2/5 $cut-distance*2/5 0 $bg-color,
(-$cut-distance)*3/5 $cut-distance*3/5 0 $bg-color,
(-$cut-distance)*4/5 $cut-distance*4/5 0 $bg-color,
(-$cut-distance) $cut-distance 0 $bg-color,
(-$cut-distance)*1/5 (-$cut-distance)*1/5 0 $bg-color,
(-$cut-distance)*2/5 (-$cut-distance)*2/5 0 $bg-color,
(-$cut-distance)*3/5 (-$cut-distance)*3/5 0 $bg-color,
(-$cut-distance)*4/5 (-$cut-distance)*4/5 0 $bg-color,
(-$cut-distance) (-$cut-distance) 0 $bg-color,
$cut-distance*1/5 (-$cut-distance)*1/5 0 $bg-color,
$cut-distance*2/5 (-$cut-distance)*2/5 0 $bg-color,
$cut-distance*3/5 (-$cut-distance)*3/5 0 $bg-color,
$cut-distance*4/5 (-$cut-distance)*4/5 0 $bg-color,
$cut-distance (-$cut-distance) 0 $bg-color,
$cut-distance*1/5 $cut-distance*1/5 0 $bg-color,
$cut-distance*2/5 $cut-distance*2/5 0 $bg-color,
$cut-distance*3/5 $cut-distance*3/5 0 $bg-color,
$cut-distance*4/5 $cut-distance*4/5 0 $bg-color,
$cut-distance $cut-distance 0 $bg-color;
The result was better but we can do better.
The matrix
Here is an exemple of a matrix of 25 shadows.
text-shadow:
-1px -1px 0 #fff,
-1px -0.5px 0 #fff,
-1px 0px 0 #fff,
-1px 0.5px 0 #fff,
-1px 1px 0 #fff,
-0.5px -1px 0 #fff,
-0.5px -0.5px 0 #fff,
-0.5px 0px 0 #fff,
-0.5px 0.5px 0 #fff,
-0.5px 1px 0 #fff,
0px -1px 0 #fff,
0px -0.5px 0 #fff,
0px 0px 0 #fff,
0px 0.5px 0 #fff,
0px 1px 0 #fff,
0.5px -1px 0 #fff,
0.5px -0.5px 0 #fff,
0.5px 0px 0 #fff,
0.5px 0.5px 0 #fff,
0.5px 1px 0 #fff,
1px -1px 0 #fff,
1px -0.5px 0 #fff,
1px 0px 0 #fff,
1px 0.5px 0 #fff,
1px 1px 0 #fff;
This is how I get the CSS in Sass.
// text-shadow
$precision: 5;
$all: ();
$delta-distance: $underline-shadow-width * 2 / ($precision - 1);
// from left to right
@for $i from 0 through ($precision - 1) {
// from top to bottom
@for $y from 0 through ($precision - 1){
$all: append($all, (-$underline-shadow-width)+$delta-distance*$i ((-$underline-shadow-width)+$delta-distance*$y) 0 $underline-background-color, comma)
}
}
text-shadow: $all;
And yeah, the result is pretty decent.
Underline lowercase number
Something to be careful with are lowercase number. The underline of the number 3 can be invisible. There is no miracle. You have to be picky with that !
Awkward background color when selected
A thing to fix is the color of the background when you select underline text. If you do nothing, it will be the same as the page background color. And it will do strange things when text is selected.
To fix that, you just have to add style on the selection. Thanks to the ::selection
CSS pseudo-element.
.underline {
// nice underlines
@include underline();
// When underline text is selected, the shadow color is the same as
// the selection color.
&::selection {
@include underline($base-text-color, 4px, 1px, lighten($base-primary-color, 30%));
}
}
Here is the result:
Things to know
Selected text may have some artifacts when selected
On text selection, you can have some background exceedance.
When selected, the underline is not the same color as the text
The reason, is simple. The underline (made of background-color
) is behind the selection layer.
The code
All the code is available on Codepen.
See the Pen Underline by Cédric Aellen (@alienlebarge) on CodePen.
Inuit-underline
For Inuitcss users, Inuit-underline is a collection of Sass files to include in your project.