18

I have two div with two different transparent, colored backgrounds. The elements overlap one another.

I would like to customize the color in the overlapped area.

For example I mix red and blue with opacity 0.5 and would like the overlapped area to be black. Is it possible? This solution would simplify the implementation of functionality.

For better understanding, example:

.wrapper{
  width: 200px;
  height: 500px;
  background-color: #fff;
  position: relative;
}
.box{
  width: 100%;
  position: absolute;
}
.box-1{
  top: 0px;
  height: 250px;
  background-color: rgba(181, 226, 163, 0.5);  
}
.box-2{
  background-color: rgba(183, 222, 241, 0.5);
  top: 200px;
  height: 300px;
}
<div class="wrapper">
  <div class="box box-1">
  </div>
  <div class="box box-2">
  </div>
</div>

UPDATE:

The height of the overlap area is unknown, so it is not an option to add an element rigidly on height 50px. Question is about custom mixing colors.

Piotr Białek
  • 2,569
  • 1
  • 17
  • 26
  • red and blue make purple so opacity will not work for what you want. – mccainz Dec 02 '16 at 14:38
  • 1
    Not sure if you can explicitly define the result, but you might want to look into [css blend modes](https://css-tricks.com/basics-css-blend-modes/) – chazsolo Dec 02 '16 at 14:39
  • @chazsolo Thank you for the link, blend modes were new to me. Unfortunately IE does not support them even in Edge http://caniuse.com/#search=background-blend-mode – mccainz Dec 02 '16 at 14:41
  • 1
    People, can you please clone the Bin & customize your code. I see changes happening on the actual bin posted by OP. His code is lost forever now. – Nikhil Nanjappa Dec 02 '16 at 14:42
  • 4
    @NikhilNanjappa This is a good reason to post the relevant code in the question itself ! – vals Dec 02 '16 at 15:17
  • I have posted an alternative approach below, deploying an absolutely positioned `::after` pseudo-element with a height dynamically calculated using `calc()` - it simulates _custom mixing colors_ and it will work in all contemporary browsers. – Rounin Dec 02 '16 at 17:07
  • Hey, it's now possible to use a native css function called color-mix. On chrome for now: https://developer.chrome.com/blog/css-color-mix/ And usable with Safari and Firefox behind a flag – LukyVj Feb 28 '23 at 17:49

8 Answers8

11

This uses the mix-blend-mode CSS property with a value of multiply to achieve the results. Unfortunately this is not supported in IE.

<!DOCTYPE html>
<html>

  <head>
    <style>
      div{
        width:100px;
        height:100px;
        opacity:1;
      }
      .red{
        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==);
      }
      .blue{
        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg==);
        mix-blend-mode: multiply
      }
    </style>
  </head>

  <body>
    
    <div class="red">
    </div>
    <div style="position:absolute;top:90px" class="blue">
    </div>
  </body>

</html>
mccainz
  • 3,478
  • 5
  • 35
  • 45
  • If OP could verify his browser support, I think this may be the best solution for him. I think it's the best solution overall, regardless: no javascript and minimal code. – chazsolo Dec 02 '16 at 15:23
  • @chazsolo How could it be the best solution overall when it's not supported on 40% of the user agents? – Scott Marcus Dec 02 '16 at 19:01
  • I prefaced it with "I think" for a reason. The issue at hand may simply be aesthetic, and therefore a fallback to opacity or some other method may be reasonably justifiable. OP wasn't specific on this, so I was simply commenting on why I upvoted the answer. Based on the question, is this even possible, then I think this option is best suited to provide what was asked for. – chazsolo Dec 02 '16 at 19:05
3

You can achieve this effect by giving .box-1 an absolutely positioned ::after pseudo-element (positioned at bottom:0;) with a height calculated as 100% of its .box-1 parent minus the vertical offset position of .box-2.

Eg. If .box-2 has a top: value of 200px, then the height of the ::after pseudo-element on .box-1 should be:

height: calc(100% - 200px); /* 200px is the top: value of .box-2 */

Working Example (with .box-1 at 4 different heights - the last .box-1 is resizable):

.wrapper{
  display: inline-block;
  width: 200px;
  height: 500px;
  background-color: #fff;
  position: relative;
}

.box{
  width: 100%;
  position: absolute;
}

