Sass how to use ampersand within :not() pseudo class to mean not having a parent class?

Viewed 1065

I need add a condition "not parent class" to the code but it is not working.

Below is what I've tried:

.display-download-icon {
  &:not(.media-2 &){ // if it does not has a parent with .media-2 class
    &:hover {
      .image-overlay {
        display: block;
        opacity: 1;
        z-index: 2;
      }
    }
  }
}

But it translates into the CSS below:

.display-download-icon:not(.media-2 .display-download-icon):hover .image-overlay {
  display: block;
  opacity: 1;
  z-index: 2;
}

The HTML structure is:

<div class="media-2">
  <div class="display-download-icon">
    <div class="image-overlay"></div>
  </div>
</div>

Can you help? Thanks

3 Answers

To begin with, we have to get some things straight. You probably misunderstood this article with the somewhat unfortunate title. Although CSS has a child selector, it lacks the possibility to select parents. As SASS is only a more convenient way to write CSS, SASS cannot have such a thing either. This said, let's move on to your question.

If I understand you correctly, you want to have the hover effect on the image-overlay element only if it is not contained in an element with the class media-2. If that's the case, then you will have to change your approach.

First, define the normal behaviour, i.e the one that is expected for all the elements except the ones with the .media-2 class. Then define a new rule for the .media-2 class children.

SASS

.display-download-icon {
  display: block; /* Just for the demo */
  height: 100px; /* Just for the demo */
  width: 100%; /* Just for the demo */
  border: 1px solid black; /* Just for the demo */
  margin-bottom: 10px; /* Just for the demo */

  > .image-overlay {
    display: block;
    opacity: 1;
    z-index: 2;
  
    height: 100%; /* Just for the demo */
    width: 100%; /* Just for the demo */

    &:hover {
      background: red; /* Just for the demo */
    }
  }
}

.media-2 .display-download-icon > .image-overlay:hover {
  background: none; /* Just for the demo */
}

Snippet

Check this snippet for the functionality

.display-download-icon {
  display: block;
  height: 100px;
  width: 100%;
  border: 1px solid black;
  margin-bottom: 10px;
}

.display-download-icon > .image-overlay {
  display: block;
  opacity: 1;
  z-index: 2;
  
  height: 100%;
  width: 100%;
}

.display-download-icon > .image-overlay:hover {
  background: red;
}

.media-2 .display-download-icon > .image-overlay:hover {
  background: none;
}
<div class="media-1">
  <div class="display-download-icon">
    <div class="image-overlay"></div>
  </div>
</div>

<div class="media-2">
  <div class="display-download-icon">
    <div class="image-overlay"></div>
  </div>
</div>

<div class="media-3">
  <div class="display-download-icon">
    <div class="image-overlay"></div>
  </div>
</div>

I hope that you get the idea.

All the "&" does is act as an alias for the preceding parent selector, which is why you are getting the compiled output you are (because of the "&" after the .media-2 classname) . You just need to remove the last "&" and it will compile fine.

.display-download-icon {
  &:not(.media-2){ // removed the "&"
    &:hover {
      .image-overlay {
        display: block;
        opacity: 1;
        z-index: 2;
      }
    }
  }
}

The above with compile to .display-download-icon:not(.media-2):hover .image-overlay { display: block; opacity: 1; z-index: 2; }

But you can also remove some of that nesting as well to make it more readable

.display-download-icon:not(.media-2):hover {
  .image-overlay {
     display: block;
     opacity: 1;
     z-index: 2;
   }
}

Which will compile to the same output.

Sadly, there's no such thing as a parent selector in CSS, so this isn't really a Sass ampersand issue as much as a core behaviour of CSS.

You'll need to write a dedicated rule like this:

/* base icon styling */
.display-download-icon {
  ...
}

/* separate rule for parent case */
div:not(.media-2) {
  .display-download-icon {
    &:hover {
      .image-overlay {
        display: block;
        opacity: 1;
        z-index: 2;
      }
    }
  }
}
Related