This blog post discusses a possible direction for allocating CSS class names in HTML markup. It briefly describes the current rules, then moves on to a proposal around the adoption of BEM (or a variant thereof).
This relates to community work going on (led by SNOW.DOG) around exploring a Sass port of the Magento 2 UI Library, currently written in Less files. (I recently blogged on possible conventions around using Sass in Magento 2.) Adding support for Sass is an opportune time to review the current CSS class names in use – it is better to make any changes now for the Sass port to leverage.
Please note, the primary purpose of this blog post is to collect community feedback, particularly from frontend developers. Is the addition of BEM styled class names a benefit, or just “another set of class names to try and understand”? (Disclaimer: This blog post is not a commitment when or even if this work will be done.)
The current documented guidance for CSS class names in Magento 2 is to use semantic class names, not presentation oriented. The documentation currently does not go much further than that. (There are additional rules the core team is currently using which has not yet made it to the documentation.)
Magento 2 brought many of the PHTML files, and hence CSS class names, over from Magento 1. Many of the old names were good semantic names and so purposely kept. Others class names were retained because the effort to review and improve them (ensuring that nothing was broken in the process) was not considered worth the benefit. Work has been underway to incrementally introduce a more consistent approach to CSS class naming. You may notice new CSS class names starting with “.block-” and similar. Such names are effectively a second generation of class names being introduced over time. These new class names however have not been pushed out consistently through all PHTML files.
Regardless, there is still general confusion around selector specificity to use. For example, currently you may have a CSS selector such as
.product .image > .header .info .extra._active
(Okay, this might be an extreme example, but it makes a point.) In BEM, this may become
It is much clearer from the name when the CSS selector is appropriate. And there is less confusion over which class names to use in combination for the selector. A part of the thoughts behind BEM is combinations of multiple class names in a selector can actually be an anti-pattern in large projects. It takes a lot of discipline to get right. Better is to have more specific class names and use technologies such as Less and Sass to manage the CSS definitions better.
Asking the Community
Bartek (from SNOW.DOG) held a community vote in the forums some time back. 33 people voted, with the top votes being:
- BEM – 16 votes
- OOCSS – 4 votes
- SMACSS – 4 votes
- Any aka YOLOCSS – 3 votes
- Atomic Design – 2 votes
Everything else had 1 or less votes.
Let’s look a bit deeper into OOCSS, SMACSS, and BEM. I found this blog post by Matt Stauffer particularly useful. My super quick summary is as follows.
OOCSS (Object-oriented CSS) talks about concepts like “components” that can be used on different pages. The idea is to encourage “always encode a panel like this if it is meant to look the same across the site”. Think about what to include in the CSS, and what to exclude. Design it and document it so people know the rules.
SMACSS (Scalable and Modular Architecture for CSS) talks about categories of CSS rules: base (raw HTML elements), layout (like Magento page layouts – header, footer, sidebar), modules (like Magento blocks), state rules (e.g. “.is-expanded”), and themes (not needed by Magento).
BEM then goes on as one concrete way to apply the above two guidelines – recommending using a “Block name” to scope “Elements” in the block, with “Modifiers”, hence the name “Block-Element-Modifier” (BEM). (Note that “block” here is a BEM concept, not the Magento concept of a block.) The BEM block has a name, with nested elements using double underscore to separate block and element name (e.g. the block “product-info” may contain a CSS class name of “product-info__active”), and double hyphen for optional modifiers (e.g. “product-info__extra–active”).
Since BEM follows the OOCSS guidelines, and we would adopt the SMACSS concepts as well, a CSS class naming scheme adapting all of these options actually has received 24 out of 33 votes. Pretty dominant.
The following is a skeleton proposal for how to move forward with CSS class naming in Magento 2. This is not intended to be a definitive proposal, but rather provide enough information as a vehicle to gather support (or lack thereof) from the frontend developer community.
The proposal is based around adding new CSS class names (deprecating old names sometime in the future). As such, the PHTML files are not going to become shorter with this proposal. Rather new, longer class names are going to be added to the PHTML files on top of the existing class names.
(There are thoughts around deprecating the old class names over time, but that is not relevant to this discussion, so I am going to avoid the distraction of describing here.)
It is also proposed to continue to only rely on CSS class names, not ids. This is the current Magento 2 rule, and it is proposed to preserve this rule.
It is being considered whether to document all class names that are officially blessed BEM block names. The reason for this is “product-info” is both a valid “old form” name and a valid BEM name (for referring to the root element of the HTML), making it hard to determine which names are “old names” in some cases. Building up a registry of “approved/deprecated” names (maybe as a JSON or XML file on GitHub) would help developers know which class names to use or avoid.
I personally am not a frontend developer by trade. So his is my perspective on BEM et al. The idea is to introducing scoping in a way that makes sense with Magento 2. The BEM block names are really identifying well defined scopes. For example, each Magento 2 block generally has a PHTML file and would be allocated its own BEM block name.
For Magento, instead of using the SMACSS concepts of base, layout, modules, state rules, and themes, Magento concepts will be used instead. For example, page layouts will also be used as a BEM block (scoping rule for elements within the page layout).
The different levels of CSS rule specifications are as follows.
Base (HTML Elements)
Styling on raw HTML elements (no class names required) would be provided by Magento. No-one else should change such base rules. If someone wants to make changes, they should use a CSS class name. The risk of unexpected side effects is too great if a theme or similar changes the base HTML element style rules.
Standard class names are already defined by Magento for “header”, “footer”, “main”, “sidebar-main”, and “sidebar-additional”. These names could remain as BEM blocks, or class names such as “page-layout__header” could be introduced. (This is an example of decisions that would need to be made when introducing BEM – what are logical BEM blocks or scopes worthy of introducing a new root CSS class name.)
There would need to be review in some cases where multiple class names are provided, such as “sidebar sidebar-main” and “sidebar sidebar-additional”. Decisions are required to work out when CSS class names should be used versus Sass (or Less) mixins to avoid duplication.
Modules vs Blocks, Containers, and UI Components
Assuming each Magento block (and container) defines a new scope, the root of each such scope is allocated a unique name. (For vendors other than Magento, it is recommended to add a vendor name prefix to the block name to avoid accidental collections in block names.) Nested elements within the block all share root block prefix.
Magento UI Library Patterns
Magento UI Library Patterns also define new scopes. Examples may include buttons, grids, and forms. These concepts should appear the same across the site.
This post contains a quick introduction on how BEM could be adopted by Magento 2. This post is to get any feedback on whether the benefits of introducing new CSS class names following BEM is greater than the potential confusion of introducing yet another set of CSS class names.
If there is general community support (particularly from the frontend developer segment of the developer base), the next steps would be to refine the proposal and then investigate how to adopt it.
Thanks for the update! My thoughts:
— CSS specificity and BEM —
Due to the specific CSS used in the Magento themes I find it quicker and easier to start from scratch so I am 100% in support of using BEM naming conventions, I would consider using the Magento themes as parents again if this moves forward.
The specificity also causes code quality problems as it forces me to write poor CSS to overwrite it, often breaking our best practices (e.g never nest more than 3 levels deep, don’t style IDs). Using BEM (correctly!) helps remove the problems with specificity and makes overwriting previous declarations much, much easier.
The way I prefer to work at the moment is to start from scratch and style all the global elements such as buttons, text inputs, dropdowns, carousels etc and then once all the global elements are complete I will start to build the actual theme. I find this easier than attempting to fumble around with specific CSS and hundreds of variables.
— Variables (slightly off topic) —
In the Magento 2 themes I am not a huge fan of how many variables there are, I understand the aim is to make it easy for customers without devs to make simple changes such as change brand colours. But when I was experimenting with the base theme it took me longer to find and overwrite the button variables then it would to create buttons from scratch. 539 lines of Less for a few buttons is ridiculous 😦 (lib/web/css/source/lib/_buttons.less and lib/web/css/source/lib/variables/_buttons.less is what I’m referring to there). Another example is 726 lines of code just for dropdowns.
On a related note it’s important not to nest too much in Sass/Less as this can also cause specificity problems, my general rule of thumb is to only nest pseudo elements and states such as :hover or :focus.
— IE9 issue with selectors —
I think the below could potentially be solved with BEM naming conventions?
There is an issue for any customers planning on having IE9 support. IE9 only supports up to 4096 selectors per CSS file, as the Blank and Luma theme only have 2 CSS files it is very easy to go over the 4096 limit which means any additional changes are ignored. In fact the Luma theme is already over the limit before customisation with 4110 selectors meaning the last 14 rules in styles-m.css in the Luma theme will be ignored. This forced me to create another CSS file named custom.less and add all my changes in there, not ideal. Because of this I would not say the Magento 2 themes support IE9.
See Github this Github issue for the full details – https://github.com/magento/magento2/issues/5126
I have a few other issues such as the inability to import the lib files into my own CSS files, but I think that’s going off topic a bit much.
I do more back end then front end development, but I do write and review front end code occassionaly. I get a little concerned about having 2 systems that _can_(are available to) be used for styling. I think it’s inevitable that the “wrong” system will be used at some point in the lifetime of a project and also it could create confusion for developers about the “right” way to do something. I understand the need for b/c though and also the need to modernize the CSS. Tough challenge.
Maybe this is introducing additionally complexity, but I wonder if different variants of Magento could be available for download. For example…
1. Magento w/ legacy + new css classes
2. Magento w/ only new classes
That way, when starting a completely greenfield project you could drop support for the legacy CSS.
Thanks for allowing us to help shape future versions!
I’ve been doing some research and I like BEM as well. Making each class more unique and reducing specificity is a great thing.
Some ideas that maybe interesting to incorporate are Harry Robert’s BEMIT naming conventions. Particularly
u- for utility classes
is-,has- for UI states
qa- for qa or testing purposes
I also like the take that http://csstyle.io takes as well.
They offer a simple structure that lets you break down your code into components / options / parts / tweaks that let you separate everything out into nice components.
With the way the namespacing is handled, It allows clean and easy to read class names and easy to tweak with modifiers.
Alan, thanks for reaching out to the community. My thoughts are twofold: the depth and complexity of the stack as it is to date – and the necessity for Magento to keep up with modern tooling and standards.
I am in favor of BEM. But to play devil’s advocate – as it pertains to the complexity of the stack, which is already quite high – BEM makes adoption that much harder. All the while solving a real problem with specificity and visualizing application state. We at Something Digital use BEM daily and have found that understanding BEM comes only with time and practice; and gentle reproof where needed. I don’t do a tremendous amount of FED and I find myself constantly screwing up BEM.
So my guess is there’s not really a way to know whether the tradeoff is “worth it” other than to ask yourself if it solves a real problem. How many people search the code for a CSS class now to find where in the templates something exists? This will generally be lost after moving to BEM. If this is the only functional disimprovement then we may as well go for it – Magento 2 is rife with abstraction that prevents grepping the code base; and I say that with no sarcasm. We’re already there on the back-end – why not take the leap on the front-end?
My second thought is that the modern FED toolkit is becoming increasingly complex. The adoption of Less or Sass should have come with extra tooling any how (and it did, with Grunt, to some degree) but the further leap into BEM represents no additional tooling aside from those already propsed – while sorting out some of the difficulties with anticipating naming conventions – a relationship (parent/child) or state (on/off) are easily relatable and understood in Sass and BEM is a way to standardize how we express those with Sass.
I’m in favor of BEM but I see further challenges with adoption as we continue to modernize.
Thx Phil. Next step to me is see some samples. Problem today is its not clear which class name combinations to use for CSS etc. So even if it’s only our core files following BEM, I suspect it is still a step forwards.
Thanks for great post.
Surprisingly amazing that Magento has became so open and flexible. And indeed this will huge step forward.
I agree with Phil that it takes time to understand BEM concepts and its easy to make mistakes if you do not understand it well. (for example create elements within elements) “block__elem1__elem2”. So it may be good idea to ask some developers from Yandex (creators of BEM) to help out?
Also I want to add few links to official BEM website:
“How many people search the code for a CSS class now to find where in the templates something exists? This will generally be lost after moving to BEM.”
I find it to be the opposite, without BEM people generally nest classes a lot more. So let’s say the product name had a class of .name, searching for .name could potentially return many uses making it difficult to find. If you were using BEM you could search for .product__name and it should only return one result, the one you need.
Greping files is so 2010, we have sourcemaps for that 🙂
Thank you, Alan, for your Post. Unfortunately I did not read it earlier. It is pleasing to see how Magento is seeking for feedback more and more openly.
Personally I like BEM and tend to use it where possible. It is very declarative, logical and (if used wisely) a potential tool to keep code efficient and readable (if you get over the first “confusion”)
Concerning M1, I mainly used it when defining own functionalities not covered from the core, as it blends not very well with default class naming.
As a side note: I prefer using .block__element–modifier alternative syntax (two dashes for modifiers) as it is easier and faster to read.
Concerning M2 – here comes the great BUT:
Although I appreciate usage of concepts like BEM, I am a little bit afraid of the actual implementation. Like other approaches and technologies they should not be used “because you can” but in order to straighten things up, to simplify, to clarify, to specify.
Taking a look at the new Less/Css UI Library there is already an example where great tools and ideas (of core developers) lead into a hustle ending up in source code wich is overly complex, almost unreadable, short documented (in detail) and slow in performance.
I fear that the introduction of BEM would continue driving this road and maybe raise more issues/frustrations than it solves.
An example: Using BEM should force you to think sharp and crisp when it comes to naming and structure. Otherwise it will completely mess up your code.
So a block should always be “.header” and never “.page-layout__header”. Inside of “.header” sometimes it would still be plausible and wise to chose another block instead of an element – depending on functional considerations. So “.header .logo” would still be a good choice. Following BEM there would for example be something like “.logo__image” and “.logo__link”. Maybe another block would make a better example, but I think you get the meaning.
As another side note: If a block name needs to be split with a ‘-‘ like “page-layout” I always ask myself if this is neccessary and if there isn’t another, yet descriptive. shorter one.
Like with other languages, good code is readable and instantly understandable in its naming and context. Which would not be the case with monsters like “page-layout-1column__mobile-menu–open-hover-blue” or something similar.