Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BEM approach for an element than can belong and look different depending on the Block?

Tags:

css

sass

less

bem

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.

like image 282
Chris Avatar asked Apr 25 '16 02:04

Chris


People also ask

What is the BEM method?

What is BEM? BEM is a front-end naming method for organizing and naming CSS classes. The Block, Element, Modifier methodology is a popular naming convention for class names in HTML and CSS. It helps to write clean CSS by following some simple rules.

What are the benefits of using the BEM convention in HTML and CSS?

The idea behind BEM is for developers to write HTML and CSS in a way that avoids inheritance issues. As we all know, CSS in bigger projects can become really messy really quickly when not managed and structured correctly. BEM helps with this. So we have an element, the block component with the class .

What is the BEM naming convention?

BEM stands for block, element, and modifier. It's a naming convention that divides the user interface into small, reusable components that will prepare us for the changes in design of our websites. This convention also enhances the cross-site copy/paste feature by helping make elements reusable components.

What is a block in bem?

A BEM class name includes up to three parts. Block: The outermost parent element of the component is defined as the block. Element: Inside of the component may be one or more children called elements. Modifier: Either a block or element may have a variation signified by a modifier.


1 Answers

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.

like image 64
zzzzBov Avatar answered Sep 25 '22 20:09

zzzzBov