8

Imagine that we have two layers of background.

  • The bottom layer is green <div class="green"></div>. For simplicity, let's assume it's just a color. But in my project, this layer contains a css animation.

  • And another layer of blue goes on top of it <div class="blue"></div>.

  • Now, I want to have another div that goes on top of both, and it reveals the green background (animation layer in my project).

The closest example I can think of is if you imagine a spotlight. Everything seems black, and the spotlight moves around and reveals the background.

Essentially, that's what I have:

<div class="green">
    <div class="blue">
        <div class="reveal"></div>
    </div>
</div> 

It will look something like this. Just remember, the green layer is an animation in my project.

enter image description here

QUESTION: how can I complete the .reveal styles to achieve the above behavior.

  • First div - draws .green background (animation)
  • Second dv - draws .blue background goes on top of it
  • Third/Fourth/... divs - Goes on top of both, but it reveals whatever the background First div draws

Note: First and Second div covers 100% of the available width and height.

.green {
    background-color: #159c82;
    width: 100vw;
    height: 100vh;
}

.blue {
    background-color: #1b4287;
    // I could change this to a sibling div and use,
    // position: absolute; but that seems unnecessary
    width: 100%;
    height: 100%;
}

.reveal {
    margin-top: 10px;
    margin-left: 10px;
    width: 200px;
    height: 50px;
    // not sure what else to put here to make it work
}

<div class="green">
    <div class="blue">
        <div class="reveal"></div>
    </div>
</div>

P.S. There is one approach I found that I did not like at all.

enter image description here

Michael Dimmitt
  • 826
  • 10
  • 23
Hafiz Temuri
  • 3,882
  • 6
  • 41
  • 66
  • Can I put the `
    ` outside the `
    `?
    – MARSHMALLOW May 02 '20 at 00:30
  • 1
    what exactly is your question here? – Dekel May 02 '20 at 00:45
  • @Dekel I am not sure how I can complete the styles of `.reveal` to achieve the mentioned behavior. I edited the OP – Hafiz Temuri May 02 '20 at 00:48
  • @MARSHMALLOW I guess so, as long as I have two full layers of background. I am planning to animate the `green` background in my project. – Hafiz Temuri May 02 '20 at 00:50
  • if we omit the dead browser IE, all the other support it: https://caniuse.com/#feat=css-masks – Temani Afif May 06 '20 at 20:08
  • Can you show an example animation in your question? Maybe it is an svg animation or a css animation? Knowing the type of animation will be helpful. It does not need to be your exact animation. – Michael Dimmitt May 08 '20 at 20:59
  • @MichaelDimmitt Its css animation – Hafiz Temuri May 08 '20 at 21:46
  • The first part (holes in elements) is a duplicate of https://stackoverflow.com/questions/8286550/transparent-hollow-or-cut-out-circle. The second part (animation) is almost certainly a duplicate of an animation canonical somewhere. I don't have a good one ready at the moment, so I won't dupehammer for now. – TylerH Mar 01 '23 at 15:49

5 Answers5

13

Use mask to create a hole and no need for the reveal div. You can later change the size and position to have the animation you want:

