← All Articles

Design patterns for nested BEM components

Brent GrahamBrent Graham
Oct 31st 17Updated Jul 6th 22

design-patterns-for-nested-components

CSS component methodologies like BEM have been in wide use for a few years now. At Cloud 66 we've been using SUIT for component-based UI development.

This approach generally works well—over time you build a library of reusable components that can be combined together to assemble your user interface. Reusable components should be independent and not rely on existing within a certain part of the DOM tree.

One area where you may run into issues using BEM is with nested components, especially where you might want to alter the look of a component that is a descendant of a specific parent component. A simple example might be a button nested within a header.

<header class=“Header”>
    <button class=“Button”>Button</button>
</header>

There are a few different ways you might approach this task. Each method has it's own pros and cons that you'll need to weigh up.

Descendant selector

.Header .Button {
  font-size: 1rem;
}

The first solution that might come to mind is the good old descendant selector. This will get the job done but goes against the BEM and component UI design principles.

It makes the .Button component unpredictable as it can now render differently depending on where it lives in the HTML. It would also cause problems if you later need to add additional different button styles.

BEM Contextual Modifier

.Button--header {
  font-size: 1rem;
}

This .Button component may have its own modifiers like .Button--small, .Button--medium, .Button--large. However, for this use case we want to apply styling only to buttons nested in the header.

The most obvious solution in BEM is to create a new contextual button modifer like .Button--header. This is better than a descendent selector as you can still use the original .Button component in the header and can create additional modifiers if you need more button styles within the header.

The downside of this approach is the number of contextual modifiers can quickly get out of hand and it can be difficult to find and manage them. Most of your components will end up having their own CSS file, this makes things easy to find. However, in this example, header styles may be located in the buttons.css component file instead of the header.css file.

The Adopted Child or Mixes Pattern

The adopted child pattern or mixes as it’s called in BEM allows you to style an element with two classes from two different components. This is a lesser known approach that I haven't often used very often.

.Header-item {
  font-size: 1rem;
}
<header class=“Header”>  
    <div class=“Header-item”>
        <button class="Button">button</button>
    </div>
</header>
<header class=“Header”>
    <button class=“Header-item Button”>Button</button>
</header>

This technique allows the button and header component to remain independent. You could also nest multiple components within .Header-item if they share common styling.

The disadvantage of this approach is that you may inadvertently override existing modifiers. For example, if you were using .Button--small in your header button or you have modifiers for color-coded buttons e.g. .Button--primary or .Button--secondary.

One way to avoid this kind of problem is not to use the Adopted Child pattern for theme related style rules. The pattern is, therefore, most useful for altering layout, size or positioning.

The SUIT documentation contains a similar example for styling dependencies.

Controlling dimensions, margins, position, and inheritable styles of a component can be done indirectly. Add a class to its root element, or wrap it in another element.

The documentation gives the example of an .Excerpt component that contains a button that uses a wrapper .Excerpt-wrapButton.

@extend

If you're using a CSS preprocessor like Sass you may decide to add a new component that extend's the existing button component.

@import './components/button.css';

.HeaderButton {
    @extend .Button;
    /* Overriding styles... */
    font-size: 1rem;
}
<header class="Header">
    <button class="HeaderButton">Download</button>
</header>

This option works well as long as your project has access to a preprocessor.

If you're using a component methodology the first option of using a descendant selector is best avoided. The other three options are all acceptable choices depending on the project and use cases you're designing for.


Try Cloud 66 for Free, No credit card required