Invert CSS font-color depending on background-color
Asked Answered
K

6

100

Is there a CSS property to invert the font-color depending on the background-color like this picture?

enter image description here

Karafuto answered 7/6, 2013 at 10:18 Comment(1)
Also check this question: Is it possible to change text color based on background color using css?Catgut
P
108

There is a CSS property called mix-blend-mode, but it's not supported by IE. I recommend using pseudo elements. If you like to support IE6 and IE7 you can also use two DIVs instead of pseudo elements.

enter image description here

.inverted-bar {
    position: relative;
}

.inverted-bar:before,
.inverted-bar:after {
    padding: 10px 0;
    text-indent: 10px;
    position: absolute;
    white-space: nowrap;
    overflow: hidden;
    content: attr(data-content);
}

.inverted-bar:before {
    background-color: aqua;
    color: red;
    width: 100%;
}

.inverted-bar:after {
    background-color: red;
    color: aqua;
    width: 20%;
}
<div class="inverted-bar" data-content="Lorem ipsum dolor sit amet"></div>
Pretypify answered 7/6, 2013 at 10:21 Comment(6)
'attr' is not widely supported.Cochin
@Cochin -- Yes it is, since at least 2010, cross all browsers, even IE from ver. 8.Plucky
attr() is only supported for the "content" parameter. You wont be able to use it for something like background-color: attr(data-color); but as long as it's content: attr(data-anything) you'll be fine. For all the other use cases better resort to css variables.Dietary
How is this the accepted answer? It 1) doesn't answer the question, and 2) doesn't work in any case.Tuft
This answer says to use mix-blend-mode but the included code doesn't have that property set anywhere.Shamanism
@MichaelZiluck No. Read the answer. This answer provides a solution for IE, of which doesn't support mix-blend-mode. This answer is valid but this answer should be accepted/top answer.Spermatid
S
77

Use mix-blend-mode.

blend-modes example

http://jsfiddle.net/1uubdtz6/

div {
    position:absolute;
    height:200px
}

/* A white bottom layer */
#whitebg { 
    background: white; 
    width:400px; 
    z-index:1
}

/* A black layer on top of the white bottom layer */
#blackbg { 
    background: black; 
    width:100px; 
    z-index:2
}

/* Some white text on top with blend-mode set to 'difference' */
span {
    position:absolute; 
    font-family: Arial, Helvetica; 
    font-size: 100px; 
    mix-blend-mode: difference;
    color: white;
    z-index: 3
}

/* A red DIV over the scene with the blend-mode set to 'screen' */
#makered { 
    background-color: red;
    mix-blend-mode: screen;
    width:400px; 
    z-index:4
}
<div id="whitebg"></div>
<div id="blackbg"></div>
<div id="makered"></div>

<span>test</span>
Stopcock answered 7/9, 2014 at 4:49 Comment(2)
Beware, that using mix-blend-mode may break your CSS transitions, like smooth opacity, because if any child element has mix-blend-mode style, the opacity is not animated smoothly on GPU (at least as of today on Chrome).Imposition
This is an overview of the browsers supporting it.Thingumajig
S
8

I know this is an old question, but I wanted to add another solution I've been using before I learned of mix-blend-mode.

The idea is to have the information duplicated in two layers, a back and a front, where the back and front have different background and text colors. These are identical in dimension and text. In between, I use a clipping box div to crop the front (top) layer to the desired width, showing the front layer where it is not clipped, and revealing the back layer outside of the clipping window.

This is similar to the "Two div" solution in the accepted answer, but uses the extra clipping box. What's advantageous in this solution is the easy centering of text if desired, and simple, direct selection of the colors.

// Set *front* width to *back* width
// Do this after DOM is ready
$('#front').width($('#back').width())

// Based upon an event that determines a content change
// you can set the text as in the below example
percent_complete = 45  // obtain this value from somewhere; 45 is just a test

$('#front').text(percent_complete.toString() + '% complete')
$('#back span').text($('#front').text())
bb_width = (percent_complete * $('#back').width())/100
$('#boundbox').css('width', bb_width.toString())
.progress {
  display: block;
  margin: 0;

  /* Choose desired padding/height in coordination with font size */
  padding: 10px;
  height: 28px;
}

#back {
  position: relative;

  /* Choose a border to your liking, or none */
  border: 1px solid lightgray;

  /* Choose your desired text attributes */
  text-align: center;
  font-family: Calibri, "Sans Serif";
  font-size: 16pt;

  /* Set the desired width of the whole progress bar */
  width: 75%;

  /* Choose the desired background and text color */
  background-color: white;
  color: black;
}

