20

What's the proper way of declaring active/focus/hover states using BEM with SASS? For example, I have this structure:

<div class="card">
    <img class="card__image" src="..." alt="">
    <div class="card__overlay">
        <div class="card__title"></div>
    </div>
</div>    

And the SCSS:

.card {
   &__image {
   }

   &__overlay {
   }

   &__title {
   }
}

And I want to modify the elements when hovering on the block. This doesn't work:

.card {
   &__overlay {
       display: none;
   }

   &:hover {
       &__overlay {
           display: block;
       }
   }
}

And having to write the whole .project__image just for this one instance seems wrong.

Is there any other way to accomplish this?

so4t
  • 379
  • 1
  • 3
  • 5
  • The problem here is what, exactly? If you look at the compiled results, the answer seems pretty obvious. Try running the results through the validator if you still don't understand what's wrong with the CSS. – cimmanon Dec 01 '15 at 13:55
  • It works. Check [this](http://sassmeister.com/gist/a5bb2d884cb0c2d12f9e). – Alex Dec 01 '15 at 13:56
  • 1
    It's the second code section that doesn't work, not the first. The one with the hover state. – so4t Dec 01 '15 at 14:01
  • 1
    "Doesn't work" does not explain what the problem is. http://importblogkit.com/2015/07/does-not-work/ – cimmanon Dec 01 '15 at 14:02
  • Sorry. It doesn't work because it compiles to `.card:hover__overlay` instead of the desired `.card:hover .card__overlay`. – so4t Dec 01 '15 at 15:30

3 Answers3

53

You can achieve the desired result using the Sass ampersand selector, without using variables or interpolation.

Referencing parent selectors by using the ampersand (&) can be a powerful tool, if used right. There are simple uses of this feature as well as some very complex uses of this feature.

For example:

.card { 

    &__overlay {
        display:none;
    }

    &:hover & {
        &__overlay  {
            display: block;
        }   
    }
}

Results in:

.card__overlay {
    display: none;
}

.card:hover .card__overlay {
    display: block;
}

This code uses fewer language constructs (e.g. no use of variables or interpolation) and so is arguably a cleaner implementation.

aaronmarruk
  • 683
  • 5
  • 7
24

Read more about interpolation: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#interpolation_

SCSS:

.card {
    $root: &;

    &__overlay {
        display: none;

        #{$root}:hover & {
            display: block;
        }
    }
}

RESULT:

.card__overlay {
  display: none;
}

.card:hover .card__overlay {
  display: block;
}

PS. It is similar to @alireza safian post, but with this way you don't need to duplicate class name. Variable $root do it for you :)

Andy
  • 282
  • 1
  • 4
  • 11
mwl
  • 1,448
  • 14
  • 18
4

Alternative way:

Use variable instead of ampersand for third level.

Link

SASS:

$className: card;
.card {
   &__overlay {
       display: none;
   }

   &:hover {

       .#{$className}__overlay {
           display: block;
       }
   }
}

CSS:

.card__overlay {
  display: none;
}

.card:hover .card__overlay {
  display: block;
}
Alex
  • 8,461
  • 6
  • 37
  • 49