BEM with SASS and :hover - css

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?

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.

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 :)

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;
}

Related

Merging selector #extend issue

I have a problem about "#extend" directive in SCSS.
.header {
.introduction-group {
text-align: center;
color: $white;
width: 70%;
}
.about {
&__description-group {
#extend .introduction-group;
This code block does not work. However,
.header {
&__introduction-group {
text-align: center;
color: $white;
width: 70%;
}
.about {
&__description-group {
#extend .header__introduction-group;
Second one works. Why?
Thank you.
As mentioned here nested classes won't be applied with #extend. Your second code block targets the specified class including the parent prefix. The first code block doesn't, it only targets the nested class.
I made a small codepen demo to illustrate the problem in a simple way. Make sure you checkout the Sass docs for a more comprehensive explanation!
<h1 class="wrong">Test style gone wrong</h1>
<h1 class="right">Test style gone right</h1>
.test {
.nested {
color: red;
}
&-nested {
color: red;
}
}
.wrong {
#extend .test;
#extend .nested;
}
.right {
#extend .test-nested;
}

How to target an after of an element with two classes with SCSS?

I know that when you want to target a div with two classes with SCSS, it should be done like this:
HTML:
<div class="item active">...</div>
SCSS:
.item {
&.active {
/* enter code here */
}
}
But when I want to target an element's after, what then? As in with CSS:
.item.active:after {
/* enter code here */
}
Thanks!
Well you can do it in a few ways
a. This you should use if you want to add some styles to the .active class also.
.item {
background: red;
height: 100px;
width: 100px;
&.active {
&:after{
content: "aaa";
}
}
}
or
b. This you should use if you want just to add some styles to the :after pseudo-element if item has class active
.item {
background: red;
height: 100px;
width: 100px;
&.active:after{
content: "aaa";
}
}
see jsFiddle
Try:
.item {
&.active {
&:after {
/* enter code here */
}
}
}
.item {
&.active {
/* enter code here */
&:after{
/* enter code here */
}
}
}

Using the same values with different classes in different #media without duplicating

I have CSS like that:
.class1 {
display: block;
color: red;
}
.class2 {
display: block;
color: blue
}
#media(max-width:800px) {
.class1-mobile {
display: block;
color: red;
}
.class2-mobile {
display: block;
color: blue
}
}
#media(min-width:800px) {
.class1-desktop {
display: block;
color: red;
}
.class2-desktop {
display: block;
color: blue
}
}
All the properties and their values are the same and the only difference is in class names and media queries. So I'd like to know if there is a way not to duplicate them.
here is a simplified way of doing your media queries using non-mobile first approach (max-width)
.class {
display: block;
/* optional because div is already block element*/
}
.class1 {
color: blue
}
.class2 {
color: red
}
#media(max-width:800px) {
.class1 {
color: red;
}
.class2 {
color: blue
}
}
<div class="class class1">red</div>
<div class="class class2">blue</div>
Use one class.
You can use the same class on multiple elements and You can use multiple
classes on the same element.

Sass Ampersand nesting with pseudo selectors

Trying to utlize the SASS Ampersand to get the following css output. It is not working when we use Ampersand with inside pesudo selector.
CSS
.test:first-child .test-image { display: block; }
SASS
.test {
&:first-child {
display: inline-block;
&-image {
display: block;
}
}
}
Above code basically cascading the -image with first-child.
This is because the ampersand is just concatenating the parent with the child. If you want the compiled CSS to look like your example you need to do this:
.test {
&:first-child &-image{
display: block;
}
}
If you are trying to achieve
.test:first-child .test-image { display: block; }
With your code it is getting compiled as this
.test:first-child-image {
display: block;
}
Instead ,you can simply write it as this .
.test:first-child {
.test-image {
display: block;
}
}
Hope it helps
It sounds like you have mistaken how the ampersand works in Sass. The ampersand is essentially a shorthand for writing each outer selector and then adding the selector after it onto the end. As .test-image is distinct from .test, you should specify it as follows.
.test {
&:first-child {
.test-image {
display: block;
}
}
}
Compiles to
.test:first-child .test-image {
display: block;
}

What does an "&" before a pseudo element in CSS mean?

In the following CSS taken from Twitter Bootstrap what does the ampersand (&) character mean?
.clearfix {
*zoom: 1;
&:before,
&:after {
display: table;
content: "";
}
&:after {
clear: both;
}
}
That's LESS, not CSS.
This syntax allows you to nest selector modifiers.
.clearfix {
&:before {
content: '';
}
}
Will compile to:
.clearfix:before {
content: '';
}
With the &, the nested selectors compile to .clearfix:before.
Without it, they compile to .clearfix :before.
A nested & selects the parent element in both SASS and LESS. It's not just for pseudo elements, it can be used with any kind of selector.
e.g.
h1 {
&.class {
}
}
is equivalent to:
h1.class {
}
Here is an SCSS/LESS example:
a {
text-decoration: underline;
#include padding(15px);
display: inline-block;
& img {
padding-left: 7px;
margin-top: -4px;
}
}
and its equivalent in CSS:
a {
text-decoration: underline;
#include padding(15px);
display: inline-block;
}
a img {
padding-left: 7px;
margin-top: -4px;
}
'&' is useful feature in both Sass and Less preprocessor. For nesting it's used. It is time saver when we compare to CSS.

Resources