#front {
  position: absolute;
  left: 0;
  top: 0;

  /* Choose the desired background and text colors */
  background-color: navy;
  color: white;
}

#boundbox {
  position: absolute;
  left: 0;
  top: 0;
  overflow: hidden;
}
html
<div class='progress' id='back'>
  <span></span>
  <div class='progress' id='boundbox'>
    <div class='progress' id='front'>
    </div>
  </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

I use jQuery to programmatically set the percent progress and make sure that the width of the front matches that of the back, and that they have identical text. This can also easily be done with pure Javascript.

And here's a fiddle: Progress bar.

Image 1

I tested this in Chrome, Firefox, Microsoft Edge, and IE version 11.

Shaeffer answered 4/7, 2019 at 18:51 Comment(0)
L
1

I've seen that this is quite an old question, but having found myself with the same problem I came up with a solution that uses only CSS but requires using the same text 3 times in 3 separate divs.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .container {
            width: fit-content;
            padding: 1rem;
            font-size: 4rem;
            overflow: hidden;
            border: 1px solid black;
            position: relative;
            color: white;   /* Hide the first text */
        }
        .container .bellow, .container .above {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
        .container .bellow span, .container .above span {
            position: relative;
            top: 1rem;    /* Same as padding */
            left: 1rem;   /* Same as padding */
        }
        .container .bellow {
            color: black;   /* The text as seen normally */
        }
        .container .above {
            color: white;   /* Text color when background slides in*/
            overflow: hidden;
            white-space: nowrap;
            background-color: red;
            width: 0;        /* width: 50%; --> See the effect*/
            transition: width 5s;
        }
        .container:hover .above {
            width: 100%;
        }

    </style>
</head>
<body>
    <div class="container">
            Input text (hover)
        <div class="bellow">
            <span>Input text (hover)</span>
        </div>
        <div class="above">
            <span>Input text (hover)</span>
        </div>
    </div>
</body>
</html>
Lectern answered 24/2, 2023 at 1:20 Comment(0)
P
0

I know this is an old question, but I had the same problem and none of the solutions provided worked for my use case. I was able to achive the same effect using background-clip: text with only an anchor tag and a ::before pseudo-element. Note that this solution does not work on all browsers, so you may need to provide a fallback style, check the list of browsers that supports background-clip and transform-style properties.

The properties transform-style: preserve-3d and translateZ(-1px) are needed to keep the ::before pseudo-element behind the text background.

.link {
  position: relative;
  cursor: pointer;
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) and (transform-style: preserve-3d) {
  .link {
    background-size: 200% 100%;
    background-position: 100%;
    transition: background-position 0.25s ease;
    background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
    transform-style: preserve-3d;
    background-image: linear-gradient(90deg, white 50%, black 50%);
  }

  .link::before {
    content: "";
    display: block;
    position: absolute;
    width: 0;
    height: 100%;
    left: 0;
    top: 0;
    z-index: -1;
    transform: translateZ(-1px);
    transition: width 0.25s ease;
    background-color: black;
  }

  .link:hover {
    background-position: 0;
  }

  .link:hover::before {
    width: 100%;
  }
}

@supports not ((background-clip: text) or (-webkit-background-clip: text) or (transform-style: preserve-3d)) {
  .link {
    transition: color 0.25s ease;
  }

  .link:hover {
    color: black;
  }
}
<a class="link">Example link!</a>
Pardew answered 18/4, 2023 at 7:9 Comment(0)
M
-2

I think this is more simple to understand.

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: sans-serif;
}
.titulo{
    text-align: center;
    margin: 2em;
}
.padre{
    margin: 0 auto;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 10em;
    position: relative;
    width: 1000px;
    height: 500px;
}
.caja-1{
    background-color: black;
    width: 500px;
    height: 500px;
    left: 0;
    mix-blend-mode: screen;
    position:absolute;
}
.caja-3{
    width: 500px;
    height: 500px;
    display: flex;
    background-color: white;
    position: absolute;
    right: 0;
}
.texto{
    font-size: 5em;
    color: white;
    mix-blend-mode: difference;
    position:absolute;
}
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ESTILOS CONTRASTADOS CSS3</title>
</head>
<body>
    <h1 class="titulo">MIX-BLEND-MODE CSS EFFECT</h1>
    <div class="padre">
        <div class="caja-1"></div>
        <div class="caja-3"></div>
        <h1 class="texto">CODE STOCK CENTER</h1>
    </div>
</body>
</html>
Mothball answered 11/7, 2019 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.