What is right BEM approach to global class inheritance? - css

I recently started using BEM methodology and I'm confused about class inheritance, or rather - when we talk about BEM - some use cases of modifiers.
Let's look at this example, I have a simple element with few children
.b-content { width: 100%; }
.b-content__image { display: block; }
.b-content__date { font-size: 14px; }
.b-content__title { font-size: 30px; }
.b-content__text { font-size: 16px; }
Now I want to reuse my .b-content block with slightly different styles, so I use modifier .m-compact and now I'm not sure what approach is the right one (in BEM).
Whether I should append modifier class to all elements (which I find more valid according to documentation):
.b-content.m-compact { width: 50%; }
.b-content__image.m-compact { display: none; }
.b-content__date.m-compact { font-size: 12px; }
.b-content__title.m-compact { font-size: 24px; }
.b-content__text.m-compact { font-size: 14px; }
or should I append modifier only to the parent element:
.b-content.m-compact { width: 50%; }
.b-content.m-compact .b-content__image { display: none; }
.b-content.m-compact .b-content__date { font-size: 12px; }
.b-content.m-compact .b-content__title { font-size: 24px; }
.b-content.m-compact .b-content__text { font-size: 14px; }
I find this second method more logical, you know, since I'm writing cascading styles and in real world if I want to write e-mail to 10 people, I would write one and just add more recipients, but on the other hand I realize BEM is practically non-cascading approach.
So what should I use and why?

As you point out in the last lines of your question, when doing BEM you should avoid cascading so, as a corollary to this, you don't have to repeat the modifier where it isn't needed.
For your Modifier I'd write something like this:
.b-content--m-compact {
width: 50%;
}
In your example the Block and the Modifier set only the width, so this is a limited use case. In general it comes handy to use some kind of CSS preprocess to ease the code writing, e.g. in SASS:
.my-block
width: 100%
color: red
&--modifier
#extend .my-block
border: 1px solid red
which will results in:
.my-block, .my-block--modifier {
width: 100%;
color: red;
}
.my-block--modifier {
border: 1px solid red;
}

Modifier in BEM looks like this: .block_modName_modValue
You can add additional class - but it's not BEM. And also modifiers have a name and value.
Block in BEM set namespace
So you set default styles for blocks and all unique(that can be changed) place in css with modifiers. This way your styles don't messed up.
To do this you need:
Place common styles in block styles(.portfolio)
Place unique style(with modifiers) like this.(portfolio_theme_list)
In css you don't need to separate this(preprocessor will be needed).
.portfolio {
/* common styles */
&_theme_list {
/* modifiers style */
}
}
In BEM project-stub(template engine) it would look like this:
If you add modifier to block. Then compile(bemjson) to html.
{
block : 'portfolio',
mods : { theme : 'list' },
}
You will see this code
<div class="portfolio portfolio_theme_list">
</div>
You write elements correctly and understand that they need to be separated(without inheritence).
So now you need just define styles for your block with modifier(portfolio_theme_list).
You have 2 options:
1) If you have 2 different blocks - you need separate common and
unique styles. Unique styles place in styles with modified blocks.
2) If you have only 1 different block & you already have styles on
this blocks. Then you can override and don't separate common
styles(but it can cause pain if you add another modifier/instance)

Related

How should page-specific styling be applied to BEM blocks?

Suppose we have a block element named .button which we want to reuse with different margin values on multiple different pages.
Possible solutions:
//1. Nested styles
.page-1 {
.button { margin: 10px; }
}
.page-2 {
.button { margin: 20px; }
}
//2. Specific modifier for EACH page
.button {
&--pg-1-margin { margin: 10px; }
&--pg-2-margin { margin: 20px; }
}
// 3. Special, page-specific block level element
// which will be COMBINED with an existing block-level
// element (ex: <button class="button page-1-element">...</button>)
.page-1-element { margin: 10px; }
.page-2-element { margin: 20px; }
Which one of these will be considered BEM-friendly way?
Is the first method acceptable / preferred way if there are too many different margins used?
Here's the answer to your question in official docs: https://en.bem.info/methodology/css/#external-geometry-and-positioning

How to modify multiple elements of a block with BEM CSS

