I often see this kind of code from people using Bootstrap:
<div class="container">
<div class="row">
<div class="col">
<h1>Lorem Ipsum Dolor Sit Amet</h1>
<p>Lorem ipsum</p>
</div>
</div>
</div>
Which can be optimized into this:
<div class="container">
<h1>Lorem Ipsum Dolor Sit Amet</h1>
<p>Lorem ipsum</p>
</div>
Because we don’t really need the .row nor the .col.
The purpose of the .row and the .col are simply to place elements into different columns. If there’s just 1 column and if the column spans the entire row, then we don’t really both the row and the column.
Reducing the number of HTML elements would allow the browser to parse the HTML document, reduces the amount of time JavaScript needs to select elements, hence improving performance and better Google Page Speed scores for SEO purposes.
Block Element Modifier (BEM) is a way to write CSS that makes it reusable, predictable, and manageable especially for large projects. Just like how we have Object-Oriented Programming (OOP) for PHP and Functional Programming (FP) for JavaScript, BEM helps organise CSS code so that it’s more manageable.
How BEM Works / Why Use BEM
With CSS, it’s really easy to create difficult to manage code. BEM was created to solve these problems if the entire team writes CSS code a certain way.
CSS global scope
CSS isn’t an OOP language and hence doesn’t have private instance variables. Even with CSS custom properties / CSS variables, we don’t have the encapsulation / restricted scope that OOP private instance variables have. In other words, CSS rules are global and 1 rule in a stylesheet can interact with any other rule in the same or even different stylesheets.
Without the encapsulation / restricted scope, some CSS that you wrote for a footer can easily interact badly with some other CSS that you wrote for the navbar if both contain menu elements.
This is perhaps the biggest reason why CSS is so easily messed up. It’s so difficult to check if our code affects other code once our code base gets large.
BEM restricts CSS specificity
In order to make CSS more manageable, BEM essentially restricts how CSS is written by styling (mostly) classes.
Classes .button, attributes [type=text], pseudo-classes, e.g. :hover
Elements div, a, p, pseudo-elements :before, :after
Order of CSS rules, bottom overrides top
By essentially styling only classes, BEM almost guarantees that all CSS rules have the same specificity. Hence the only thing that matters is where the CSS rules is placed – bottom overrides top.
1. No !important
2. No inline styles
3. No styling of IDs
4. No styling of elements
5. Minimal nesting e.g. .parent .child
By restricting ourselves to use style (mostly) class selectors, and with minimal nesting, we are almost guaranteed that all the CSS code we write will work in a predictable manner.
But if we have only classes in our stylesheets, we will need a way to manage them better. Enter naming conventions.
Naming conventions
BEM classes are written in a special manner that attaches meaning to underscores and dashes in CSS classes. From what I’ve read, there’s mainly 2 ways of writing BEM CSS:
US / Popular Way
This is probably what you’ll find when you Google BEM. CSS code written this way use double underscores and double dashes extensively in CSS classes, like this: block__element--modifier.
And if we have Blocks, Elements, or Modifiers that have > 1 word in their names, we use a single dash to separate the words. Like this: block-name__element-name--modifier-name.
Some examples:
Block: button
Element: button__text
Element: button__icon
Modifier on element: button__icon--left
Modifier on element: button__text--bounce
Modifier on block: button--success
Modifier on block: button--disabled
Block: login-form
Element: login-form__radio-input
Element: login-form__submit-button
Modifier on element: login-form__radio-input--error
Modifier on element: login-form__submit-button--disabled
Modifier on block: login-form--active
Russian / Yandex Way
Since Yandex essentially gave birth to BEM, this should be recognised as the original doctrine. There’s not difference in the way double underscores have special meaning to separate Blocks from Elements in CSS classes e.g. block__element. However the difference lies in Modifiers – there’s no double dashes -- before a Modifier. Instead the modifier is separated from the Block / Element via a single underscore _.
In addition, there’s 2 kinds of Modifiers: boolean and key-value.
A boolean Modifier just specifies whether the Modifier is present or not, i.e. a boolean Modifier has only true / false values. For example, a button can be disabled or not, leading to button_disabled. A button can also be active or not, leading to button-active.
A key-value Modifier instead has multiple values for the Modifier. Prime examples include color, where the button can be green or red or blue etc. For key-value Modifiers, a single underscore is still used to separate the Block / Element from the Modifier. However, there’s an addition single underscore that separates the Modifier name from the Modifier value, leading to classes like button_color_green, button_color_blue etc.
More examples:
Block: button
Element: button__text
Element: button__icon
Key-value Modifier on element: button__icon_position_left
Key-value Modifier on element: button__text_animation_bounce
Key-value Modifier on block: button_validation_success
Boolean Modifier on block: button_disabled
Block: login-form
Element: login-form__radio-input
Element: login-form__submit-button
Key-value Modifier on element: login-form__radio-input_validation_error
Boolean Modifier on element: login-form__submit-button_disabled
Boolean Modifier on block: login-form_active
Which to follow?
The popular naming convention seems to be more readable as double underscore and double dashes are more visibly distinct compared to double underscores and single underscores. However, the Yandex method has is more semantic and explains what the Modifiers are. Personally, I’d choose the Yandex method as I can more easily understand what the Modifiers mean.
Regardless of how you write the classes, you need to agree within your team what naming convention to follow and be consistent. Maybe even use a post css BEM linter plugin to highlight inconsistencies.
What is a Block?
A functionally independent page component that can be reused. In HTML, blocks are represented by the classattribute.
A Block is similar to a Lego brick. There are different kinds of Blocks just like there are different kinds of Lego bricks e.g. square, rectangle, circle, floor, grass, tree, wheel, head, hair etc etc. Similar to how a castle is made out of different Lego bricks, we can make a website out of different Blocks including the header, menu, blog-list, blog-post, filters, footer, and others.
If we follow the BEM methodology in writing Blocks, we can be assured that these Blocks will be reusable and look the same way across pages and projects.
What is an Element?
A composite part of a block that can’t be used separately from it.
While I disagree that an Element can’t be used separately from a block, it’s true that a Block can be made up of one or more Elements. Take for example a button with an icon in it. In this kind of button Block, we have a button__text Element and a button__icon Element.
That being said, it doesn’t make sense to use Elements by just placing them outside a Block, i.e. don’t use button_icon if it’s not within a button Block parent. I know sometimes we want to do that because we want to make it look different from a default icon, but the BEM way to do that is to use Modifier(s) instead.
No Elements of Elements
One common mistake that I’ve made earlier in my coding life is to use double underscores more than once in a class, e.g. login-form__input-group__input and login-form__input-group__icon. Maybe something like this:
However, having elements of elements / sub-elements like that makes the class names really long. Instead, we could just make them elements only, like this:
Or if need be, and maybe even better, separate into 2 Blocks – login-form and input-group one containing the other, like the below, where the login-form acts more like a box where we place the input-group Block:
Modifiers, as the term suggests, modifies the block or the element it is applied to. Per the Yandex naming convention, we can have 2 types of Modifiers: boolean and key-value. In fact, just read the above section on Yandex to understand more.
composer install will install packages listed in the lock file composer.lock. When composer.lock is committed to the git repo, this ensures all developers in a team will install the same dependencies, meaning less room for works on my machine kind of error due to different setup.
However, running composer update doesn’t install the exact versions of packages stated in composer.lock. Instead it installs versions that matches those listed in composer.json. Hence newer versions could be installed.
Example.
Developer A first created the repo, did a commit, did not track composer.lock. v1.0.0 of package A is installed in Developer A’s machine
Some time passed, package A now has version 1.0.12
Developer B joined the team, clone the repo onto his machine, runs composer update. But package A version 1.0.12 is installed as there’s no composer.lock file. Due to different versions of package A installed, the app works well in Developer A’s machine but not in Developer B’s machine.
When to run composer update
Only when we are upgrading packages do we run composer update.
Of course, then we run the automated tests to check if all went well and fix.
Recent Comments