htmlcsscolorsbackground-color

Dynamically change colours with CSS so background and text colour aren't clashing


I'm facing the problem that users can change background colours for HTML components which sometimes conflict with text colours they can't change (or more generally, background and text colour are visually too similar). As a result, the text may become invisible (or difficult to see) if those colours are visually close enough.

See below where's there's a triangle (=text)...

enter image description here

...which only becomes more visible on hover.

enter image description here

(the user could also have also opted for a grey background which would have made this even worse)

I've tried to tinker with filters (e.g. filter: brightness(50%) contrast(150%); or filter: saturate(0) grayscale(1) brightness(.7) contrast(1000%) invert(1) as suggested here), but that's sensitive to the selected colours (i.e. may not work in all cases of background and text colour combinations). Is there a way (in CSS, without JS as shown here) that would change the text colour slightly (ideally, the "type of colour" itself is somewhat maintained), so that text on conflicting (too close) background and text colours becomes visible?

I realise that this question is asking for 2 things (1. detecting the conflict and 2. resolving the conflict by applying some sort of filter (or others)). If some suggestions are made for how to resolve the conflicting colours (2.), I'd already be happy.

Trying mix-blend-mode: difference

Without mix-blend-mode: difference;

enter image description here

With mix-blend-mode: difference;

enter image description here


Solution

  • This article demonstrates a CSS method for switching the (text) color between black and white based on the (perceived) luminance of the background color. So it’s not exactly what you’re asking for, but it’s close.

    A simpler alternative you could consider is using a contrasting text-shadow to improve readability when the text and background colours are similar.

    :root {
      --clr-white-80: rgb(255 255 255 / 0.8);
    }
    
    body {
      background: lightsalmon;
      font-size: 2.5em;
    }
    
    p {
      color: peru;
      margin: 0;
    }
    
    .p2 {
      text-shadow: 0 0 5px var(--clr-white-80);
    }
    
    .p3 {
      text-shadow: 0 0 20px var(--clr-white-80);
    }
    
    .p4 {
      text-shadow: 2px 2px 0 var(--clr-white-80);
    }
    <p class="p1">Is this readable?</p>
    <p class="p2">What about now?</p>
    <p class="p3">Or with a larger spread</p>
    <p class="p4">Or with an offset</p>