Let us say I have the following setup,
.block
.block__header
.block__content
.block__footer
Now I want to show an active state of this block. Let us say the block itself gets a green background and element 2 and 3 should get bold text. As I understand the philosophy of BEM, one should not use child selectors in order to keep the specificity as low as possible.
So is this really the way to do it?
.block.block--active
.block__header
.block__content.block__content--active
.block__footer.block__footer--active
Update: and how would I write that solution in SASS (very new to it)? This my setup so far... if I can use nested selectors, what is best practice here?
.block {
&--active {
}
&__header {
}
&__content {
// active modifier of content
&--active {
font-weight: bold;
}
// would be the same as
.block--active & {
font-weight: bold;
}
// but can i reference the active block somehow else in sass?
// & is a parent selector but i would need the parent of the parent here...
}
&__footer {
&--active {
}
}
}
The philosophy of BEM is about to keep blocks context free. The low specificity is just a good practice, not a golden rule. I give three valid solutions below.
If you're sure the block cannot be recursively included in itself, a simple cascade can be used:
.block--active {
background-color: green;
}
.block--active .block__element-2,
.block--active .block__element-3 {
font-weight: bold;
}
If the elements are directly located in the block, the children selector is valid:
.block--active {
background-color: green;
}
.block--active > .block__element-2,
.block--active > .block__element-3 {
font-weight: bold;
}
Or the flat solution (but not DRY):
.block--active {
background-color: green;
}
.block__element-2--active,
.block__element-3--active {
font-weight: bold;
}
With SCSS, there are several ways to write the first solution. Here is the one I use:
.block {
&--active {
background-color: green;
}
&--active &__element-2,
&--active &__element-3 {
font-weight: bold;
}
}
See another solution here.

Name clash between Less mixin and CSS selector

I have this simplified Less script
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
.placeholder {
margin-top: 20px;
}
The output when I run this through my local compiler or winless online less compiler is
input {
margin-top: 20px;
}
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Insted of the desired output
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Is this a bug or am I missing something here?
By the result it looks to me like I can't have CSS-selectors with the same name as mixins with default values.
I'm running into this problem when compiling Bootstrap with my site specific code. In this particular case I can work around it, but as the project grows and I include other projects I can't imaging I have to keep track of any mixins with default values?
Edit: I see now that I should have read the manual and pretty much seen on the first page of the docs that everything can be treated as a mixin.
In Less, everything is technically a mixin irrespective of whether we write it with parantheses (as in with parameters) or without parantheses (as in like a CSS class selector). The only difference between the two is that when the parantheses are present, the properties present within it are not output unless called from within a selector block.
Quoting the Less Website:
It is legal to define multiple mixins with the same name and number of parameters. Less will use properties of all that can apply.
In this case, since the other mixin has a default value for its only parameter, both the properties can apply when called without any parameter and hence there is no way to avoid it from happening.
Workaround Solution: One possible solution to work-around this problem is to enclose all such conflicting rules within a parent selector (like body).
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
body{
.placeholder{
margin-top: 20px;
}
}
Compiled CSS:
input::-webkit-input-placeholder {
color: #333333;
}
body .placeholder {
margin-top: 20px;
}
Option 2: Extracted from the solution posted by seven-phases-max in the Less GitHub Issue thread.
For the particular use-case one of possible workarounds is to isolate conflicting classes in unnamed scope so they won't interfere with external names:
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
& { // unnamed namespace
.placeholder {
background: #ffffff;
}
} // ~ end of unnamed namespace
Note: The above is a straight copy/paste from the GitHub thread without any modifications so as to not tamper with the information.
#mixin placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
#include placeholder();
}
.placeholder {
margin-top: 20px;
}
that should work.
So if i understood right, you just want to add 20px on top of the placeholder ? Add padding-top to input instead.
input {
padding-top: 20px;
}

Double ampersand in LESS

