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.
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.
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 .
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.
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.
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.
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
.
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.
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.
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With