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
Why Does Display Inline-Block Match Height of Text
Angular Materials Won't Apply Styles to Components
Possible to Set Hex Color Opacity Independently
CSS: Are Class and Id Interchangeable
Nesting Element+Element Selector in SCSS
Customizing Boostrap with Sass; Where Exactly Should I Import Bootstrap in My SCSS File
Media Query CSS and Notepad++ Compatibility Issue
Responsive Irregular Background Shape with CSS
CSS Absolute Positioned Elements and Margins
Neatly Display Images of Different Sizes Sequentially in The Same UI Element
Shiny Presentation (iOSlides): Custom CSS and Logo Windows 7/8
Why Bootstrap CSS Is Not Overriding in Other Project with The Same Code
Why CSS Height 100% Is Not Applying for Normal Div
How to Combine Position: Relative and Float: Left
CSS Footer Position Stick to Bottom of Browser
With Webpack, How to Generate CSS Only, Excluding The Output.Js