3

I am trying to closely stick to the BEM methodology and I am frequently running into this issue which I feel it is time to ask for a solution (or opinion) to.

Consider I have a human body; this human body is light by default and therefore, it's arms are also light. My pseudo-code in CSS may look like the below:

.human {
    background-color: white;
}

.human__arm {
    background-color: white;
}

Now we want to add a class for humans who are dark. We can create a modifier for this:

.human--dark {
    background-color: black;
}

The problem I am facing is that by now, the human's arm is still white. We can make them black in two ways that I know of:

Solution A

.human--dark {
    background-color: black;
}

.human--dark .human__arm {
    background-color: black;
}

This solution breaks the BEM methodology by adding specificity to the CSS. However, I feel that this solution is more portable where you are sure to only modify the human and assume that all of its elements will also adapt to the changes of the parent.

Solution B

.human--dark {
    background-color: black;
}

.human__arm--dark {
    background-color: black;
}

I like to think that a 'Block' is a reusable component in BEM. If the human has various other body parts that also need turning white, it seems less maintainable to modify all of the blocks elements in order to achieve this.

While background-color: inherit; may look like a solution to this case, in a real-world application we may have a dark background that contains elements that require light text.

How would we modify the light text to become dark when its parent block becomes light?

Sunny Jangid
  • 578
  • 4
  • 19
dnlwtsn
  • 180
  • 2
  • 10

3 Answers3

0

The first solution is the better one assuming that a dark human may not necessarily have a white arm.

The idea behind BEM syntax is to allow for composability. Writing your selector that way defeats this purpose.

A better way to define these selectors is:

.human {} /* block */
.human--dark {} /* modified block */
.human--arm {} /* block element */

These can be altogether composed this way in markup:

<div class="human human--dark">
   <div class="human__arm">
     Human
   </div>
</div>

For your second question, you may want to take advantage of a CSS preprocessor to compute the inverse of the background color for the text color.

In Sass, it's done this way using a custom mixin:

@mixin duotone ($color) {
  background-color: $color;
  color: invert($color);
}

.human {
    @include duotone(white);
}

.human--dark {
    @include duotone(black);
}

.human__arm {
}
Oluwafemi Sule
  • 36,144
  • 1
  • 56
  • 81
  • 1
    Thank you for your solution Oluwafemi. I'm going to assume you meant that solution B was the better solution, however, I am unsure what your example is showing; A human arm would certainly be an element of a human: `
    `. Please could you clarify?
    – dnlwtsn Oct 25 '17 at 10:40
  • I actually favor solution A since it's closer to how this should be when you think of it that a modified parent element may not have a child element modified on a base attribute like background color. However you need to lose the parent selector like I did in my answer. – Oluwafemi Sule Oct 25 '17 at 10:50
0

In order to modify child elements via parent modifier

we should nest the element selector like below:

Block with an element:

.human {
    background-color: white;
}

.human__arm {
    background-color: white;
}

Block Modifier modifying the child element:

.human--dark {
    background-color: black;
}

.human--dark .human__arm {
    background-color: black;
}

If we want to achieve the same in SCSS, we can use the following approach:

.human {
    background-color: white;
    &__arm {
        background-color: white;
    }
    &--dark { // block modifier
        background-color: black;
        .human { // block
            &__arm {
                background-color: black;
            }
        }
    }
}
Jasneet Dua
  • 3,796
  • 2
  • 10
  • 15
0

If you're using scss you can use interpolation to avoid duplication

.human {
  $c: &;
  background-color: white;

  &__arm {
    background-color: white;
  }

  &--dark { 
    background-color: black;
    #{$c}__arm {
      background-color: black;
    }
  }

}

Also, this may not be relevant to the question, but using background-color: inherit on the arms would mean you wouldn't have to change any css for the arms as it would inherit from the parent.

dantheman
  • 3,189
  • 2
  • 10
  • 18