HTML & CSS: How to change text color based on background color

Let’s say we have a progress bar with some text in it and the background is a light color. We want the font to be dark enough to contrast with the light background. Then as the progress bar or meter fills up with a darker color, we want the font color to adjust (lighter font color) so that the text will be readable as it spans both light and dark background colors.

There is a CSS trick to this that I have created a working example of on codepen:

See the Pen light and dark font based on background (CSS only) – 4 by Chris Nielsen (@Chris_Nielsen) on CodePen.0

Light blue background –> dark font
Dark blue background –> white font

In this codepen example, I have added some buttons that will allow you to advance  the progress of the dark blue so that you can see the font color changing.

This CSS solution has three parts.

Part 1: outer div

This needs to be have the following attributes:

  • position: relative  /*  this is the most important part */
  • width should be 100% of the task
  • features light colored background with dark font

Here is the CSS for Part 1 of the codepen example above:

  .ats-status-bar {
    height: 44px;
    color: #222;
    background-color: #deedf4;
    text-align: left;
    border-radius: 10px;
    width: 500px;
    border-radius: 10px;
    font-family: 'Raleway', sans-serif;
    font-size: 20px;
    margin-left: 2%;  
    position: relative;
    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
  }

Part 2: inner div

  • position: absolute;
  • z-index to raise it above the lighter layer
  • overflow: hidden;
  • white-space: nowrap;
  • width is % of task that is complete (adjustable)
  • features dark blue background with white text

Here is the CSS for Part 2 of the codepen example above:

.ats-status-bar-completed {
    height: 44px;
    background-color: #59A3C8;
    width: 100px;
    border-radius: 10px;
    font-family: 'Raleway', sans-serif;
    color: #fff;
    font-size: 20px;
    text-align: left;
    position: absolute; 
    z-index: 10; 
    overflow: hidden; 
    white-space: nowrap;
 }

 

Part 3: repeated text

This is the step that makes the whole trick work. You basically repeat the text twice. Once inside the inner div and again in the outer div that holds the inner div. For example, here is the HTML:

<div class="ats-status-bar-completed">
    <div class="ats-task-name">The quick brown fox jumped over the lazy dogs.</div>
    </div>
    <div class="ats-task-name">The quick brown fox jumped over the lazy dogs.</div>
</div>

Here is the complete HTML:

<div class="ats-job-progress-container">
  <div class="ats-status-bar-bottom">
    <div class="ats-left-spacing" style='width: 10%;'></div>
      <!-- Part 1 -->
      <div class="ats-status-bar">
        <!-- Part 2 -->
        <div class="ats-status-bar-completed">
          <div class="ats-task-name">The quick brown fox jumped over the lazy dogs.</div>
        </div>
        <div class="ats-task-name">The quick brown fox jumped over the lazy dogs.</div>
      </div>
    </div>
  </div>
</div>
<br>
<input style='margin-left: 2%;' type="button" id="b1" value="Reset" onClick="reset();"> 
<input type="button" id="b2" value="Increase Percent" onClick="increase();">

Here is the complete CSS:

.ats-job-progress-container {
  width: 100%;
  align: left;
  border-radius: 10px;
  font-family: 'Raleway', sans-serif;
  font-size: 16px;
}

.ats-status-bar-bottom {
  width: 100%;
  height: 44px;
  border-radius: 10px;
  text-align: right;
  vertical-align: middle;
  position: relative;
}

.ats-left-spacing {
  height: 44px;
  padding-top: 8px;
  padding-right: 8px;
  position: absolute;
  z-index: 121;
}

.ats-status-bar {
  height: 44px;
  color: #222;
  background-color: #deedf4;
  text-align: left;
  border-radius: 10px;
  width: 500px;
  border-radius: 10px;
  font-family: 'Raleway', sans-serif;
  font-size: 20px;
  margin-left: 2%;  
  position: relative;
  box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}

.ats-status-bar-completed {
  height: 44px;
  background-color: #59A3C8;
  width: 100px;
  border-radius: 10px;
  font-family: 'Raleway', sans-serif;
  color: #fff;
  font-size: 20px;
  text-align: left;
  position: absolute; 
  z-index: 10; 
  overflow: hidden; 
  white-space:nowrap;
}

.ats-task-name {
  vertical-align: middle;
  text-align: left;
  padding-top: 10px;
  padding-left: 10px;
  padding-right: 10px;
  position: absolute;
}

body {
  background-color: #888;
}

#b1{
  background-color: #59A3C8;  
  padding: 5px;
  width: 100px;
  color: white;
  font-size: 14px;
  border: none;
  border-radius: 10px;
  box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}

#b2 {
  background-color: #59A3C8;  
  padding: 5px;
  width: 200px;
  color: white;
  font-size: 14px;
  border: none;
  border-radius: 10px;
  margin-left: 10px;
  box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}

#b1:hover, #b2:hover {
  background-color: #deedf4; 
  color: black;
}


And here is the JavaScript code (jQuery) that was used for the buttons:

function reset() {
  $('.ats-status-bar-completed').css('width', '100px')
}

function increase() {
  let percent = $('.ats-status-bar-completed').width();
  console.log(percent);
  percent += 100;
  let p = percent.toString() + 'px';
  $('.ats-status-bar-completed').width(p);
  if (percent > 550) reset();
}