.box-1{
  top: 0px;
  background-color: rgb(128,191,128);
}

.box-2{
  top: 200px;
  height: 300px;
  background-color: rgb(128,128,255);
}

.wrapper .box-1 {
z-index: 6;
}

.wrapper .box-1::after {
content: '';
position: absolute;
left: 0;
bottom: 0px;
width: 100%;
height: calc(100% - 200px); /* 200px is the top: value of .box-2 */
background-color: rgba(0,0,0,1);
}

div:nth-of-type(1).wrapper .box-1 {
height: 250px;
}

div:nth-of-type(2).wrapper .box-1 {
height: 275px;
}

div:nth-of-type(3).wrapper .box-1 {
height: 300px;
}

div:nth-of-type(4).wrapper .box-1 {
height: 325px;
}


div:nth-of-type(4).wrapper .box-1 {
resize: vertical;
overflow: auto;
}
<div class="wrapper">
  <div class="box box-1">
  </div>
  <div class="box box-2">
  </div>
</div>

<div class="wrapper">
  <div class="box box-1">
  </div>
  <div class="box box-2">
  </div>
</div>

<div class="wrapper">
  <div class="box box-1">
  </div>
  <div class="box box-2">
  </div>
</div>

<div class="wrapper">
  <div class="box box-1">
  </div>
  <div class="box box-2">
  </div>
</div>
Rounin
  • 27,134
  • 9
  • 83
  • 108
2

This is probably a lot more than you would want to do, but I think it is the only way you can get the result that you are looking for.

Using javascript you can basically detect if there is an overlap, and if there is create a div that represents the overlapped area and color that div black. Below is a code snippet showing a very simple example.

UPDATE:

I saw in a comment that this needs to work across multiple elements, not just two elements, so I updated my snippet to work with multiple elements. As long as the element is a class type overlappable-box then an overlap will be calculated for the element.

clearOverlaps is not being used in the snippet, but if your page is dynamic at all, then you will need to be able to clear any calculated overlaps which you can do with that function.

function clearOverlaps() {
  overlaps = document.getElementsByClassName("box-overlap");
  var i = 0;
  for (i = 0; i < overlaps.length; i++) {
    document.body.removeChild(overlaps[i]);
  }
}

function addOverlap(box1, box2) {
  var box1Top = box1.offsetTop;
  var box1Left = box1.offsetLeft;
  var box1Right = box1Left + box1.offsetWidth;
  var box1Bottom = box1Top + box1.offsetHeight;

  var box2Top = box2.offsetTop;
  var box2Left = box2.offsetLeft;
  var box2Right = box2Left + box2.offsetWidth;
  var box2Bottom = box2Top + box2.offsetHeight;

  var isOverlappedVertically = box1Bottom > box2Top && box1Top < box2Bottom;
  var isOverlappedHorizontally = box1Right > box2Left && box1Left < box2Right;

  if (isOverlappedVertically && isOverlappedHorizontally) {
    var overlapTop = Math.max(box1Top, box2Top);
    var overlapBottom = Math.min(box1Bottom, box2Bottom);
    var overlapLeft = Math.max(box1Left, box2Left);
    var overlapRight = Math.min(box1Right, box2Right);
    var overlap = document.createElement("div");
    overlap.className += "box-overlap";
    overlap.style.position = "absolute";
    overlap.style.left = overlapLeft + "px";
    overlap.style.width = (overlapRight - overlapLeft) + "px";
    overlap.style.top = overlapTop + "px";
    overlap.style.height = (overlapBottom - overlapTop) + "px";
    document.body.appendChild(overlap);
  }
}

var overlappableBoxes = document.getElementsByClassName("overlappable-box");
var i = 0;
var j = 0;
var box1;
var box2;
for (i = 0; i < overlappableBoxes.length; i++) {
  box1 = overlappableBoxes[i];
  for (j = i + 1; j < overlappableBoxes.length; j++) {
    box2 = overlappableBoxes[j];
    addOverlap(box1, box2);
  }
}
.overlappable-box {
  position: absolute;
}
.red-box {
  top: 0px;
  left: 40px;
  height: 50px;
  width: 50px;
  background-color: red;
  opacity: .5
}
.blue-box {
  background-color: blue;
  top: 20px;
  left: 25px;
  height: 50px;
  width: 50px;
  opacity: .5
}
.green-box {
  background-color: green;
  top: 60px;
  height: 50px;
  width: 50px;
  opacity: .5
}
.box-overlap {
  background-color: black;
  z-index: 99;
}
<div class="overlappable-box red-box"></div>
<div class="overlappable-box blue-box"></div>
<div class="overlappable-box green-box"></div>
TJ Rockefeller
  • 3,178
  • 17
  • 43
