I am trying to achieve the following:
I have the problem that the .rocket::before
element does not want to be displayed behind the .rocket
element.
I have the following code:
.main-nav-card {
background: linear-gradient(
to bottom,
transparent 50%,
#1c7ed6 50%
);
background-size: 100% 200%;
height: 70rem;
border: 1px solid currentColor;
padding: 4.8rem 9.6rem;
border-radius: var(--border-radius-large);
position: relative;
z-index: 2;
}
.main-nav-card:hover {
animation-name: fillCard;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes fillCard {
from {
background-position: top;
}
to {
background-position: bottom;
}
}
.rocket {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background-color: #000;
height: 50px;
width: 25px;
border-radius: 50% 50% 0 0;
/* z-index: 20; */
}
.rocket::before {
content: "";
position: absolute;
bottom: 0;
right: -15px;
height: 20px;
width: 55px;
border-radius: 50% 50% 0 0;
background-color: #f03e3e;
z-index: -1;
}
.rocket::after {
content: "";
position: absolute;
left: 0;
right: 0;
margin: auto;
bottom: -4px;
height: 4px;
width: 15px;
border-radius: 0 0 2px 2px;
background-color: #f03e3e;
z-index: -1;
}
.main-nav-card:hover .rocket {
display: block;
animation-name: flyRocket;
animation-duration: 3s;
}
@keyframes flyRocket {
from {
transform: translateY(1000%);
/* z-index: 9999; */
}
to {
transform: translateY(0);
/* z-index: 9999; */
}
}
.card-title {
font-size: 3.2rem;
}
.window {
height: 10px;
width: 10px;
background-color: var(--color-primary);
border-radius: 50%;
position: relative;
top: 17px;
left: 7.5px;
/* transform: translateX(25%); */
}
<div class="main-nav-card">
<p class="card-title">Introduction</p>
<div class="rocket">
<div class="window"></div>
</div>
</div>
What am I doing wrong?
I believe this issue is due to the CSS "transform" property creating a new stacking context on the rocket, which prevents child elements and pseudo elements from appearing behind the stacking context root (.rocket
). As a result, the fix is to put your .rocket
element in some wrapper element and apply the transform on the wrapper instead. That way, the stacking context is created on the wrapper rather than on the rocket directly, enabling any child and pseudo elements to appear behind it once again.
However, there is a better way to do this, using the translateZ()
and preserve-3d
. Specifically, instead of managing z-indexes on your pseudo elements, use transform: translateZ(-1px);
to put them behind the parent element. Then, all you have to do to make it work is apply transform-style: preserve-3d;
to the .rocket
selector. This works in all most modern browsers. Here is the full code:
.main-nav-card {
background: linear-gradient(to bottom, transparent 50%, #1c7ed6 50%);
background-size: 100% 200%;
height: 70rem;
border: 1px solid currentColor;
padding: 4.8rem 9.6rem;
border-radius: var(--border-radius-large);
position: relative;
z-index: 2;
}
.main-nav-card:hover {
animation-name: fillCard;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes fillCard {
from {
background-position: top;
}
to {
background-position: bottom;
}
}
.rocket {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background-color: #000;
height: 50px;
width: 25px;
border-radius: 50% 50% 0 0;
transform-style: preserve-3d;
}
.rocket::before {
content: "";
position: absolute;
bottom: 0;
right: -15px;
height: 20px;
width: 55px;
border-radius: 50% 50% 0 0;
background-color: #f03e3e;
transform: translateZ(-1px);
}
.rocket::after {
content: "";
position: absolute;
left: 0;
right: 0;
margin: auto;
bottom: -4px;
height: 4px;
width: 15px;
border-radius: 0 0 2px 2px;
background-color: #f03e3e;
transform: translateZ(-1px);
}
.main-nav-card:hover .rocket {
display: block;
animation-name: flyRocket;
animation-duration: 3s;
}
@keyframes flyRocket {
from {
transform: translateY(1000%);
}
to {
transform: translateY(0);
}
}
.card-title {
font-size: 3.2rem;
}
.window {
height: 10px;
width: 10px;
background-color: var(--color-primary);
border-radius: 50%;
position: relative;
top: 17px;
left: 7.5px;
/* transform: translateX(25%); */
}
<div class="main-nav-card">
<p class="card-title">Introduction</p>
<div class="rocket">
<div class="window"></div>
</div>
</div>
Edit: Apparently this doesn't work for iOS Safari because perserve-3d
is somewhat broken there. So if you really want to support all browsers, you should use the wrapper element technique.