2

I'm creating several divs in Javascript by inserting something like this

'<div style="background-color:' + bgColor + '</div>'

Now I want to set the color of the text automatically based on the luminosity of the background to black or white.

I see 2 options - all driven from Javascript or in CSS only. I prefer the CSS option, however, don't know how to read the background color for a CSS function, e.g.

@function set-color($color) {
  @if (lightness($color) > 40) {
    @return #000;
  }
  @else {
    @return #FFF;
  }
}

How can I fetch the background color to do something like this

div { color: set-color(???); }

How about mix-blend-mode ?

7
  • 6
    you cannot ... you need JS Commented Nov 7, 2020 at 2:06
  • @TemaniAfif Using SASS then converting it back to CSS? Commented Nov 7, 2020 at 2:11
  • 3
    @Tân SASS comes before any JS or page rendring so it's even more impossible Commented Nov 7, 2020 at 2:14
  • Do you want only two colors i.e. #000 and #fff for your text based on the background-color? Commented Nov 7, 2020 at 5:32
  • @PraneetDixit Yes only black/white Commented Nov 7, 2020 at 6:15

2 Answers 2

3

There are several ideas put forward in the question about how to choose between black and white for text color depending on the background color.

Most have been answered one way or another in comments. Taking the ideas one by one:

Can we use CSS mix-blend-mode - no. There is no one setting for this that ensures text will appear readable on all possible backgrounds.

Can we use CSS (the preferred method) - unfortunately no as the div requiring the text color to depend on background color is created by JS at run time.

Can we use JS - yes and as the div is being created by JS and having its background-color set then it might as well have its color set then too.

The JS string is as given in the question with the addition of a setting for color:

'<div style="background-color:' + bgColor + '; color: ' + textBlackOrWhite(bgColor) + ';"'

Here is a snippet which defines the function. The snippet also lets you choose a background color and it then sets a color which (roughly) depends on the 'brightness' of the background. See the SO questions referenced here for further discussion as human color perception is a difficult topic.

//from https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
function textBlackOrWhite(hex) {
  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function(m, r, g, b) {
    return r + r + g + g + b + b;
  });

  let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);//also checks we have a well-formed hex number
  
//from https://stackoverflow.com/questions/596216 the answer by @FranciPenov which gives an approximation: (R+R+G+G+G+B)/6 
  let itsbright = function () { return ((2*parseInt(result[1], 16) + 3*parseInt(result[2], 16) + parseInt(result[3], 16))/6)>127; }
  return result ? (itsbright()) ? '#000000' : '#ffffff' : '#000000';//falls back onto black if bgColor was not a well-formed 3 or 6 digit hex color
}
<div id="div" style="font-family: monospace; box-sizing: border-box; margin: 0; padding: 40px 0; width: 100px; height: 100px; border-style: solid; border-radius: 50%; background-color: black; color: white;text-align:center;">#ffffff</div>
Click to choose background color: <input id="input" placeholder='#00000' type='color' value='#000000'/>
<button onclick="let d = document.getElementById('div'); let i = document.getElementById('input'); d.innerHTML = i.value; d.style.backgroundColor = i.value; d.style.color = textBlackOrWhite(i.value);">Submit</button>

Sign up to request clarification or add additional context in comments.

Comments

1

I want to set the color of the text automatically based on the luminosity of the background to black or white.

I recognise this is a different approach from what you're asking for, but one CSS-only approach to having universally-readable text, regardless of background-color is to have white text with a black outline (or vice versa).

You can use 4 text-shadows for the outline:

text-shadow: 1px 1px rgb(0, 0, 0), -1px 1px rgb(0, 0, 0), -1px -1px rgb(0, 0, 0), 1px -1px rgb(0, 0, 0);

Working Example

const divs = [...document.getElementsByTagName('div')];

for (div of divs) {

  let redValue = Math.floor(Math.random() * 255);
  let greenValue = Math.floor(Math.random() * 255);
  let blueValue = Math.floor(Math.random() * 255);
  
  div.style.backgroundColor = `rgb(${redValue}, ${greenValue}, ${blueValue})`;
}
div {
  float: left;
  width: 180px;
  height: 40px;
  margin: 0 6px 6px 0;
  line-height: 40px;
  color: rgb(255, 255, 255);
  font-size: 32px;
  text-align: center;
  text-shadow: 1px 1px rgb(0, 0, 0), -1px 1px rgb(0, 0, 0), -1px -1px rgb(0, 0, 0), 1px -1px rgb(0, 0, 0);
  background-color: yellow;
}
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>
<div>Sample Text</div>

4 Comments

This is a really nice solution (being CSS only) for display-type text, headings and so on. It's not unreadable at say 16px but quite difficult to read a long text down at these lower font sizes maybe. Depends on a particular use case.
Thanks, @AHaworth. You're right, of course - this approach is better suited to some use-cases and less well suited to others.
Yes, but the UX people might be upset :D
@BenjaminE. - Yes, possibly. I actually stole this technique from vintage LucasArts graphic adventure games (like The Secret of Monkey Island from 1990).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.