1

document.querySelector( 'style' ).innerHTML += `
   /* rotation animations*/
  .rotate_x, .rotate_y, span {
    animation-name: rotateX;
    animation-duration: calc( var( --duration ) * 1s );
    animation-timing-function: linear;
    animation-iteration-count: infinite;
  }
  @keyframes rotateX {
    0% { transform: rotateX( 0deg ); }
    100% { transform: rotateX( 360deg ); }  
  }
  .rotate_y { animation-name: rotateY; }
  @keyframes rotateY {
    0% { transform: rotateY( 0deg ); }
    100% { transform: rotateY( 360deg ); }  
  }
  
  /* hidden circle inside cube */
  hr {
    position: absolute; 
    z-index: 1000; 
    border-style: none; 
    border-radius: 10rem;
    width: 10rem; height: 10rem; 
    background-color: rgba( 50,200,150,1 );
  }  
`;
/* main */
:root {
  font-size: 0.65rem; /* decreased font-size to fit cube in viewport */
  --duration: 10; 
  --quarter_time: calc( var( --duration ) / 4 ); 
  --eighth_time: calc( var( --duration ) / 8 );
  --bright: 125; --mid: 100; --dark: 75;
}
* {
  box-sizing: border-box;
  transform-style: preserve-3d;
  margin: 0; padding: 0;
}
html, body { height: 100%; }
body, span {
  display: flex;
  justify-content: center; 
  align-items: center;
}
body { perspective: 30rem; }
section, div, span {
  position: absolute;
  width: 10rem; height: 10rem;
}
span {
  background-color: rgba( 50,150,200,1 );
}
div span:nth-of-type( 1 ) { transform: translateZ( 5rem ); }
div span:nth-of-type( 2 ) { transform: rotateY( 180deg ) translateZ( 5rem ); }
.left_right { transform: rotateY( 90deg ); }
.top_bottom { transform: rotateX( 90deg ); }
/* Note: Some CSS was moved to the HTML and Javascript windows of the snippet
only to eliminate scrolling and for readability on desktops.
<style>
   /* face brightness animations*/
  .x_lighting span { animation-name: frontFaces; }
  .front_back span, .top_bottom span {
    animation-duration: calc( var( --quarter_time ) * 1s );
    animation-timing-function: ease-in-out; animation-direction: alternate;
  }
  .top_bottom span, .x_y_lighting .left_right span { 
    animation-direction: alternate-reverse; 
  }
  @keyframes frontFaces {
    0% { filter: brightness( calc( var( --bright ) * 1% ) ); }
    100% { filter: brightness( calc( var( --dark ) * 1% ) ); }    
  }
  .x_y_lighting .left_right span {
    animation-name: sideFaces; animation-duration: calc( var( --eighth_time ) * 1s );
  }
  @keyframes sideFaces {
    0% { filter: brightness( calc( var( --mid ) * 1% ) ); }
    100% { filter: brightness( calc( var( --dark ) * 1% ) ); } 
  }
</style>
<body class='x_lighting x_y_lighting'>
  <section class='cube_cont rotate_x'>
    <section class='cube_wrap rotate_y'>
      <section class='cube rotate_y'> <hr>
        <div class='front_back'> <span></span><span></span> </div>
        <div class='left_right'> <span></span><span></span> </div>
        <div class='top_bottom'> <span></span><span></span> </div>
      </section>
    </section>
  </section>
</body>

When you have a 3D constructed object in CSS ( such as a cube ) that has children elements located "inside" of that 3D object in 3D space and you want to show that child element through the parent or "on top of" the parent element - what is the best way to achieve such an effect?

This might be best understood visually so in the snippet above inside of the cube I have an hr element in the HTML that I styled as a green circle. It can't be seen because its directly inside of the opaque cube but when you change the opacity of the cube you see it through the faces:

enter image description here

One line of code that appears ignored is the z-index I set on the hr:

  /* hidden circle inside cube */
  hr {
    position: absolute; 
    z-index: 1000; 
    border-style: none; 
    border-radius: 10rem;
    width: 10rem; height: 10rem; 
    background-color: rgba( 50,200,150,1 );
  }  

I set the z-index to 1000 however the expected result isn't displayed in the snippet above. To be clear: How do I set the z-index of the child element greater than the parent element so the circle shows on top of the cube but keeps it's position in 3D space? ( without making the cube transparent )

There are questions like this: how-to-get-a-child-element-to-show-behind-lower-z-index-than-its-parent that have answers that rely on hacks using 3D transforms. Yet I can't do that here because my child and parent are 3D transformed objects themselves. Also these questions aren't exactly asking the same thing here. So what's a CSS solution to going about this?

Lynel Hudson
  • 2,335
  • 1
  • 18
  • 34
  • You need to make the 6 faces of the box (semi) transparent by changing the alpha value of the `rgba()` to less than 1 (4th parameter)... – Rene van der Lende Mar 12 '21 at 02:36
  • @RenevanderLende In my example with the gif I show a semi-transparent cube by changing the alpha value on the rgba in the CSS. The desired result however is to do this without transparency or opacity. *I'm trying to put something like a 'tool tip' on top of a 3dimensional object*. – Lynel Hudson Mar 12 '21 at 02:44
  • Yep, missed the second part of your question. Sorry for that. Remember that the `z-index` is just an instruction for HTML how to stack elements on top of eachother, not its location in 3D space. You need to use something like `hr { translateZ(-2.5rem); }` as the circle is in the center of the cube (which sides are translateZ 5rem). I have not tested this, so you need to fiddle with that value. Try using 5.5rem (- or +) for the `hr` and go from there. – Rene van der Lende Mar 12 '21 at 03:01
  • @RenevanderLende Yes the more things I try I'm realizing I might have to create this illusion using 3D translations. I've tried all types of blend-modes but they always break the 3D elements. The big problem however; is I want the circle to remain in the center of the cube - so translating in the Z direction will compromise it's position in 3D space. Was hoping for a solution that doesn't change it's location but only gives it the appearance of being stacked infront of the cube. Like how sometimes a video game shows objects of interest that are spatially behind the character at the forefront. – Lynel Hudson Mar 12 '21 at 03:09
  • Actually managed to accomplish desired result with some messy trickery. If camera angle changes though it breaks the illusion: https://jsfiddle.net/z89ysh4b/ – Lynel Hudson Mar 12 '21 at 03:16
  • From what I understand is that your requirement is impossible: keep the circle inside the cube while the faces of the cube remain opaque and make the circle visible. In real life you would place some dish inside a closed box and ask to see the dish. Not going to happen...you need to compromise... Place the `hr` inside one of the face `span`s and see what happens. – Rene van der Lende Mar 12 '21 at 03:25
  • @RenevanderLende check the fiddle. In "real life" what you would do is move the "dish" up closer and closer to the camera until it 'covered' the box. Sync it's movements with the spinning cube and to prevent the dish being so large as to block the cube from view due to its proximity to the camera - you would use a super tiny one. And thus the illusion is complete. This is essentially what I did in my fiddle. No "camera" changes and should be fine. This is my working solution. Its like when you frame a picture to put a tall building in between your fingers. A play on perspective. – Lynel Hudson Mar 12 '21 at 04:18

0 Answers0