Block, Element, Modifying Your JavaScript Components

Mark Dalgleish
SEEK blog
Published in
6 min readApr 30, 2015

--

BEM was a transformative methodology in the world of CSS, but — when limited to the style sheet — it can only take us so far.

The act of defining “blocks” forced us to treat our classes more semantically and methodically, clearly defining the component boundaries and relationships in our interfaces, and yet — as I’m sure the methodology’s originators at Yandex would be quick to point out — CSS is only a fraction of the BEM puzzle.

We need a new generation of tooling and workflows to make this methodology truly transform the way we construct our interfaces.

Thanks to BEM’s naming conventions, what used to be a big ball of HTML became a suite of nested components. However, for many of us the component abstraction didn’t go further than the style sheet.

Our single page apps could quickly devolve into a collection of massive controllers with overly bloated templates, combining multiple blocks into a larger, combined view.

Remember the gist of this (admittedly contrived) markup, we’ll revisit it later.

<div class="my-page">
<header class="header">
<h1 class="heading header__heading">...</h1>
</header>
<article class="post">
<header class="post__header">
<h1 class="heading post__header__heading">Hello World</h1>
</header>
<section class="post__content">...</section>
</article>
<footer class="footer">...</footer>
</div>

This is the environment in which the verbosity of BEM’s naming convention could evoke repulsion from many developers, but this reaction was arguably a sign that BEM was merely being applied at a surface level. Without JavaScript, this technique could only take us so far.

The Yandex team also developed a matching JavaScript implementation for BEM, but it used a custom template format that isn’t widely used — certainly not to the degree that BEM is used for CSS.

As we move into a world of component-based UI libraries like React, Ember and Angular — not to mention Web Components themselves— the thought process that many of us honed with BEM can now help us write better JavaScript.

That is, of course, assuming that we properly leverage the tools these frameworks provide to help us break our applications down into smaller, re-usable pieces. Many applications — particularly those using frameworks with string-based templates like Angular and Knockout — only leverage components for lower level widgets, nesting them inside more traditional pages with many concerns jostling for your attention.

Fighting to build a truly component-based architecture requires careful consideration. We need to reframe the way we think about our front-end architectures — it needs to be components all the way down, consistently across different asset types, and not simply at the edges of our application.

“Thinking in components” is fundamentally different to how we’ve worked before.

As Pete Hunt famously said while defending the initially controversial nature of React’s JSX, we’ve long been forced to separate our technologies rather than our concerns.

While a single component may be made up of smaller fragments of HTML, CSS, JavaScript, fonts, images and other media, it’s only when these assets come together that they provide value — and yet, our tooling often enforces them to remain separated at a fundamental level until they are somehow laced together at build time.

Gulp builds that handle each asset type separately can be manageable for smaller projects, but scale awkwardly as each asset type increasingly depends on the others. By coupling a series of seemingly disparate asset compilation steps, we’ve now created a maintenance burden with countless places for bugs to hide.

Soon enough we find that we’ve created a wide gulf between how we picture our ever-growing asset graph, and how it’s actually described in code.

To tackle this all-too-common problem, we’ve been greeted with a new generation of build tools that aid us in the creation of component-based architectures.

Webpack, JSPM and — with the right collection of plugins — even Browserify, can now help us expose components composed of multiple, varying asset types through a single import statement.

import MyComponent from ‘./MyComponent’;

The fact that this might internally contain a style sheet with multiple images and dependencies on a handful of smaller components is simply implementation detail that the consuming code doesn’t need to worry about. It’s only in our build configuration that we decide where all of these assets should go.

Inside our component we require all dependencies, including CSS.

require('./MyComponent.less');import { Component } from 'react';export default class MyComponent extends Component {
render() {
return (
<div className="MyComponent">
<div className="MyComponent__Icon">Icon</div>
...
</div>
);
}
}

Our CSS then includes any assets it needs using the standard ‘url’ syntax.

.MyComponent__Icon {
background-image: url('icon.svg');
background-position: 0 50%;
background-size: fit;
height: 50px;
}

The relationships between the assets is now within the assets themselves rather than a standalone build script, detached from the actual content.

This allows us to configure our build process at a higher level—using a series of Webpack loaders, for example—to define how each asset type should be handled across the board. We can extract styles into a separate file with Webpack’s Extract Text plugin, inline images below a certain size with url-loader, and transparently compile ES6/7 code with Babel— things that previously used to require a lot more manual build configuration.

// webpack.config.js:loaders: [
{
test: /\.less$/,
loader: ExtractTextPlugin.extract('style',
'css!autoprefixer!less')
},
{
test: /\.svg$/,
loader: 'url?limit=10000&mimetype=image/svg+xml'
},
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
}
]

So how do we bring BEM into the broader world of JavaScript components?

While it may be easy to fall into the old habits of mixing-and-matching different BEM blocks within singular components — a habit that slowly snowballs into a serious maintenance headache—it’s important to recognise that a ‘block’ in BEM and a ‘component’ in JavaScript is the exact same abstraction, only expressed in different languages. This leads us to a very natural conclusion.

Blocks should only be used inside a component of the same name.

For example, given our ‘MyComponent’ example, the only place in your app that you should see elements with a class name beginning with ‘MyComponent’ should be inside ‘MyComponent.js’.

If you ever find yourself habitually reaching for a new block inside of a larger component, take this as a not-so-subtle hint that your component is taking on too many concerns. It’s time to write a new component.

This simple rule of thumb has a surprisingly positive impact on the quality of your codebase.

Revisiting our BEM-styled markup from earlier, we can now ensure that multiple blocks don’t appear in the same template by encapsulating them within individual components.

Again, admitting that this a very contrived example…

require('./MyPage.less');import { Component } from 'react';import Header from 'Header';
import Post from 'Post';
import Footer from 'Footer';
export default class MyPage extends Component {
render() {
return (
<div className="MyPage">
<Header />
<Post title="Hello World" content="..." />
<Footer />
</div>
);
}
};

Forcing CSS and other asset types to be private implementation detail of a component, rather than something that is littered throughout your codebase, opens up a whole new level of opportunity for more efficient creation, optimisation and abstraction in our interfaces. Copying-and-pasting markup between pages — if it wasn’t already — should become a thing of the past.

As a bonus, taking this methodology to its logical conclusion will naturally result in a process that is much more amenable to style guide driven development. Using libraries like React where the cost of creating a new component is extremely low will quickly reward any efforts to separate display logic into smaller, independently testable units.

Working towards the creation of a living style guide, where any visual updates are immediately propagated to production, requires another layer of discipline — ensuring that your low level components are isolated and strictly implemented as pure, completely stateless components — but that’s a story for another time.

Note: I’m sure many of you are wondering why I don’t give up on CSS entirely and use JavaScript for styles instead. This approach is gaining in popularity in the React community and eliminates the problem of global selectors that BEM tries to solve. The current set of tooling for this approach is yet to properly support progressive enhancement (support for media queries, pseudo classes and sibling selectors is patchy, at best), but this is an area that is showing a lot of promise.

--

--

Mark Dalgleish
SEEK blog

CSS Modules co-creator, @MelbJS organiser, DesignOps Lead at @seekjobs.