How to Properly Set an Element's Scope Using Bem

How to properly position a page scope block with BEM

You should separate the two scopes header and social-links as parent / child. It is important that BEM blocks are isolated. When using two classes from different blocks on the same elements, we risks future interference, and regression, when we will update one block without checking the other one.

The separation is also important to be able to move the social-links block.

// Show the fixed header on scrollvar fixedHeader = document.querySelector('.page__social-links');
document.addEventListener('scroll', function() { if (window.scrollY > 100) { fixedHeader.classList.remove('page__social-links--hidden'); } else { fixedHeader.classList.add('page__social-links--hidden'); }});
body {  margin: 0;  height: 300vh;}
.page__social-links { position: fixed; top: 0; left: 0; right: 0; background: hotpink;}
/* Hide the fixed header by default */.page__social-links--hidden { display: none;}
.header { border-bottom: 1px solid hotpink;}
.social-links { text-align: center;}
.social-links__link { padding: 0 0.5em; line-height: 3em;}
<div class="page">  <div class="page__header">    <header class="header hero" role="banner">      <img class="header__logo" src="assets/logo.png" alt="Sample Image" />
<div class="header__social-links"> <div class="social-links"> <a class="link social-links__link" href="#"> twitter </a> <a class="link social-links__link social-links__link--last" href="#"> facebook </a> </div> </div> </header> </div> <div class="page__social-links page__social-links--hidden"> <div class="social-links"> <a class="link social-links__link" href="#"> twitter </a> <a class="link social-links__link social-links__link--last" href="#"> facebook </a> </div> </div></div>

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

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.

Confusion with BEM class naming convention. One level deeper

The menu should be a class unto itself so .menu should suffice. If it's a menu that is ONLY in a header and never anywhere else, then .header-menu. Then you can point to the menu directly without going through the header class.

If you prefer to do it the way you outlined, then .header_menu.

How to format html according to BEM naming concept?

I guess I'd probably go more in this direction, but even this isn't really perfect:

<div class="container">
<div class="profile">
<p class="profile__message></p>
<div class="profile__item">
<div class="profile__attribute profile__image">
<a class="thumb"><img></a>
<div class="action--remove"></div>
</div>
<span class="profile__attribute profile__name"></span>
<span class="profile__attribute profile__author"></span>
<span class="profile__attribute profile__date"></span>
<div class="action--favorite"></div>
</div>
</div>
</div>

The necessity of the "profile__attribute" class is questionable. You only really need it if you intend to apply styles to all of those various attributes.

I think you're misunderstanding the purpose of the 'modifier' part of BEM. Firstly, you only used one hyphen, but it should be with two, and secondly, the modifier is meant more for different variants of the same thing, like if you have a button element and a large and small variant, then you could have button--large and button--small. I'd say that name, author, and date are all separate elements, not different versions of the same element.

That all said, I'm not really sure there's a definite right or wrong for BEM, a lot of it depends on the person and what kind of coding styleguide you might have.

navigation using BEM

You have two options here (or you can decide to use both of them):

Use different elements for li and a:

<ul class="menu">
<li class="menu__item">
<a class="menu__link" href="/what">What</a>
</li>
<li class="menu__item">
<a class="menu__link" href="/why">Why</a>
</li>
<li class="menu__item">
<a class="menu__link" href="/how">How</a>
</li>
</ul>

Important notice here is that you shouldn't use nested elements like menu__item__link.

Use separate block for links:

<ul class="menu">
<li class="menu__item">
<a class="link" href="/what">What</a>
</li>
<li class="menu__item">
<a class="link" href="/why">Why</a>
</li>
<li class="menu__item">
<a class="link" href="/how">How</a>
</li>
</ul>

So you can apply rules with a little bit of cascade: .menu .link {}

Or you can use mixes which is the best way I think:

<ul class="menu">
<li class="menu__item">
<a class="link menu__link" href="/what">What</a>
</li>
<li class="menu__item">
<a class="link menu__link" href="/why">Why</a>
</li>
<li class="menu__item">
<a class="link menu__link" href="/how">How</a>
</li>
</ul>

This time you can avoid using cascade but preserve common styles for links on your project.

Is it OK to apply multiple BEM elements to an html element's class?

That's absolutely valid and is called mix:

  • https://en.bem.info/methodology/key-concepts/#mix
  • https://en.bem.info/methodology/css/#mixes


Related Topics



Leave a reply



Submit