Search code examples
csstextprogress-bareffects

CSS progress bar text color for contrast filled and empty backgrounds?


I want to have XHTML+CSS progress bar with contrast colors between filled and empty background areas.

I have a problem with text color. Because filled and empty backgrounds are too contrast (this is a requirement), to remain readable the text should be double-colored to be contrast to both of them. The image should explain it better than words:

Progress bar with dark blue filled area and white empty background http://drdaeman.pp.ru/tmp/20090703/progress-bar-text-example.png Example of the problem http://drdaeman.pp.ru/tmp/20090703/progress-bar-text-problem.png

My current progress bar implementation is trivial, but as example above shows, the text can be hard to read in some cases, which is exactly a problem I want to solve.

My current (simplified) implementation attempt (fails, because overflow: hidden does not work without positioning div.progress which I cannot position because of inner span's width):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <title>Progress bar test</title>
  <style type="text/css">
    div.progress_bar {
        border: 1px #ccc solid; position: relative;
        text-align: center; height: 32px;
    }
    div.progress_bar .progress {
        height: 32px;
        overflow: hidden; /* This does NOT work! */
    }
    div.progress_bar .progress div {
        position: absolute; width: 100%; height: 32px;
        z-index: 30; overflow: hidden;
        background-color: #44a;
    }
    div.progress_bar span {
        position: absolute; top: 0; left: 0; width: 100%;
        z-index: 20;
        color: #000;
    }
    div.progress_bar .progress span {
        position: absolute; top: 0; left: 0; width: 100%;
        z-index: 40;
        color: #eee;
    }
  </style>
</head>
<body>
  <!-- Can be of any (unknown) width. Think of "width: auto".
       The 400px value is just to keep it small on a big monitor.
       DON'T rely on it! -->
  <div id="container" style="width: 400px;">
    <div class="progress_bar">
      <!-- div.progress is a dark filled area container -->
      <div class="progress" style="width: 51%;">
        <!-- Actually dark filled area -->
        <div style="width: 51%;"></div>
        <!-- Text (white).
             Does not clip, even with overflow: hidden on parent! -->
        <span>This is a test</span>
      </div>
      <!-- Text (black) -->
      <span>This is a test</span>
    </div>
  </div>
</body>
</html>

Live version of the above: http://drdaeman.pp.ru/tmp/20090703/test2.html
Previous attempt: http://drdaeman.pp.ru/tmp/20090703/test.html

The images are GIMP edited prototypes, and not exactly what this code displays.

Add: Thank you all, especially Meep3D, Nosredna and Lachlan! However I still have a problem — in my case progress bar should have no fixed width and take all horizontally available space (width: auto; or width: 100% are acceptable). But without width: 400px rule Lachlan's code breaks. And I'd still like to avoid using JavaScript, if that's possible.


Solution

  • As per Meep3D's suggestion, take 2 copies of the text.

    Wrap each in a div of the same width as the container. The "upper" div is wrapped with another div which clips at the desired percentage.

    Update: removed the fixed widths.
    The "upper" div is sized to the inverse percentage of its wrapper.

    <html>
    <head>
      <style type="text/css">
        #container {
            position: relative;
            border: 1px solid;
            text-align: center;
            width: 400px;
            height: 32px;
        }
        .black-on-white {
            height: 32px;
            color: #000;
        }
        .white-on-black {
            height: 32px;
            color: #fff;
            background-color: #44a;
        }
        .wrapper {
            width: 53%;
            overflow: hidden;
            position: absolute;
            top: 0; left: 0;
        }
        .black-on-white {
            width: 100%;
        }
        .white-on-black {
            width: 188.7%;
        }
      </style>
    </head>
    <body>
      <div id="container">
        <div class="wrapper">
            <div class="white-on-black">
                 <span>This is a test</span>
            </div>
        </div>
        <div class="black-on-white">
            <span>This is a test</span>
        </div>
      </div>
    </body>
    </html>