13

Let's say I have a styled checkbox (think material design, where there is a bit going on to achieve the desired checkbox style). Which block is responsible for modifying a parent-dependent child block?

Example Component - Checkbox

So I have the following:

<div class="Checkbox">
    <label class="Checkbox__label">
        <input class="Checkbox__input" type="checkbox" checked=true />
        <span class="Checkbox__icon glyphicon glyphicon-ok"></span>
        <span class="Checkbox__text">{label}</span>
    </label>
</div>

I style up each element within the block for the base checkbox. Within the context of the application, the checkbox block can live in many other blocks (with their own BEM structures).

Example of other blocks

The checkbox with have slightly difference appearance when say within the "Compact Panel":

<div class="Panel Panel--compact">
    <p>Disclaimer.. [text]</p>
    <Checkbox label="I Agree" /> 
</div> 

Option One - Parent "knows" about child

So.. should the Compact Panel be "aware" of the various children blocks, and style them, so:

// Checkbox.scss 
.Checkbox {
    margin: 15px;
    // .. other
}


// Panel.scss
.Panel {
    &.Panel--compact {
        margin: 0;
        padding: 2px;
    }
    &.Panel--compact .Checkbox {
        margin: 0;
        padding: 1px;
    }
}

Option Two - Child "knows" about parent

Or, the panel has zero awareness, and the checkbox checks for parent scope.

// Checkbox.scss 
.Checkbox {
    margin: 15px;
    padding: 15px;
    // .. other
}
.Panel.Panel--compact .Checkbox {
    margin: 0;
    padding: 1px;
}


// Panel.scss
.Panel {
    &.Panel--compact {
        margin: 0;
        padding: 2px;
    }
}

Option Three - ?

Maybe there are other options.

Chris
  • 54,599
  • 30
  • 149
  • 186

4 Answers4

3

Usually with BEM if they look different, they are different.

Usually.

There are a number of different choices for handling context and state with BEM. Each has different pros and cons, so which you use will depend heavily on your use case.

The first option I'll mention is to use descendant selectors. You've already identified this choice, and are running into the usual problem of "where does the code belong?"


For the following examples, I'm going to rely on LESS syntax, this is only to make it easier for me to demonstrate relationships in the code.


Descendant Selector

If you're going to use a descendant selector, I recommend that the code be grouped with the child block.

widget.less
.widget {
  &__container {
    ...
  }
  ...
}
checkbox.less
.checkbox {
  &__label {
    ...
  }
  ...

  // using inversion to override checkbox styles in a widget
  // this will render to `.widget .checkbox` instead of
  // `.checkbox .widget` due to the `&` placement
  .widget & {
  }
}

The reason I recommend associating the styles with the inner block is because the styles will affect the checkbox, and the cascade order will be important.

If the styles were associated with the parent, reordering the parent styles relative to the child styles could adversely affect how the styles render.

Consider this inclusion order:

site.less
@import 'widget';
@import 'checkbox';

If the styles were part of the widget, they could be overridden by a selector of equal specificity in checkbox.less.

Modifiers

I recommend using modifiers for state. I don't generally consider position or context to be "state", so modifiers may not be appropriate. Additionally, multiple modifiers on the same element can be difficult to reason about and therefor difficult to style.

Assuming you're not using a modifier on the checkbox block, then it may be simpler to add the modifier for the case where it's used in a panel.

.checkbox {
  &__label {
    ...defaults...
  }
  ...defaults...

  &--alt {
    &__label {
      ...overrides...
    }
    ...overrides...
  }
}

Of course, this requires that the markup be updated for the particular case where it's used in a panel, but then it also opens you up to using the checkbox with the same styles elsewhere.

Different Selector

I'm going to reiterate my first point: If they look different they are different.

This doesn't mean you have to start from scratch on the checkbox. BEM allows for object oriented styles. Come up with a new name, and extend* the checkbox:

checkbox.less
.checkbox {
  &__label {
    ...
  }
  ...
}
checkbox-2.less
@import (reference) 'checkbox';
.checkbox-2 {
  .checkbox;

  &__label {
    ...overrides...
  }
  ...overrides...
}

* in LESS I'm using a mixin for this as it's generally better suited toward extending and overriding styles than using the :extend feature of the language. Feel free to use the :extend feature, just be aware that selector order will matter.

