Inheritance, visited links and colour-contrast
Tags: inheritance, css, a11y, colourcontrast
Published: 2020-01-02 12:41:14

---
title: Inheritance, visited links and colour-contrast
published: true
canonical_url: https://dev.to/rossangus/inheritance-visited-links-and-colour-contrast
description: A meander through the complicated world of colour contrast and inheritance, plus a (potentially flawed) solution.
cover_image: https://screenshot.codepen.io/321502.eaGmJz.7aec34c4-562e-40a1-aca6-afdb95fccbb7.png
tags: inheritance, CSS, accessibility, "Colour contrast"
---

As I'm sure you know, by default, links have a bright blue colour and visited links have a maroon colour. Look!

{% codepen https://codepen.io/ross-angus/pen/QWwqeeL %}

While you might not be in love with that colour scheme, users tend to associate blue with <q>I've not been here before</q> and purple with <q>I've already seen this page</q>. This is a useful convention to piggy-back off, in order for users to understand your <abbr title="User Interface">UI</abbr> faster.

But how do those text colours work against different background colours?

{% codepen https://codepen.io/ross-angus/pen/BaywXXE %}

Not ideal. According to <a href="https://webaim.org/resources/contrastchecker/">WebAIM's colo(u)r contrast checker</a>, this has a ratio of 2.44:1, which is a fail for <abbr title="Double A">AA</abbr> and <abbr title="Triple A">AAA</abbr>.

Our usual pattern for working around this is to use contextual selectors:

```css
.bg-black a:link    {color: #99f;}
.bg-black a:visited {color: #fcf;}
```
{% codepen https://codepen.io/ross-angus/pen/wvBPwvb %}

This _mostly_ works, until you find you need to nest one coloured box inside another:

{% codepen https://codepen.io/ross-angus/pen/XWJzrJM %}

You could _try_ and get out of this mess with contextual selectors, I suppose:

```css
.bg-black a:link             {color: #99f;}
.bg-black a:visited          {color: #fcf;}
.bg-black .bg-pink a:link    {color: #00e;}
.bg-black .bg-pink a:visited {color: #551a8b;}
```

... but anticipating every potential markup configuration in your code is hardly a route to a small <abbr title="Cascading Style Sheets">CSS</abbr> file.

Situations such as this is the reason why the property `inherit` was invented. When I first read about the `inherit` property, I thought <q>I can't imagine ever having to use that</q>. Inheriting the properties of the parent? Browsers do that sensibly without any input from us.

Here's one way to do it:

```css
.bg-black {
  background: black;
  color: white;
}
.bg-pink {
  background: fuchsia;
  color: black;
}
[class*="bg-"] a {
  color: inherit;
}
```

The final rule above would allow anchor tags to inherit the foreground colour of any element they were inside, which had a class which included the string `bg-` anywhere in its name.

## Inheriting an unwanted gift

Here's a little quirk of this behaviour: if you want a property to be available to the child, you need to define it on the parent, _even if it isn't used_. Lemmy explain that a bit.

Say you didn't want to use with `text-decoration` to underline your anchors, but wanted to use `border-bottom` instead. But you _also_ wanted the `border-color` to be inherited by the kids. You'd need to do something like this:

```css
a {
  border-bottom: solid .1em blue;
  text-decoration: none;
}
a:visited {
  border-bottom-color: purple;
}
.bg-black {
  /* Not used directly on this element... */
  border-color: white;
  color: white;
}
[class*="bg-"] a {
  color: inherit;
  /* ... but inherited here. */
  border-color: inherit;
}
```

Note that you can only use the `inherit` value on its own. The following is _not_ valid <abbr>CSS</abbr>:

```css
/* Oh God, don't do this */
[class*="bg-"] a {
  border-bottom: dashed .1em inherit;
}
```

As long as you don't mind your links being the same colour as your body text, this is job done. This is exactly what `inherit` was invented for.

## A brief visit to `:visited` links
`inherit` isn't a solution to our problem yet, but let me swerve slightly here, because I find the following digression interesting (your milage may vary).

When we attempt to style `:visited` links, [there's a white-list of <abbr>CSS</abbr> properties we're allowed to use](https://developer.mozilla.org/en-US/docs/Web/CSS/:visited). The reason for this is solid: if we allow the browser to start mucking about with `:before` and `:after` elements and absolute positioning, pretty quickly some wag would find a way to put one million hidden links at the bottom of their page and work out which sites you've already visited on the web.

Those <abbr>CSS</abbr> properties which _are_ good to use only work when they've already been applied to the default anchor style. For example, this <abbr>CSS</abbr> would do something:

```css
a:link {outline: solid .1em blue;}
a:visited {outline-color: purple;}
```

... where as this would not:

```css
a:link {border-bottom: solid .1em blue;}
a:visited {
  outline: solid .1em purple;
  outline-color: purple;
}
```

In the above example, because no `outline` is defined on links by default, no outline will appear on the visited link either.

I originally had the idea that visited links could have an `opacity` of `0.8`, so they would work against any background colour, as long as they inherited the text colour. But as `opacity` isn't on the list of permitted declarations for `:visited` links, it wouldn't work.

## So how _do_ we get visited links recoloured against any background?
Let's recap what a perfect answer to this question would have to achieve:

- Links will always display a colour tint which shows them as being either visited or not visited
- Link colours should have sufficient contrast with the background colour
- Coloured boxes should be able to be nested together in any order and the links should still maintain contrast
- This should _not_ require any classes on the links themselves

My answer to this _mostly_ works, but unfortunately requires that any coloured boxes must be an _immediate_ child of its parent. Here's a couple of <abbr>DOM</abbr> trees, to illustrate:

{% codepen https://codepen.io/ross-angus/pen/GaQXxM %}

This is the tricky piece of CSS which does the hard work:

```css
/* Assuming .bg-1 is a dark colour... */
.bg-1 > :not([class*="bg-"]) a:link {
  color: #9ff; /* A light blue */
}
.bg-1 > :not([class*="bg-"]) a:visited {
  color: #fcf; /* A light pink */
}
```

If I were to write that first selector in English, I'd say <q>Get me all of the links which sit _under_ an element with the class `bg-1`, but not if they're a child of any other element which has a class which contains `bg-`</q>.

You might be wondering why I used the immediate child selector (`>`). Because a space in a selector could be interpeted by the browser as <q>immediate child</q> or <q>child twenty levels deep in the <abbr title="Document Object Model">DOM</abbr></q>, there's a means for the browser's selecter engine to sidestep that `:not([class*="bg-"])` part, and to still select the wrong links.

If you imagine that the selector was less rigid, say:

```css
/* Note: this doesn't work */
.bg-1 :not([class*="bg-"]) a:link {
  color: #9ff; /* A light blue */
}
```

This selector will still match links inside `.bg-2` elements (which themselves are inside `.bg-1` elements) because it can skip over the `.bg-2` part of the <abbr>DOM</abbr> chain, and just substitute the paragraph tag in instead. Look:

<a href="http://images.realise.com/realise/blog/inheritance.png">![Trying to explain the above paragraph in an image, but still failing](http://images.realise.com/realise/blog/inheritance.png)</a>

This happens because if the <abbr>CSS</abbr> rule which controls link colour appears _later_ in the style sheet, it will beat the earlier rule for inheritance.

## Stress test

What I'm sure you're curious about is how can this be tested, in the most garish possible way. Got you covered there, buddy:

{% codepen https://codepen.io/ross-angus/pen/eaGmJz %}

<small>This article was originally published on <a href="https://codepen.io/ross-angus/post/inheritance">CodePen</a>.</small>