1

Sure. Although, you can only do this by including another element that is on top of the overlapped area. To show this, I'm changing the widths and heights of the divs so you can see where all the borders are.

See this:

div {position:absolute; border:1px solid black;}
#div1 { background:red; opacity:.5; top:0; height:110px; width:100px;}
#div2 { background:blue; opacity:.5; top:50px; height:100px;width:175px;}
#div3 { background:black; top:50px; height:50px; z-index:99; width:150px;}
<div id="div1">test</div>
<div id="div2">test</div>
<div id="div3">test</div>

For your second issue: "I don't know the height of the overlapped area" This can be solved with JavaScript:

var d1 = document.getElementById("div1");
var d2 = document.getElementById("div2");
var d3 = document.getElementById("div3");  // This is the div that will mask the overlapping area

// Get the height and top of the first element
var d1Height = parseInt(window.getComputedStyle(d1).height.replace("px", ""));
var d1Top = parseInt(window.getComputedStyle(d1).top.replace("px", ""));
console.log("div1 height: " + d1Height, ", div1 top: " +  d1Top);

// Get the height and top of the second element
var d2Height = parseInt(window.getComputedStyle(d2).height.replace("px", ""));
var d2Top = parseInt(window.getComputedStyle(d2).top.replace("px", ""));
console.log("div2 height: " + d2Height, "div2 top: " + d2Top);

// Determine where the overlap starts
var overlapTop = d1Height - d2Top;
console.log("Overlap begins at: " + overlapTop);

// Determine the height of the overlap
var overlapHeight = (d1Top + d1Height) - overlapTop;
console.log("overlap height: " + overlapHeight);

// Move the overlapping div into position by setting its style programmatically:
d3.style.top = overlapTop + "px";
d3.style.height = overlapHeight + "px";
div {position:absolute; border:1px solid black;}
    #div1 { background:red; opacity:.5; top:0; height:100px; width:100px;}
    #div2 { background:blue; opacity:.5; height:100px; top: 50px; width:175px;}

    /* This is the div that will mask the overlapping area
       Note that a position and height are not set
       The only reason the width is set is to show what area
       it winds up overlapping, but that would be removed from
       this rule and determined in JavaScript, just as the height is */
    #div3 { background:black; z-index:99; width:150px;}
<div id="div1">test</div>
<div id="div2">test</div>
<div id="div3">test</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 1
    OP knows it would give him purple, he is asking if there is a way to make it black – Luka Kvavilashvili Dec 02 '16 at 14:37
  • @Whitcik See my updated answer for a cross-browser solution when the overlap area is unknown. – Scott Marcus Dec 02 '16 at 15:05
  • @Scott Marcus it will be good solution but for only two `div`. I gave this example to illustrate the problem. I have a similar problem, but for over 200 elements. That is why I am looking for a simple solution using CSS, if possible. If not, I'll have to write it in javascript. – Piotr Białek Dec 02 '16 at 15:10
  • @Whitcik If you don't know the overlapping areas, you'll have no choice but to determine it programmatically. While working with 200 elements will be hard, the process is no different than what I've shown. – Scott Marcus Dec 02 '16 at 15:11
0

I think you need to set the opacity of the 2 boxes to have their colors mixed instead of setting the opacity on the color. The rgba opacity only lightens or darken the color applied on the div. The divs by default have opacity set to 1 so if both boxes overlap then only the top box will be shown and the bottom box will be hidden. To achieve what you want add opacity:0.5 to the .box-1 and .box-2 classes to apply to the divs as well.

Nasir T
  • 2,603
  • 1
  • 9
  • 15
  • That will mix the colors, but won't produce a custom color. – Scott Marcus Dec 02 '16 at 14:44
  • Really? Focus. You actually answered your own query. Mixing colors actually produces custom color. Also the question was asked to mix the colors and this does that. – Nasir T Dec 02 '16 at 14:48
  • 2
    The OP wants a *different color* than the two colors blended by opacity will produce. That's what I mean by custom color - - Not the generated color due to mixing. You should read carefully before telling others to focus. – Scott Marcus Dec 02 '16 at 15:07
  • Actually now you are making sense. See when you focus things get clear. Hehe. – Nasir T Dec 02 '16 at 15:10
