Published:

Want better looking text decoration on your site? Check out this easy to use SCSS mixin that makes it easy to underline text with special effects such as skipping descenders.

So what's wrong with standard text decoration?

Built-in text underlining in CSS for the most part works just fine, however, it is one the least customisable aspects of typography online. For example, it will overlap with any descenders such as the tail on a 'g' which can look messy. Also, you are unable to change the colour, thickness or in fact modify it in any way which is very limiting. 

Enter a new method of dealing with underlines

In order to deal with some of these issues, I built a simple to use SCSS mixin which uses a couple of different techniques to build up an underline effect with some extra special effects.

 

Some of these benefits include the following:

  • Spacing around descenders and ascenders 
  • Ability to modify line colour, thickness distance from text
  • Support for animating the above properties

 

Some things which it doesn't support at present:

  • Element must be inline text if line-breaks are required
  • Won't work well if the text is placed over a background image
  • Falls back to standard text decoration on mobile devices due to artifacts
  • Doesn't allow for broken or dotted lines 

How the hover looks using the underlines plugin

So how does it work?

Let's take a look at the full mixin and work through how it works, here is the code:

 


/* 
Properties used
===============
$background          - The background colour the text is on
$color               - The color of the text
$outline-width       - How much of an outline to show around text
$underline-thickness - How thick the underline should be
$underline-position  - How high up the text the underline should be
$outline-above       - Also display the outline above the text, looks good if underline is low
*/

@mixin nice-underline($background, $color, $outline-width, $underline-thickness, $underline-position, $outline-above) {
  display: inline;
  color: $color;

  // Create outline
  $value: ();
  @for $i from 1 through $outline-width {
    $num: $i + px;
    $numNeg: ($i*-1) + px;
    $XShadow: $num 0 $background;
    $XShadowNeg: $numNeg 0 $background;
    $YShadow: 0 $num $background;
    $YShadowNeg: 0 $numNeg $background;
    $TLShadow: $numNeg $numNeg $background;
    $TRShadow: $num $numNeg $background;
    $BLShadow: $numNeg $num $background;
    $BRShadow: $num $num $background;
    $value: append($value, $XShadow, comma);
    $value: append($value, $XShadowNeg, comma);
    $value: append($value, $YShadow, comma);
    $value: append($value, $BLShadow, comma);
    $value: append($value, $BRShadow, comma);

    @if $outline-above {
      $value: append($value, $YShadowNeg, comma);
      $value: append($value, $TLShadow, comma);
      $value: append($value, $TRShadow, comma);
    }
  }
  text-shadow: $value;

  // Create underline
  box-shadow: inset 0 (($underline-position * -1px) + $underline-thickness) 0 0 $background,
    inset 0 ($underline-position * -1px) 0 0 $color;

  // Mobile fix
  @media (max-width: 991px) {
    box-shadow: none;
    text-decoration: underline;
  }
}

 

There are a variety of arguments that can be passed to the mixin, these are defined as the following:

$background
The background colour the text is on
$color
The colour of the text
$outline-width
How much of an outline to show around text
$underline-thickness
How thick the underline should be
$underline-position
How high up the text the underline should be
$outline-above
Also display the outline above the text, looks good if underline is low

 

To give an idea of how this is implemented, this is how the mixin could be included:


@include nice-underline(blue, currentColor, 3, 0, 0, false);

 

The mixin is basically doing two things: 

Firstly, the text outline is built up using a stack of comma separated 'text-shadows' which extend out in every direction from the text. A loop within the mixin adds more outlines depending on how thick the outline around the text should be. This outline is then used to mask off the text underlining as it matches the background colour specified in the mixin call arguments. 

Secondly, two inset box shadows are included which are used to construct the actual underline effect. The way this works is that one shadow matches the background colour and overlaps the other box-shadow to create a line. This can then be set so that the line can be placed further up or down the text.

A working demo

Check out this codepen for a demo of all this in action, I have included a few examples of what kinds of effects could be achieved, although there are many more combinations available. Remember this falls back to regular text-decoration for devices as there is some artifacting around the overlapping shadows.

View demo

A second overview of the SCSS underline tool



Comments