.green {
  background: linear-gradient(45deg,#159c82,red);
  height: 100vh;
}

.blue {
  background:#1b4287;
  height: 100%;
  -webkit-mask:
    /* you adjust this */
    linear-gradient(#fff,#fff) 
     50px 50px/ /*left top*/
     200px 20px, /*width height*/
    /**/
    linear-gradient(#fff,#fff); 
  -webkit-mask-repeat:no-repeat;    
  -webkit-mask-composite: destination-out;
  
  mask:
    /* you adjust this */
    linear-gradient(#fff,#fff) 
     50px 50px/ /*left top*/
     200px 20px, /*width height*/
    /**/
    linear-gradient(#fff,#fff);
  mask-repeat:no-repeat;    
  mask-composite:exclude;
  transition:.5s;
}
.blue:hover {
  -webkit-mask-position:100px 100px,0 0;
          mask-position:100px 150px,0 0;
  -webkit-mask-size:300px 50px,auto;
          mask-size:300px 50px,auto;
}

body {
  margin: 0;
}
<div class="green">
  <div class="blue">
  </div>
</div>

You can also add as many mask as you want:

.green {
  background: url(https://picsum.photos/id/1018/800/800) center/cover;
  height: 100vh;
}

.blue {
  background:#1b4287;
  height: 100%;
  -webkit-mask:
    /* 3rd mask */
    radial-gradient(farthest-side,#fff 99%,transparent) 
     top 50px right 50px/ 
     100px 100px,
    /**/
    /* 2nd mask */
    linear-gradient(#fff,#fff) 
     bottom 50px right 50px/ 
     300px 20px,
    /**/
    /* 1st mask */
    linear-gradient(#fff,#fff) 
     50px 50px/ 
     200px 20px,
    /**/
    linear-gradient(#fff,#fff);
  -webkit-mask-repeat:no-repeat;    
  -webkit-mask-composite: destination-out;
  
  mask:
    /* 3rd mask */
    radial-gradient(farthest-side,#fff 99%,transparent) 
     top 50px right 50px/ 
     100px 100px,
    /**/
    /* 2nd mask */
    linear-gradient(#fff,#fff) 
     bottom 50px right 50px/ 
     300px 20px,
    /**/
    /* 1st mask */
    linear-gradient(#fff,#fff) 
     50px 50px/ 
     200px 20px,
    /**/
    linear-gradient(#fff,#fff);
  mask-repeat:no-repeat;    
  mask-composite:exclude;
  transition:.5s;
}
.blue:hover {
  -webkit-mask-position:
            100px 100px,
            bottom 100px left 50px,
            top 50px right 50px,
            0 0;
          mask-position:
            100px 100px,
            bottom 100px left 50px,
            top 50px right 50px,
            0 0;
  -webkit-mask-size:
            150px 150px,
            50px 50px,
            300px 50px,
            auto;
          mask-size:
            150px 150px,
            50px 50px,
            300px 50px,
            auto;
}

body {
  margin: 0;
}
<div class="green">
  <div class="blue">
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
1

A techhnique that will work, with an even simpler layout, is box-shadow

.layer1 {
  width: 100%;
  height: 100%;
  position: absolute;
  background-image: linear-gradient(yellow, blue);
  background-size: 200% 200%;
  animation: bkg 2s infinite alternate;
  background-origin: padding-box;
}

.layer2 {
  width: 200px;
  height: 200px;
  position: absolute;
  background: transparent;
  border-radius: 30px;
  left: 20px;
  top: 40px;
  box-shadow: 0px 0px 0px 10000px blue;
}


@keyframes bkg {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 100% 100%
  }
}
<div class="layer1"></div>
<div class="layer2">
</div>

Also, you have a posibility to achieve this using blending.

The main disadvantage is that this uses hard-light , son the colors used in layer2 are limited to having 0 or 255 for the primary colors (red blue and green).

It will work for pure red (255, 0,0), green (0, 255, 0), blue (0, 0, 255), and also (255, 255, 0), (255, 0, 255), (0, 255, 255)

But it has the advantage that you can set severial divs to act as windows

.layer1 {
  width: 100%;
  height: 100%;
  position: absolute;
  background-image: linear-gradient(yellow, blue);
  background-size: 200% 200%;
  animation: bkg 2s infinite alternate;
  background-origin: padding-box;
}

.layer2 {
  width: 100%;
  height: 100%;
  position: absolute;
  background: rgba(0, 255, 0);
  mix-blend-mode: hard-light;
}

.layer3 {
  width: 200px;
  height: 200px;
  position: absolute;
  background: grey;
  border-radius: 30px;
  left: 20px;
  top: 40px;
}

@keyframes bkg {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 100% 100%
  }
}
<div class="layer1"></div>
<div class="layer2">
  <div class="layer3"></div>
</div>

This will work also for multiple divs:

.layer1 {
  width: 100%;
  height: 100%;
  position: absolute;
  background-image: linear-gradient(yellow, blue);
  background-size: 200% 200%;
  animation: bkg 2s infinite alternate;
  background-origin: padding-box;
}

.layer2 {
  width: 100%;
  height: 100%;
  position: absolute;
  background: rgba(0, 255, 0);
  mix-blend-mode: hard-light;
}

.layer3 {
  width: 200px;
  height: 200px;
  position: absolute;
  background: grey;
  border-radius: 30px;
  left: 20px;
  top: 40px;
}

.layer3:nth-child(2) {
  left: 120px;
  top: 80px;
}

@keyframes bkg {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 100% 100%
  }
}
<div class="layer1"></div>
<div class="layer2">
  <div class="layer3"></div>
  <div class="layer3"></div>
</div>
vals
  • 61,425
  • 11
  • 89
  • 138
0

Do you want to put the reveal div in this postion only, or show the green color from the bottom layer?

for only the positon you can add realtive position in your blue div and absolute position in your reveal div with top and left values.

.green {
    background-color: #159c82;
    width: 100vw;
    height: 100vh;
}

.blue {
    background-color: #1b4287;
    width: 100%;
    height: 100%;

    position: relative;
}

.reveal {
    position: absolute;
    top: 10px;
    left: 10px;

    width: 200px;
    height: 50px;
}
Alpha
  • 21
  • 4
0

If I understand you correctly you probably should use a css variable. https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties

Editing your example we get the following.

:root {
  --bottom-layer-color: #159c82
}

.green {
  background-color: var(--bottom-layer-color);
  width: 100vw;
  height: 100vh;
}

.blue {
  background-color: #1b4287;
  width: 100%;
  height: 100%;
}

.reveal {
  margin-top: 10px;
  margin-left: 10px;
  width: 200px;
  height: 50px;
  background-color: var(--bottom-layer-color)
}

<div class="green">
  <div class="blue">
    <div class="reveal"></div>
  </div>
</div>

cheers,

to change the color with css:

/* Use a later css file to redeclare the root variable,    */
/* this will override previously declared css. */
/* https://css-tricks.com/precedence-css-order-css-matters/ */
:root {
  --bottom-layer-color: powderblue
}

to change the color with javascript:

const eStyle = document.documentElement.style
eStyle.setProperty('--top-bar-height', '263px');

You can change a lot of things with css variables. Not just background-colors.

For instance

root: {
  --bottom-layer-color: #159c82
  --bottom-layer-radius: 50%;
  /* this would make the bottom layer a circle. And reaveal a circle. */
}

.green {
  background-color: var(--bottom-layer-color)
  border-radius: var(--bottom-layer-color)
  width: 100vw;
  height: 100vh;
}

.reveal {
  background-color: var(--bottom-layer-color)
  border-radius: var(--bottom-layer-color)
}

/* Set --bottom-layer-radius back to 0 to make both items square again. */
Michael Dimmitt
  • 826
  • 10
  • 23
  • 1
    No, this will not work for me. I have an animation at the bottom layer. So, it has to be exactly whatever the bottom layer is. – Hafiz Temuri May 08 '20 at 20:23
-3

This is one of the easiest things to do in CSS

<Style>
.blue {
width: 100%;
height: 300px;
background-color: lightblue;
}
</style>
<div class="blue">
</div>