A while back I wrote an intro to BEM – you can check it out here. It covers the basics to get you up to speed on the what, how and why of BEM. What I want to cover in this article though is a no-BS, real-world application of BEM. BEM is great, but in everyday use you’re still going to encounter gray area issues. Of course this isn't limited to BEM, this applies to web development in general. Often you’re simply making the best call based on your particular situation. And that’s the reality of it, you’re not always going to be sure that your approach is the best is could be (even after hitting up the Interwebs to see what others have done), but many times it’s about making judgment calls and meeting deadlines.
Within BEM or any other CSS framework, every developer will have a different approach that works for them. There are dozens of ways to tackle any problem. What I strive for is creating code that is as modular, semantic, and scalable as possible. Sometimes I reach that goal, sometimes I don’t. Let’s take a look at something real that I wrote on a deadline. I’m keepin’ it real and practical with you. I also welcome any thoughts, questions or suggestions. We all learn from each other so let’s do this.
One Block, Four Ways
Below you'll find a responsive, base block ("block" is BEM's term for a module) used to display company products in four different configurations. The base block is structured to be as generic and reusable as possible. The three other block configurations are accomplished by subclassing the base block. To see the inner workings of each block, check out it's HTML, Sass, and my notes following each block. Note that I'm assuming you already have a basic understanding of Sass. If you don't, check it out here. Also, I'm only showing the Sass once because it includes the CSS for every block. Once you read the explanation behind my Sass, you won't really need to see it again.
Example 1: The base block
Our Products
Product One
Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.
Product Two
You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.
Product Three
Poutine seitan fap, keytar fixie fanny pack. Whatever wayfarers kitsch banjo before they sold out. Leggings kickstarter shabby chic, irony crucifix polaroid vegan yuccie heirloom wayfarers slow-carb.
HTML
<section class="b-products"> <h2 class="products__section-header">Our Products</h2> <hr> <div class="products__wrap"> <article class="products__item"> <img class="products__img" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product One</h3> <p class="products__copy">Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.</p> </article> <article class="products__item"> <img class="products__img" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Two</h3> <p class="products__copy">You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.</p> </article> <article class="products__item"> <img class="products__img" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Three</h3> <p class="products__copy">Poutine seitan fap, keytar fixie fanny pack. Whatever wayfarers kitsch banjo before they sold out. Leggings kickstarter shabby chic, irony crucifix polaroid vegan yuccie heirloom wayfarers slow-carb.</p> </article> </div> </section> |
Sass
.b-products { background-color: #eef2f7; } .products__striped--odd { .products__item:nth-child(odd) { background-color: #d6dade; } } .products__striped--even { .products__item:nth-child(even) { background-color: #d6dade; } } .products__section-header { margin-bottom: .25em; padding: .7em .6em 0; } .products__section-subheader { color: #999; font-weight: 300; margin-top: 0; } .products__wrap { display: flex; flex-flow: column nowrap; } .products__wrap--row { display: flex; flex-flow: column nowrap; } .products__item { padding: 1em; text-align: center; } .products__header { margin-top: .25em; } .products__copy { overflow: hidden; } .products__img { margin-bottom: 1em; max-width: 100%; } @media screen and (min-width: 600px) { .products__wrap--row { flex-flow: row nowrap; } .products__item { text-align: left; } .products__item--center { text-align: center; } .products__item--center--left { text-align: center; } .products__img { float: left; margin-right: 1em; } .products__img--right { float: right; margin: 0 0 1em 1em; } .products__img--center { float: none; margin-right: 0; } .products__img--center--left { float: none; margin-right: 0; } } @media screen and (min-width: 1200px) { .products__item--center--left { text-align: left; } .products__img--center--left { float: left; margin-right: 1em; } }
As you can see, BEM classes are used to create a block that will be reused by subclassing. I've only added classes where needed, not every HTML element needs to have a class. In my Sass, I also try to do as little selector nesting as possible. I try not to go more than two levels deep if I absolutely need to nest. Any more than that and you'll be finding yourself running into annoying specificity issues. Adding new classes as needed to the HTML instead of targeting elements with deep nesting in CSS may seem not-so-great to some folks, but think about the benefits for a minute. You get:
- Code that is easier to reuse by subclassing
- Code that is easier to understand because of BEM's naming convention, which is particularly useful if you're new to an existing project and trying to understand the previous developer's approach, or coming back to your own code after a long period of time
- CSS that is flatter and massively less prone to time-consuming specificity issues
- CSS that has a structure, and again, follows a naming convention thanks to BEM or whatever CSS framework you choose
CSS has no inherent structure, you can do whatever you want with it, which most of the time is not good in terms of reuse and scalability. Your CSS is going to benefit from having a rules instead of having a mishmash of different approaches. And I'm talking about a stylesheet that has only been worked on by one developer. Once you get a few developers in the mix YOU WILL NEED RULES. I've been there, I'm sure you have too.
Example 2: The base block with the addition of striped even rows and a right-aligned product image.
Our Products
Product One
Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.
Product Two
You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.
Product Three
Poutine seitan fap, keytar fixie fanny pack. Whatever wayfarers kitsch banjo before they sold out. Leggings kickstarter shabby chic, irony crucifix polaroid vegan yuccie heirloom wayfarers slow-carb.
HTML
<section class="b-products products__striped--even"> <h2 class="products__section-header">Our Products</h2> <hr> <div class="products__wrap"> <article class="products__item"> <img class="products__img" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product One</h3> <p class="products__copy">Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.</p> </article> <article class="products__item"> <img class="products__img products__img--right" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Two</h3> <p class="products__copy">You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.</p> </article> <article class="products__item"> <img class="products__img" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Three</h3> <p class="products__copy">Poutine seitan fap, keytar fixie fanny pack. Whatever wayfarers kitsch banjo before they sold out. Leggings kickstarter shabby chic, irony crucifix polaroid vegan yuccie heirloom wayfarers slow-carb.</p> </article> </div> </section> |
Here I've added only two subclasses to reuse the base block in a different configuration. products__striped--even
handles the striping of even numbered children article
tags. This is accomplished using the CSS :nth-child
selector and is the only area in my Sass where I chose to nest. Applying a striping class manually to every even numbered article
in a reusable, scalable block doesn't sound like a great idea. What happens if the block is reused with a 500-product list? Yeah…let's go ahead and nest in this instance. To round things out, products__img--right
simply handles the right-floated image.
Example 3: The base block with the addition of a row layout on 600px + viewports.
Our Products
Product One
Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.
Product Two
You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.
HTML
<section class="b-products"> <h2 class="products__section-header">Our Products</h2> <hr> <div class="products__wrap products__wrap--row"> <article class="products__item products__item--center"> <img class="products__img products__img--center" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product One</h3> <p class="products__copy">Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.</p> </article> <article class="products__item products__item--center"> <img class="products__img products__img--center" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Two</h3> <p class="products__copy">You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.</p> </article> </div> </section> |
The subclassing gets a little more intensive here because I'm altering the base block to operate as a column layout instead of a row layout. This also means I have to adjust the float and text-align of some elements as well. I'm subclassing with products__wrap--row
to change the flex-flow
from column to row. I'm also subclassing with products__item--center
and products__img--center
to deal with the float and text-align. I've just completely changed the base block into a whole new configuration using only three subclasses. Awesome.
Example 4: The base block with the addition of a row layout on 600px + viewports, striped odd rows/columns, and left-aligned content on 1200px + viewports.
Our Products
Product One
Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.
Product Two
You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.
HTML
<section class="b-products products__striped--odd"> <h2 class="products__section-header">Our Products</h2> <hr> <div class="products__wrap products__wrap--row"> <article class="products__item products__item--center--left"> <img class="products__img products__img--center--left" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product One</h3> <p class="products__copy">Distillery taxidermy asymmetrical, viral swag photo booth street art ramps mlkshk tattooed normcore hella franzen put a bird on it kitsch. Chillwave paleo humblebrag cronut pickled keffiyeh.</p> </article> <article class="products__item products__item--center--left"> <img class="products__img products__img--center--left" alt="image placeholder" src="/assets/image__placeholder.jpg"> <h3 class="products__header">Product Two</h3> <p class="products__copy">You probably haven't heard of them, single-origin coffee authentic ennui humblebrag slow-carb farm-to-table. PBR&B heirloom direct trade, normcore organic pop-up letterpress.</p> </article> </div> </section> |
In this final example I'm using four subclasses to alter the base block. products__striped--odd
handles the striping of odd numbered children article
tags. products__wrap--row
changes the flex-flow
from row to column. products__item--center--left
and products__img--center--left
deal with float and text-align at different viewports. This last two classes are a perfect example of those gray areas of BEM that I mentioned earlier. What I'm trying to accomplish are class names that have different values at different viewports. With products__img--center--left
for example, I want the image this class is being applied to to be centered below 1200px viewports and floated left above 1200px viewports. Hence the order of the BEM modifiers --center--left
in the class name. Is this the best way to do this? Maybe, maybe not, but it's the best solution I've come up with so far and it does the job. Am I open to another approach? Absolutely.
Pretty Cool, Right?
We just saw how a single BEM block can be configured four different ways just by adding a handful of subclasses. Of course there are a dozen ways I could have tackled building this block. If taking a different approach seems better for you, don't be afraid, go for it. Just take some time to think through your approach. The smallest oversights can sometimes become the biggest headaches when you're knee-deep into building your block.
Final Thoughts
Just some closing notes on things I've learned while using BEM in the real world.
Start building mobile first if you're not already. This applies to any CSS framework, not just BEM. By starting with mobile first, you are adding CSS as needed as your viewport gets wider instead of subtracting CSS as your viewport gets smaller. Your code will be cleaner and you won't be overwriting desktop-view CSS with mobile-view CSS. This is a recipe for disaster which can quickly get unwieldy and steers you right into specificity issues.
When creating new blocks it also helps to have a sandbox. By this I mean a place where you can experiment and test your code. I go into my sandbox with the goal of making whatever block I’m working on as modular, reusable and scalable as possible. The sandbox approach does take some extra time, but it’s worth the extra effort in the long run. I’ve skipped this step in the past due to time constraints, which is understandable, but I’ve also experienced the pain that can come later down the road when you're trying to repurpose or scale your code and it wasn't built with this in mind the first time.