Using BEM Classes
The BEM methodology was created at Yandex for developing websites that need to be built quickly and maintained for many years. It allows creating scalable and reusable interface components.
You can read more about the approach in the official documentation
In this article, we will look at how to implement this approach using built-in tools.
Basic Usage
To work with classes in Steroids, use the useBem hook imported from @steroidsjs/core/hooks.
As the only argument, it takes the name of the BEM block.
Block
To create a BEM block, use the block method of the useBem hook.
export default function NavBarTemplate() {
const bem = useBem('NavBarTemplate');
return (
<div className={bem.block()}> // will be replaced with NavBarTemplate
// Rest of the code
</div>
);
}Element
To create a BEM element, use the element method of the useBem hook.
Basic usage example:
export default function NavBarTemplate() {
const bem = useBem('NavBarTemplate');
return (
<div className={bem.block()}> // NavBarTemplate
<div className={bem.element('logo')}> // NavBarTemplate__logo
// Logo
</div>
<a href='#' className={bem.element('link')}> // NavBarTemplate__link
// Link to home
</a>
// Rest of the code
</div>
);
}BEM Block Modifier
To create a BEM block modifier, pass the modifier as an argument to the block method.
export default function CustomButton() {
const bem = useBem('CustomButton');
return (
<button className={bem.block('red')}> // CustomButton CustomButton_red
// Rest of the code
</button>
);
}Note that both the BEM block class and the BEM modifier class will be applied.
BEM Element Modifier
To create a BEM element modifier, pass the modifier as the second argument to the element method.
<div className={bem.block()}> // NavBarTemplate
<div className={bem.element('logo', 'big')}> // NavBarTemplate__logo NavBarTemplate__logo_big
// Logo
</div>
</div>Multiple and Conditional Modifiers
An object can be used as a modifier, where the keys are the modifiers and the values are the conditions under which the modifiers will or will not be applied.
<button
className={bem.block({ // The block class will always be applied (e.g., CustomButton)
hidden: props.isHidden, // if props.isHidden === true, the CustomButton_hidden class will be applied
})}
>
<span
className={bem.element('content', { // The CustomButton__content class will always be applied
firstLine: true, // the CustomButton__content_firstLine class will always be applied
dark: theme === 'dark' // if theme === 'dark', the CustomButton__content__dark class will be applied
})}
>
Submit form
</span>
</div>The value in the modifier object can be not only boolean, but also string or number.
<div className={bem.element('buttons')}>
{['one', 'two', 'three'].map((value, index) => (
<div
key={index}
className={bem.element('button', {
number: index,
})}
>
{value}
</div>
))}
</div>
// transforms into the markup:
<div class="NavBar__buttons">
<div class="NavBar__button">one</div>
<div class="NavBar__button NavBar__button_number_1">two</div>
<div class="NavBar__button NavBar__button_number_2">three</div>
</div>Combining Classes
If you need to combine multiple classes on one element (for example, when you need to pass styles from the parent to a custom button component), you can pass the necessary classes.
// The result of calling the bem.element('home', 'active') function in the parent element is passed as additionalClassNames
export default function CustomButton(props: {additionalClassNames: string}) {
const bem = useBem('CustomButton');
return (
<button className={bem(
bem.block(),
props.additionalClassNames
)}> // CustomButton NavBarTemplate__home NavBarTemplate__home_active
// Rest of the code
</button>
);
}Summary
const bem = useBem('Block');
bem.block(); // Block
bem.block('modificator'); // Block Block_modificator
bem.block({
modificator: false,
hidden: true,
button: 'one',
}); // Block Block_hidden Block_button_one
bem.element('Element'); // Block__Element
bem.element('Element', 'modificator'); // Block__Element Block__Element_modificator
bem.element('Element', {
modificator: true,
hidden: false,
button: 'two',
}); // Block__Element Block__Element_modificator Block__Element_button_one
bem(
bem.block(),
bem.element('FirstElement'),
bem.element('SecondElement', 'modificator'),
'visuallyHidden'
); // Block Block__FirstElement Block__SecondElement Block__SecondElement_modificator visuallyHidden