0

From my understanding, ::before should appear below the element, and ::after should appear above of the element (in terms of z-index).

In the following example I am trying to make just the background color darker (not the foreground color) when one hovers over the button. Even though I used ::before it still appears in front. Why? I know I could fix it with z-index, but according to this comment which has 6 upvotes:

I think it's better to use :before so you get the right stacking order without playing with z-index.

I should not have to, and the order should be correct?

.parent {
  --my-color: red;  
}
button {
    color: blue;
    background-color: var(--my-color);
    padding: 8px 16px;
    position: relative;
}
button:hover {    
    background-color: transparent;
}
button:hover::before {
    display: block;
    content: "";
    position: absolute;
    top: 0; left: 0; width: 50%; height: 100%; /* width is 50% for debugging (can see whats below) */
    background-color: var(--my-color);
    filter: brightness(80%);
}
<div class="parent">
    <button type="button">CLICK ME</button>
</div>
j08691
  • 204,283
  • 31
  • 260
  • 272
run_the_race
  • 1,344
  • 2
  • 36
  • 62
  • 2
    the `::before` pseudo-element comes before the selected element in the DOM. The `::after` pseudo-element comes after the selected element. It has nothing to do with z-index but the DOM or read direction. – tacoshy Oct 28 '22 at 13:50
  • Elements that come after elements in the DOM stack on top of them. – run_the_race Oct 28 '22 at 13:56
  • 1
    Did you ever consider the comment with 6 upvotes was wrong? – morganney Oct 28 '22 at 14:02
  • 2
    From MDN `In CSS, ::before creates a pseudo-element that is the first child of the selected element.`. You have also `position`ed the pseudo element. Now read this https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index. Bullet item 3. – morganney Oct 28 '22 at 14:05
  • easy self test: `
    1
    2
    `. Which div is on top and which is on the bottom? By default, the 1st div is before the 2nd div in the DOM and as such at the top. Same goes for pseudo-elements. `<::before><::after>`. As said before, the pseudo-elements are inserted before or after the element in the DOM and as such correctly displayed before or after. It has nothing to do with the z-index!
    – tacoshy Oct 28 '22 at 14:09
  • 1
    Regardless this being an awful design, why are you not using a simple linear gradient to achieve this? – connexo Oct 28 '22 at 14:18

1 Answers1

1

There's no difference between ::before and ::after regarding the z-index or z-axis order. By default both will be placed in front of their parent, covering it (if their position is defined accordingly). To achieve z-axis layering beyond that, you need to actually use a z-index (besides a combination of relative and absolute position).

Addition after comment:

In the snippet below there are two variations of the situation. The only difference if that once ::after is used, once ::before, both times without a z-index, and both time with the same result, i.e. the pseudo element covering its parent:

.parent {
  --my-color: red;
}

button {
  color: blue;
  background-color: var(--my-color);
  padding: 8px 16px;
  position: relative;
}

button:hover {
  background-color: transparent;
}

.parent:nth-child(1) button:hover::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: var(--my-color);
  filter: brightness(80%);
}
.parent:nth-child(2) button:hover::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: var(--my-color);
  filter: brightness(80%);
}
<div class="parent">
  <button type="button">CLICK ME</button>
</div>

<div class="parent">
  <button type="button">CLICK ME</button>
</div>

So, to come back to your question in your second comment: Yes, they are wrong - you need to use a z-index to move the pseudo element behind the parent.

So your actual solution should look like this, using a negative z-index: -1; for the pseudo element (and you could as well use ::after here, it doesn't matter...).

.parent {
  --my-color: red;
}

button {
  color: blue;
  background-color: var(--my-color);
  padding: 8px 16px;
  position: relative;
}

button:hover {
  background-color: transparent;
}

button:hover::before {
  content: '';
  position: absolute;
  z-index: -1;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: var(--my-color);
  filter: brightness(80%);
}
<div class="parent">
  <button type="button">CLICK ME</button>
</div>
Johannes
  • 64,305
  • 18
  • 73
  • 130