CSS Text underlining too long when letter-spacing is applied?

waffl picture waffl · Oct 25, 2010 · Viewed 9.8k times · Source


Whenever letter-spacing is applied to something with an underline or a bottom border, it seems like the underline extends beyond the text on the right. Is there any way to do prevent the underline from extending beyond the last letter in the text?

For example:

<span style="letter-spacing: 1em; border-bottom: 1px solid black;">Test</span>

Perhaps it'd be possible with a fixed width <div> and a border, but that is really not an option to surround every underlined element.

Thanks!

Answer

nealio82 picture nealio82 · Dec 5, 2011

It's not perfect, but the best solution I have come up with so far is to mask it with a :after pseudo element. This way there's no need for extra elements throughout your text.

For example:

h1 {
  /* A nice big spacing so you can see the effect */
  letter-spacing: 1em;
  /* we need relative positioning here so our pseudo element stays within h1's box */
  position: relative;
  /* centring text throws up another issue, which we'll address in a moment */
  text-align: center;
  /* the underline */
  text-decoration: underline;
}

h1:after {
  /* absolute positioning keeps it within h1's relative positioned box, takes it out of the document flow and forces a block-style display */
  position: absolute;
  /* the same width as our letter-spacing property on the h1 element */
  width: 1em;
  /* we need to make sure our 'mask' is tall enough to hide the underline. For my own purpose 200% was enough, but you can play and see what suits you */
  height: 200%;
  /* set the background colour to the same as whatever the background colour is behind your element. I've used a red box here so you can see it on your page before you change the colour ;) */
  background-color: #990000;
  /* give the browser some text to render (if you're familiar with clearing floats like this, you should understand why this is important) */
  content: ".";
  /* hide the dynamic text you've just added off the screen somewhere */
  text-indent: -9999em;
  /* this is the magic part - pull the mask off the left and hide the underline beneath */
  margin-left: -1em;
}

<h1>My letter-spaced, underlined element!</h1>

And that's it!

You can also use borders if you want finer control over colour, positioning, etc but these require you to add a span element unless you have a fixed width.

For example, I'm currently working on a site which requires h3 elements to have 2px letter spacing, centred text and an underline with added space between the text and the underline. My css is as follows:

h3.location {
  letter-spacing: 2px;
  position: relative;
  text-align: center;
  font-variant: small-caps;
  font-weight: normal;
  margin-top: 50px;
}

h3.location span {
  padding-bottom: 2px;
  border-bottom: 1px #000000 solid;
}

h3.location:after {
  position: absolute;
  width: 2px;
  height: 200%;
  background-color: #f2f2f2;
  content: ".";
  text-indent: -9999em;
  margin-left: -2px;
}

and my HTML is:

<h3><span>Heading</span></h3>

Notes:

It's not 100% pretty CSS but it does at least mean you don't have to modify & hack your HTML to achieve the same result.

I haven't yet had to try this on an element with a background image, so haven't yet thought of a method of achieving this.

Centring text makes the browser display the text in the wrong place (it accounts for the text + extra spacing afterwards), so everything is pulled left. Adding a text-indent 0.5em (half the 1em letter spacing we used in the top example) directly on the h1 element (not the :after pseudo element) should fix this, though I've not tested this yet.

Any feedback is gratefully received!

Neal