I'm confused about double ampersand behaviour in LESS compiler.
Look:
.heading {
&&--type-small {
font-size: 15px;
}
}
Will be compiled to:
.heading.heading--type-small {
font-size: 15px;
}
And thats good.
But.
.wrapper {
.heading {
&&--type-small {
font-size: 15px;
}
}
}
Will produce:
.wrapper .heading.wrapper .heading--type-small {
font-size: 15px;
}
It looks weird. No?
Is there any advice to make this code works like:
.wrapper .heading.heading--type-small {
font-size: 15px;
}
Thanks =)
What happens when you use an ampersand in a nested rule is that the default nested structure gets ignored in the output selector and the ampersand acts as a placeholder for the complete list of outer selectors and will just insert all the parent rules all the way to the top of the hierarchy (the "path" for all nesting levels above) ... no way around that.
So using the first one - & will just join (concatenate) the nested selector to the whole list of outer selectors (appearing as if it just added it to the parent selector) and act as a combinator - see "Nested rules" at lescss.org. But then when you use the second ampersand - your selector will end up including all outer rules once again - the .wrapper and all rules in between will be added twice now. So, the behavior is not really strange. See also my answer to this question: "How to refer to two previous elements / tags / classes with LESS?" and for some more functionality of & see also seven-phases-max's comments below. Or find some examples of & being used as a "path" placeholder under "Advanced Usage of &" at lescss.org.
And to your concrete example:
I am not completely sure why you want to repeat the word "header" in the class name .header--type-small, if you are using it in addition to a class called .header ... I would just use additional classes such as .type-small, like so:
.wrapper {
//style for the wrapper
.heading{
//general style for the heading
&.type-small {
//style for the heading with class .type-small
font-size: 15px;
}
&.type-large {
//style for the heading with class .type-large ... and so on
}
}
}
with output CSS:
.wrapper .heading.type-small {
font-size: 15px;
}
but if you really really need the whole long string with the repeated names for some particular reason ... you could just do something like this:
.wrapper {
//style for the wrapper
.heading {
//general style for the heading
&.heading--type{
&-small {
//style for the heading with class .type-small
font-size: 15px;
}
}
}
}
with output CSS:
.wrapper .heading.heading--type-small {
font-size: 15px;
}

Dealing with repeated selectors: Placing elements together or using classes?

Let say I have to repeat the color blue in my web page, what's most effective, time saving, and smart way of doing it?
Examples:
1. This example can mess up a little bit my css file.
#header, #content, #footer {
color: blue;
}
#header {
(other properties)
(other properties)
(other properties)
}
#content {
(other properties)
(other properties)
}
#footer {
(other properties)
(other properties)
(other properties)
}
2. With this example I'll end up modifying my html file more often.
css:
.default-color {
color: blue
}
#header {
(other properties)
(other properties)
(other properties)
}
#content {
(other properties)
(other properties)
}
#footer {
(other properties)
(other properties)
(other properties)
}
html:
<div id="header" class="default-color">
(content here)
</div>
<div id="content" class="default-color">
(content here)
</div>
<div id="footer" class="default-color">
(content here)
</div>
I'd prefer the first form. Adding a "default-color" class starts to move into the territory of adding style into your markup, and it's generally more flexible to keep them separate as much as possible. On the other hand, if you have a semantic class name you can add to all of those that makes sense, then that could work.
Otherwise, if you really do just want a "default" color, you can specify it on the html or div elements in your css, and just override it with more specific classes where you don't want elements to show up as the default color.
Consider authoring your stylesheets using SASS. This will allow you to manage duplication in a number of ways:
The simplest is to define a variable for your blue color and not worry about having to update multiple occurrences:
$color-corporate-base: #009
#header { color: $color-corporate-base; }
#content { color: $color-corporate-base; }
This will compile to regular CSS, putting the color values wherever they're referenced in your document:
#header { color: #009; }
#content { color: #009; }
You could use "mixins" to include rules into different selectors:
#mixin bold-color {
color: blue;
font-weight: bold;
}
#header {
#include bold-color;
background: black;
}
#content {
#include bold-color;
background: white;
}
This will compile to regular CSS, with the two included style rules in each selector. Of course, this creates duplication:
#header {
color: blue;
font-weight: bold;
background: black;
}
#content {
color: blue;
font-weight: bold;
background: white;
}
Even though that takes care of the duplication in your Sass stylesheet source making it easy to work with, the CSS output still has that duplication. (You could group the common styles with commas and put the different styles into their own selectors, but that's right back to your original question.)
There's a cool new feature of Sass that addresses this. It's called "selector inheritance". Check it out:
.bold-color {
color: blue;
font-weight: bold;
}
#header {
#extend .bold-color;
background: black;
}
#content {
#extend .bold-color;
background: white;
}
At a glance, this seems very similar to mixins, but look at the CSS output:
.bold-color, #header, #content {
color: blue;
font-weight: bold;
}
#header { background: black; }
#content { background: white; }
This lets you organize your selectors in your Sass stylesheet as you wish, and, you get the optimized output you want!
One way of doing it for standard compliant browsers would be to use !important.
Example:
div
{
color: blue !important;
}
I would prefer the first version, too. But remember that you can also use multiple classes within one element. So you could you something like:
.blue {
color: #00F;
}
.bold {
font-weight: bold;
}
<div class="blue bold">Text</div>

Resources