Refactor the Need Away

Sometimes I run into cases where I want to use a descendant selector or modifier because I need to bump a block for positioning purposes in a container.

In these cases, I often find that the container itself is what should be changed. I can usually tell that it's the container when I need to update the child to have different:

  • margins
  • padding
  • position
  • top, right, bottom, left
  • width
  • height
  • flex

Refactoring comes with other challenges, however I often end up using container divs to normalize the insertion region for blocks that contain other blocks; YMMV.


tl;dr: Which Should I Pick?

Can you (reasonably) update the markup?

YES: If you can easily update the markup to use different classes, I'd recommend extending your checkbox block as a new block. Naming things is hard though, so be sure to document which one is which somewhere.

NO: If you can't easily update the classes, using modifiers wouldn't be a great choice either. I'd recommend skipping that one, and falling back to the good ol' descendant selector. In BEM you really want to avoid descendant selectors, but sometimes they're the right tool for the job.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • Dude - with 5 mins on the clock, you have nailed the answer. Esp. this line "The reason I recommend associating the styles with the inner block is because the styles will affect the checkbox, and the cascade order will be important." -- This is the sound logic I was after - Awesome – Chris May 09 '16 at 13:53
  • 1
    @Chris, I'd have answered this days ago if I'd seen it. It's a good question, and it touches on some of the advanced parts of BEM that make it really powerful. – zzzzBov May 09 '16 at 13:57
  • 1
    The timer makes it more dramatic. Thanks again, I will read over it a few times in the coming days to make sure it sinks in. I think powerful answers like these step through the thinking behind a decision as well as the decision itself – Chris May 09 '16 at 14:01
1

According to this documentation, the use of nested selectors should be avoided in BEM, but "is appropriate for changing elements depending on the state of a block or its assigned theme". This means that you can use a block's modifier to style its elements, but I couldn't find any mention of using a block's modifier to style its child blocks.

I think the best approach would be to add modifiers to both .Pannel and .Checkbox blocks, like: .Panel--compact and .Checkbox--context-compact. This way you won't create a dependency between them, which is a BEM principle.

If somehow you can't add a modifier to .Checkbox depending on it's context, I think the closest option would be the second one, where the child behaves differently depending on the state of its parent.

Leonardo Favre
  • 308
  • 2
  • 6
1

This is the "right" way:

I'm quite surprised why the accepted answer doesn't mention BEM "mixes". In these scenarios, that is, when a block should change style when used within another block, the official BEM documentation recommends using a block AND an element class on the same DOM node.

So, in your case, this would be the markup:

<div class="Panel Panel--compact">
    <p>Disclaimer.. [text]</p>
    <div class="Checkbox Panel__checkbox" /> <!-- Here is the change -->
</div> 

And this would be the CSS/SCSS:

// Checkbox.scss 
.Checkbox {
    margin: 15px;
    padding: 15px;
    // .. other
}


// Panel.scss
.Panel {
    &__checkbox {
        margin: 0;
        padding: 2px;
    }
}

It's really that easy and simple. The other answers are overcomplicating this unnecessarily, and I don't know why, the BEM documentation has a clear answer to this question. You can also find many articles online that explain the very same technique I just described.

Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
-1

I recently started a react project and I would like to share my experience on styling react components.

Cascading styles within react components are really confusing. So first thing is try to write class per element. If you want you can wrap using a main class which defines the component.

.panel {
  .checkbox {

  }
}

I use css-modules. This is super awesome. This tool can create unique classes so you don't need to worry about duplicating same name in other components. You can learn more from their git page and there are many articles online.

If you are using sass and need to use variables you can define a separate sass file in a different folder (let's say ../assets/variables.scss), in your component scss file you can import it and use all variables. Same goes to mixins and functions.

In your case don't worry about child and parent relationship, write single classes for all elements you need to style. Sometime you may need to target children using parent.

A good read

Ryxle
  • 880
  • 7
  • 21
  • 1
    this is not BEM methodology. The OP requested for a BEM approach to this – nicholaswmin May 09 '16 at 13:26
  • 1
    Your very last sentence is what I am looking for more information on "sometimes you may need to target children using the parent" - a detailed answer outling the how and why around parent children relationships in BEM is basically what I am after – Chris May 09 '16 at 13:47