Hacky styling of :visited links

Hacky styling of :visited links

This post outlines a method to ‘hide’ a glyph on an anchor tag using a pseudo-element in order to expand the boundaries of what :visited styles typically allow.

In HTML, <a> tags have 4 CSS pseudo classes that are used to style hyperlinks in different states: :link :visited :hover and :active.

We used to be able to style these states with whatever CSS we wanted, but in early 2010 a privacy vulnerability forced browsers to restrict what properties were allowed to be used with the :visited selector.

Here’s what we’re left with:

a:visited {
  outline-color: ;
  border-color: ;
  background-color: ;
  color: ;
}

Boring, eh? Not to worry, we can work with this.

First, we’ll create a link and give it some styling. Note: I’m using Sass here instead of vanilla CSS1.

<a href="#!" class="demo">Link</a>
$buttonColor: #3ba7bb;

a.demo {
  background: $buttonColor;
  border-radius: 4px;
  border: 2px solid $buttonColor;
  color: white;
  display: inline-block;
  font-weight: 500;
  line-height: 1;
  padding: 0.75rem 3rem;
  position: relative;
  text-decoration: none;
}

Result:

Link

Cool! Let’s design a :visited style.

CSS pseudo elements

Pseudo elements in CSS are great. Chris Coyier describes them well:

For every element on the page, you get two more free ones that you can do just about anything another HTML element could do.

We’ll use a CSS pseudo element to help style the :visited state of our link. While pseudo elements have the same CSS property restrictions as the <a> tag, with absolute positioning and some basic colour tricks we can essentially hide a :visited style in our link right from the start.

Let’s add a check mark using the pseudo element ::after.

$buttonColor: #3ba7bb;
$textColor: white;

a.demo {
  background-color: $buttonColor;
  border-radius: 4px;
  border: 2px solid $buttonColor;
  color: $textColor;
  display: inline-block;
  font-weight: 500;
  line-height: 1;
  padding: 0.75rem 3rem;
  position: relative;
  text-decoration: none;

  &::after {
    content: '';
    height: 1rem;
    position: absolute;
    right: 1.5rem;
    width: 1rem;
  }
}

Result:

Link

Completing functionality

While we can’t technically ‘add’ the check mark strictly to :visited links, we can hide it in the link’s default state by changing it to the same colour as the background.

Now we can style the :visited state to provide some visual contrast and show off the check mark and border, both of which were disguised originally.

$buttonColor: #3ba7bb;
$textColor: white;

a.demo {
  background-color: $buttonColor;
  border-radius: 4px;
  border: 2px solid $buttonColor;
  color: $textColor;
  display: inline-block;
  font-weight: 500;
  line-height: 1;
  padding: 0.75rem 3rem;
  position: relative;
  text-decoration: none;

  &::after {
    color: $buttonColor;
    content: '';
    height: 1rem;
    position: absolute;
    right: 1.5rem;
    width: 1rem;
  }

  &:visited {
    background-color: #fff;
    border-color: currentColor;
    color: #333;

    &::after {
      color: #49b749;
    }
  }
}

Give it a try:

Click Me

Wrap-up

With pure CSS we’ve created an interesting visual style for visited links without sacrificing user privacy.

Footnotes

  1. 2020 – Sass is not required, and this could be done with CSS custom properties or some other means that wasn’t available or practical in 2013. The demo link that appears inline in this article uses the same technique described but is implemented in React with styled-components and integrated into the post using MDX.