Working with BEM at Scale — Advice From Top Developers
At the start of the year, I put together an article on BEM and SMACSS that focused on the confusion of choosing a CSS methodology. I contacted a range of different developers and got their words of advice, their success stories and their horror stories in the hope that others could learn from their experiences with these popular CSS methodologies.
The article was quite well received, but there was a recurring question I’d seen over and over again with BEM that the article didn’t cover clearly: How do you deal with BEM at scale?
It was all well and good to say BEM is beneficial (it is!), but often the guides on the web for BEM stick to the basics. They tell you to arrange things into block__element--modifier
but there isn’t a lot of guidance on what to do when things get messy. Is block__element__subelement--modifier
okay? Is there a best practice for arranging your CSS files? Should you use @extend
to inherit parent class values or should you list those in your HTML?
In this article, I wanted to get the thoughts of developers who’ve had experience working with BEM at scale already — what do they do? What lessons have they learned in the process that would be good for us to know beforehand?
One thing you’ll find is there are some differing opinions — but that’s okay. I’ve tried to include as many of the responses as possible, even if they include contradictory views, so that readers can make up their own minds.
Some Background
BEM originated at Yandex, a Russian search engine. It has since evolved into two different streams of use — it can be either the whole BEM stack that Yandex created (CSS, JS, HTML templating and development tools) or it can be solely the CSS methodology behind it.
The latter approach has evolved from the Yandex concept via ideas by Nicolas Gallagher, Harry Roberts, and many others. The team at CSS-Tricks did a great write up on the CSS BEM methodology recently too — worth checking out!
This article will cover a bit of both worlds as I thought it was best to include both perspectives. However, quite a large proportion of the respondents focus solely on the CSS methodology rather than the Yandex BEM stack.
The talented developers that let me into their minds
I’ll start by introducing the developers who were kind enough to let me into their minds to get some tips and advice on using BEM at scale. I was lucky enough to find BEM enthusiasts from all over the world — I’ve got responses from the Netherlands, Sweden, UK, Crimea, and Estonia! A very big thank you to all of them for taking the time to answer my questions.
- Bob Donderwinkel, a front-end developer from Rotterdam – Website, Twitter
- Hamish Taplin, a front-end developer working for Bluegg in Cardiff – Website, Twitter
- Harry Roberts, a Consultant Front-end Architect, designer, developer and absolute BEM guru from the UK – Website, Twitter
- Serge Herkül, a developer at Teamweek – Website, Twitter
- Vladimir Grinenko, the head of the BEM development group at Yandex – Website, Twitter
- Vladimir Starkov, a front-end developer from Sweden who is one of the founders of getbem.com and previously also worked at Yandex – Website, Twitter
Let’s get started looking at the advice from these developers!
Be careful with nesting
Nesting is one of the biggest dangers I’ve seen with development teams using BEM. It is very easy to go overboard and make things much more complicated than necessary. I wanted to know, is naming a class .block__element__subelement--modifier
okay? Or should you stick to no more than the two levels of parent and child in .block__element--modifier
? Different developers have different levels of nesting that they’re okay with, so I’ll cover both viewpoints.
No nesting beyond two levels
This was my personal preference and appears to be the most commonly followed practice by the developers I interviewed.
Vladimir Starkov pointed out his concern for block__element__subelement--modifier
nested classes, saying “it is the wrong way, you should always have an option to use one element without another.”
Bob Donderwinkel also gradually moved to this approach:
“I used the block__element__subelement—modifier
notation at first, but that is not optimal and a little superfluous. So I try to sticking with block__element—modifier
now, and that’s also easier on the eye ;)”
“It doesn’t really serve a purpose and over complicates your CSS. And also perhaps the extra file size hit which some styles of Sass/LESS nesting can produce. The resulting CSS can get pretty extensive.”
Vladimir Grinenko from Yandex agrees with avoiding nesting too, suggesting making the element name longer if needed, rather than adding more levels of nesting:
“There’s no need to resemble nesting in entity names. DOM structure is enough for that. Nesting may change in the future and the best way to avoid renaming things while refactoring is to name them properly from the very beginning.
In case you really need to resemble nesting in the name, consider doing it with just longer name of an element: block__element-subelement
”
Harry Roberts explained the idea of this to me in quite possibly the best way I’ve ever seen, so I’ve included it below almost completely unedited:
“One important thing to know about BEM—and something that I feel causes problems for a lot of developers—is that you do not step through each layer of the DOM. To use a metaphor, the following is incorrect:
.person {}
.person__head {}
.person__head__face {}
.person__head__face__eye {}
.person__head__face__eye__pupil {}
“This is terribly verbose and also forces a DOM structure upon us; it is very inflexible. Instead we should write:
.person {}
.person__head {}
.person__face {}
.person__eye {}
.person__pupil {}
“This is much more terse; it doesn’t force any DOM structure upon us, and we can visually indent rulesets to show us an implied DOM structure.
“If you do need to nest one Block inside another then just use traditional nesting. To continue our metaphor:
.person {
clothing: pyjamas;
.outside & {
clothing: covered;
}
}
“This time we want to style Person because it’s Outside—we don’t need to invoke or use anything BEMmy here. (N.B. we are using Sass’ parent selector nesting for better encapsulation here.) Alternatively, though, we could use something like this:
.person {
clothing: pyjamas;
}
.person--covered {
clothing: covered;
}
“We can use BEM here in order to create a variation of a person who can be covered regardless of where they are.”
Hamish Taplin doesn’t really follow a strict rule with this but aims to keep things to element and subelement where possible:
“I try to keep things as simple as possible. I have no real preference as it happens so rarely but I generally find that just separating the element/subelement into its own element is enough. An example of this would be whenever you have some of repeating elements inside an element that has its own components (such as header/footer). For example, you could do this:
.list__header
.list__items
.list__item
.list__item__header
.list__item__body
.list__item
.list__item__header
.list__item__body
.list__item
.list__item__header
.list__item__body
.list__footer
“However, I prefer to just separate those elements:
.list__header
.list__items
.list-item
.list-item__header
.list-item__body
.list-item
.list-item__header
.list-item__body
.list-item
.list-item__header
.list-item__body
.list__footer
“So now we have a ‘list’ element and a ‘list-item’ element that isn’t tied to the ‘list’. I can’t really comment on the pros and cons of this approach because I haven’t given it a great deal of thought, but it’s worked well for me so far!”
However, not everyone agrees with the strict approach
Serge Herkül had a different approach to the nesting restrictions which warrants a mention as I know of other teams that do the same. His team does go beyond two levels deep. He said:
“We use block__element__subelement--modifier
when needed. Try not to get too strict with how the ‘original’ BEM sees things and bend things to your own needs.”
Be cautious with where you start your block scope
A common mistake Harry Roberts has seen is developers starting their Block scope too high up. He gives the following example of incorrect CSS:
.room {}
.room__floor {}
.room__desk {}
.room__laptop {}
Harry explains, “Here we can see we’re scoping the laptop to the desk to the room. The issue here is that that laptop could be in my bag in the trunk of a car; that desk could be moved into the corridor; the laptop could be placed straight on the floor of the room.”
Instead, the focus should be on separating out the DOM elements that can live independently of one another. Start your Block scope from “the smallest, most self-sufficient bit”. As an example, Harry provided the following solution to the incorrect CSS above:
.room {}
.room__floor {}
.desk {}
.laptop {}
Harry points out, “The laptop now has no dependency on anything, nor does the desk. This is how it should be.”
Separate your CSS in easy to understand files
Serge Herkül had a focus on keeping things modular and in separate files to assist in scaling up BEM. He said, “Our entire SPA is made out of modules. Roughly speaking we have a SCSS file for each module or module group (if it’s made of smaller modules).” He recommends the modular approach for both CSS and JS: “Try combining your JS and CSS modules so they would share the same name (profile_picture.js
and profile_picture.scss
).”
Harry Roberts says “each file should be as small as possible but as large as necessary”.
Vladimir Starkov had separated files per block, modifiers and elements when previously working at Yandex but has found it is easier to focus on separating by block:
“In Yandex we usually separate styles per block, its modifiers, its elements and for their modifiers. But in reality it is very hard to support all these separations. That’s why in pet projects I and ex-Yandexers support a one-file-per-block structure, which is flexible and good enough to support and maintain. Best tip is to extract blocks into different files, one block per file. There is a brilliant article in English by Vladimir (@mistakster) at frontendbabel.info/articles/bem-with-css-preprocessors/ explaining how to maintain your selector’s structure inside block files.”
Bob Donderwinkel considers his approach on a project by project basis depending on how complex things get, but also adapts some SMACSS concepts:
“It depends on the project. I started out by using different files for each Block, and then perhaps files for each Element and Modifier if those got extensive. But nowadays I also use a SMACSS baseline for files often, and fill those with BEM CSS.”
Vladimir Grinenko says Yandex’s focus is on separating by block, with elements and modifiers separated when necessary:
“We store each block in a separate folder. For elements and modifiers additional folders are optional (having them in different files gives us possibility to build a project with just needed parts). e.g.
blocks/
button/
button.css
header/
__logo/
header__logo.css
header.css
footer/
footer.css
“Then build tools look at what BEM entities (blocks, elements and modifiers) are there and concatenate needed files in their proper order.”
Hamish Taplin has found his style for structuring CSS files has changed over time and provided an insight into where he’s moving towards and why:
“Previously, I was building my Sass in a structure inspired by SMACSS:
- base (base-styling for typography, forms, tables, grid, etc)
- helpers (Sass functions and mixins)
- modules (My BEM modules)
- vendor (3rd party stuff)
“The contents of everything other than ‘modules’ would remain pretty much the same from project-to-project with only some ‘base’ styling being done on a per-project basis. This would include the basic typographical styling (headings, body copy, etc) for that project and things like tables and forms will be styled.
“Everything else goes in ‘modules’ with one file per module. This might look something like:
- modules
-- article
-- avatar
-- dropdown
-- feature
-- list
-- media
-- nav
-- panel
-- section
“However, dumping everything in a ‘module’ wasn’t really working for me as not every bit of styling can be abstracted into a re-usable module. Obviously, writing re-usable code should be done as much as possible but the realities of deadlines and budgets dictate that sometimes you just need to get something styled and move on. I had it in the back of my mind to look for a better way and had experimented with dropping the BEM syntax for styling that wasn’t intended to be re-usable. I tried this out on a couple of projects and wasn’t really that happy with it.”
Hamish was then influenced by an article by Harry Roberts on namespacing, which Harry himself actually explains a bit further down in this article (it’s wonderful when responses link together so nicely!):
“A few weeks ago, Harry Roberts published an article on using namespacing to get better control over large scale codebases. Although I haven’t completely adopted his approach, he makes some really good points about the types of abstractions he uses. This made a few things ‘click’ for me and I came up with a new approach to structuring my Sass that I’ve been trialing recently:
- base
- components
- helpers
- objects
- vendor
“The only real difference so far is that I’ve split what I previously called ‘modules’ into two folders called ‘objects’ and ‘components’. Basically, ‘objects’ are pure abstractions such as the ‘media’ object and my own grid system whereas ‘components’ are self-contained implementations of styling such as ‘dropdown’ or ‘signup form’. Those components might even be based on an ‘object’ but will more likely be project-specific whereas the ‘objects’ are much more re-usable between projects.”
ITCSS and source ordering
Harry Roberts has his own methodology for structuring BEM CSS called ITCSS, which keeps things easier to manage and scale. Harry explains it as a methodology that allows you to “break your projects up based around Specificity, Explicitness, and Reach”. He focuses on a specific CSS source order, arranging styles into layers of different levels of specificity. More specific stylings have layers placed near the end of your CSS and globally shared variables, mixins and styles are in layers at the start.
If you’d like more info on the ITCSS framework, Harry’s presentation “Managing CSS Projects with ITCSS” is a great resource (if you’d prefer just slides rather than a YouTube link, they can be found here). ITCSS is a really well thought out framework for keeping CSS manageable at scale and is definitely worth a look.
Namespaces
Harry Roberts recommends augmenting BEM with namespaces. This involves creating a set of namespaces for things like theme classes (in other words, this class is restyling child elements to match a certain theme), state classes (e.g. is-active
) and many more. I’ve seen various developers using namespacing recently in various ways, some using ideas from SMACSS (such as state classes of is-active
and layout classes for overall layout containers).
Harry explained the beneficial relationship between BEM and namespacing really well. He said, “I augment BEM with my own suite of namespaces. This adds a whole other layer to the meaningful naming that BEM gives us by signalling the role of a class in non-relative terms: where BEM tells us how classes are related to each other (e.g. .foo__bar
must live inside of .foo
), namespacing tells us what a class does in absolute terms (e.g. .t-*
tells us this class is theme related).”
Harry’s post outlining the namespaces he uses (the one Hamish mentioned earlier) provides more details and can be found at More Transparent UI Code with Namespaces.
Don’t be afraid of long class names
Serge Herkül also suggests a focus on self documenting class names. Don’t be afraid of long class names! His advice – “Use long, descriptive class names over short names that don’t document themselves.”
Avoid Sass’ @extend
I provided the two different formats I’ve seen people use with BEM to the developers to get their thoughts on which they use and why. The first was the style similar to object oriented CSS:
<div class="block">
<div class="block__element"></div>
<div class="block__element"></div>
<div class="block__element block__element--modifier"></div>
</div>
The second was the style that uses Sass to handle inheritance:
<div class="block">
<div class="block__element"></div>
<div class="block__element"></div>
<div class="block__element--modifier"></div>
</div>
I had a unanimous agreement from all the developers on this one. Don’t use @extend
where possible — go with the first option!
Harry Roberts recommends avoiding @extend
and instead keeping styles defined within classes in your markup (like the first markup example above). His thoughts on this were:
“You can create a greater number of combinations in the view by not ‘tying’ classes together in Sass. The HTML has a much better paper trail in that you can see every class acting on a piece of the DOM. Your CSS stays much slimmer in that you don’t have to create new placeholder classes (or the manifest classes that combine them) every time you want to create a new piece of UI.”
Vladimir Starkov agreed with avoiding the second option as it leads to much bigger CSS files. He prefers “declarative in HTML” with “clean and minimum sized CSS”. He suggested the use of automatically generated systems:
“If you are confused with HTML classes that are a bit messy, then you can generate them automatically. You can try kizu/bemto on github for a jade templating system and azproduction/b_ on github for any nodejs-base templating systems to achieve that automation goal.”
Bob Donderwinkel also agreed with the object-oriented option:
“I personally use the object-oriented approach because it is a little more tech-agnostic. Concerning the Sass approach I actually tried to make things even more ‘organized’ by using multiple modifiers in a single BEM class name (https://gist.github.com/BobD/e51edd989e43aaf3d74d). But the CSS it produced was pretty awful.”
If you needed any more encouragement to avoid Sass’s @extend
, Hamish Taplin provided another good example of why it’s better to structure things into multiple classes:
“I very much encourage using the ‘multi-class’ approach in the first example over the ‘single-class’ in the second example. The biggest problem with the ‘single-class’ approach is related to nesting and makes it an anti-pattern, in my opinion. Consider the following example whereby we want to apply a small change to a .btn
inside a .signup-form
, making it full width. If we use the multi-class approach to modifiers we can do the following:
.signup-form {
.btn {
width: 100%;
}
}
“Here, any instance of the .btn
will be styled accordingly in the relevant context. If we had adopted the single-class pattern and our .signup-form
had a modified .btn--blue
then our CSS fails and we have to add every type of .btn
modifer to our .signup-form
styling context. This would be a maintenance nightmare for obvious reasons!”
Lint your CSS!
Harry Roberts also has a brilliant system for ensuring all CSS keeps to the right naming convention: he lints his CSS using scss-lint to ensure all team members stick to the convention correctly. As Harry put it, “It’s all well and good telling the team to use BEM, but it’s important to police it as well.”
Harry even wrote a regex to use with scss-lint that works to detect his style of namespaced BEM classes. You can find that at the More Transparent UI Code with Namespaces article mentioned earlier.
Choose the tools and workflow that work best for you
Vladimir Grinenko from Yandex was keen to point out that BEM can be about more than just CSS classes. Yandex has a whole range of tools to compliment the BEM methodology and bring it into your JavaScript and templates too.
“One of the most important things about the BEM methodology is that it’s not just about CSS names but it’s all about the same things as Web Components: each BEM block knows everything about itself (styles, JS, templates, images, tests, documentation, etc).
“We use the object-oriented declarative approach everywhere: in CSS, JavaScript and templates.
“Also, we have such powerful concepts like mixes — when there could be more then one BEM block on one DOM node: <div class="b1 b2 b3__elem1"></div>.
”
For details on the tools and the BEM stack from Yandex, see bem.info.
As I mentioned above, many developers I know prefer to focus just on the CSS aspect of BEM. However, this doesn’t mean sacrificing the ability to have a fully featured workflow. The Yandex tools can be beneficial for some projects but there are alternatives for those who prefer to use different workflows too. Yandex even provides workshops to help developers use BEM with things like Gulp, Angular, and more.
Vladimir Starkov prefers to focus on the CSS methodology and recommends finding the workflow that works best for your team:
“Basically BEM is only a CSS methodology, and all the stuff invented by Yandex is only their way to make code sharing between teams simpler.
“From this point of view it’s good to understand that you don’t need to use full yandex-bem-stack with bemjson, bh, priv and bem-tools, because this way is very inflexible and hard to be implemented in other workflows.
“To be honest it was a reason for us to create gulp-bem and getbem.com, we wanted to deliver BEM to people in the easiest way.”
From what I gather from our devs, there are plenty of resources and tools to help you use BEM in the way that best suits your workflow. Use some aspects in your JavaScript and templating if that works in your team. Do some research and find the tools that suit your team and project. Combine them. Use some from Yandex and some from other developers. Try some of the cool stuff Harry Roberts has got going. Build your own! The power is yours. Just make sure you’ve got a solid workflow in place.
Documentation
One aspect of Vladimir Grinenko’s advice earlier stood out to me — documentation. BEM provides a great base of structured components (your Blocks) to document and keep consistent throughout your project. If you can keep your naming and structure throughout all levels of your site (CSS, JS, HTML templates and documentation) organised by Block components, your project becomes easier to follow and expand. Combine that with the brilliant concepts in Harry Robert’s ITCSS methodology and a well documented and structured site should be just that little bit easier.
Avoid over-automation
I thought this piece of advice from Bob Donderwinkel was quite valuable:
“I guess my main word of advice would be to keep your BEM CSS portable to other projects if needed. This means not over-automating things so they are only useful in a single project environment. That is not to say you will eventually re-use that specific piece of CSS, but at the very least it will result in a clean CSS approach.”
Still haven’t tried using BEM in your team?
For those who haven’t taken the leap into giving BEM a go just yet, I thought I’d finish up the article with some words from Serge Herkül on his positive experience with BEM and Teamweek:
“We originally did not have any specific CSS structure and stuff just started to slip out of hand. There was a lot of Sass nesting going on, resulting in long CSS rules. Some styles were affecting other elements, that they were not supposed to, etc.
“Moving to BEM was a pretty straightforward process, a lot of manual labor but not too hard. In the end I cannot think of a better way, all of the problems we had before are gone.”
Conclusion
I hope this series of interviews has provided a good set of tips and ideas for developers looking to implement BEM on large scale projects (or even small projects which always have the tendency to grow exponentially in ways you least expect). If you were previously unsure about using BEM on your next project, maybe this will give you a little bit more of a push to give it a go — the benefits are worth it!
Have you got any tips or ideas on using BEM at scale that weren’t covered? Feel free to include your thoughts in the comments below.