0

Something like this ?

But sure thing it wont be much helpful if the height is dynamic

.wrapper {
  width: 200px;
  height: 500px;
  background-color: #fff;
  position: relative;
}
.box {
  width: 100%;
  position: absolute;
}
.box-1 {
  top: 0px;
  height: 250px;
  background-color: rgba(181, 226, 163, 0.5);
}
.box-2 {
  background-color: rgba(183, 222, 241, 0.5);
  top: 200px;
  height: 300px;
}
.box-2:before {
  background-color: black;
  height: 50px;
  display: block;
  content: ' ';
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
  <div class="wrapper">
    <div class="box box-1">
    </div>
    <div class="box box-2">
    </div>
  </div>
</body>

</html>
Jones Joseph
  • 4,703
  • 3
  • 22
  • 40
0

You can use CSS gradients:

.box-2 {
    background-color: blue;
    top: 200px;
    height: 300px;
    background: rgb(0,0,0);
    background: -moz-linear-gradient(top, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 12%, rgba(0,0,0,1) 25%, rgba(0,0,0,1) 39%, rgba(44,44,44,1) 50%, rgba(0,0,0,1) 51%, rgba(25,33,255,1) 51%, rgba(25,33,255,1) 65%, rgba(25,33,255,1) 78%, rgba(25,33,255,1) 78%, rgba(25,33,255,1) 100%);
    background: -webkit-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,1) 12%,rgba(0,0,0,1) 25%,rgba(0,0,0,1) 39%,rgba(44,44,44,1) 50%,rgba(0,0,0,1) 51%,rgba(25,33,255,1) 51%,rgba(25,33,255,1) 65%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 100%);
    background: linear-gradient(to bottom, rgba(0,0,0,1) 0%,rgba(0,0,0,1) 12%,rgba(0,0,0,1) 25%,rgba(0,0,0,1) 39%,rgba(44,44,44,1) 50%,rgba(0,0,0,1) 51%,rgba(25,33,255,1) 51%,rgba(25,33,255,1) 65%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 100%);
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#1921ff',GradientType=0 );
    opacity: 0.5;
    background: rgb(0,0,0);
    background: -moz-linear-gradient(top, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 12%, rgba(0,25,255,1) 25%, rgba(0,25,255,1) 39%, rgba(0,25,255,1) 49%, rgba(0,25,255,1) 50%, rgba(0,25,255,1) 53%, rgba(0,25,255,1) 53%, rgba(25,33,255,1) 54%, rgba(0,25,255,1) 60%, rgba(25,33,255,1) 65%, rgba(25,33,255,1) 78%, rgba(25,33,255,1) 78%, rgba(25,33,255,1) 100%);
    background: -webkit-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,1) 12%,rgba(0,25,255,1) 25%,rgba(0,25,255,1) 39%,rgba(0,25,255,1) 49%,rgba(0,25,255,1) 50%,rgba(0,25,255,1) 53%,rgba(0,25,255,1) 53%,rgba(25,33,255,1) 54%,rgba(0,25,255,1) 60%,rgba(25,33,255,1) 65%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 100%);
    background: linear-gradient(to bottom, rgba(0,0,0,1) 0%,rgba(0,0,0,1) 12%,rgba(0,25,255,1) 25%,rgba(0,25,255,1) 39%,rgba(0,25,255,1) 49%,rgba(0,25,255,1) 50%,rgba(0,25,255,1) 53%,rgba(0,25,255,1) 53%,rgba(25,33,255,1) 54%,rgba(0,25,255,1) 60%,rgba(25,33,255,1) 65%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 78%,rgba(25,33,255,1) 100%);
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#1921ff',GradientType=0 );
    opacity: .8; 
}
matsjoyce
  • 5,744
  • 6
  • 31
  • 38
style28
  • 11
  • 3
0

try using this website to make your colours then it gives you the tag you need

http://www.w3schools.com/colors/colors_picker.asp

CatyG
  • 1
  • 1