@textkernel/bem
v0.2.4
Published
Automatically generates list of classnames based on its props/state for your React component.
Downloads
1,366
Keywords
Readme
BEM
Magically generates class names for React component.
Installation
npm install @textkernel/bem --save
or
yarn add @textkernel/bem
Usage
1. Create and export your own bem function using make
.
// initBem.js
import make from 'bem';
// `make` allows you to customize bem prefixes
export default make({
elemPrefix: '__',
modPrefix: '--',
valuePrefix: '_',
});
2. Import bem into a React component, create block
and elem
functions and use them in render method
// Button.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import bem from './initBem';
import classnamesMap from './Button.scss';
const { block, elem } = bem(
'Button', // Block name
classnamesMap // Class names dict generated by CSS modules loader
);
const Button = (props) => (
<button
{/* If needed, `props` should be spread before `block` or `elem in order to avoid className overwrite. */}
{...props}
{/* Spread `block` to add class names to the top level node */}
{ ...block(props) }
>
<span
{/* Spread `elem` to add class names to an element. */}
{ ...elem('label', props) }
>
{props.children}
</span>
<span
{/* Custom modifiers combined with props. */}
{...elem('icon', {
...props,
almostRandomValue: 42
})}
>
{props.children}
</span>
</button>
);
Button.propTypes = {
active: PropTypes.bool,
};
Button.defaultProps = {
active: false,
};
export default Button;
Passing custom class names to block
and elem
functions.
block
If props
object that you pass to block
contains className
property, then this className
is applied to the resulting class name list. In case of elem
function though it is ignored.
const result = block('Button', { size: 'big', className: 'custom-class-name' });
result.className === 'Button Button--size_big custom-class-name' // true
elem
If props
object that you pass to elem
contains elemClassName
property, then this elemClassName
is applied to the resulting class name list. In case of block
function though it is ignored.
const result = elem('label', { position: 'right', elemClassName: 'custom-elem-class-name' });
result.className === 'Button__label Button__label--position_right custom-elem-class-name' // true
3. Write css respecting BEM methodology and it will be automatically picked up.
/* Button.scss */
/* Component's root node class name */
.Button {
display: inline-block;
/*
Block: "Button", modifier: "active" (based on props.active), value: true.
Is applied to the component's root node when props.active = true is set.
*/
&--active {
color: red;
}
/*
Block: "Button", modifier: "type" (based on props.type), any truthy value.
Is applied to the component's root node when `props.type = "normal"` is set.
*/
&--type {
border: 1px;
}
/*
Block: "Button", modifier: "type" (based on props.type), value: "normal".
Is applied to the component's root node when `props.type = "normal"` is set.
*/
&--type_normal {
background-color: grey;
}
/*
Block "Button", modifier "type" (based on props.type), value "extraordinary".
Is applied to the component's root node when `props.type = "extraordinary"` is set.
*/
&--type_extraordinary {
background-color: red;
}
/*
Block "Button", modifier "clicked" (based on state.clicked), value true.
Is applied to the component's root node when `state.clicked = true` is set.
*/
&--clicked {
border-style: dashed;
}
/*
Block "Button", element "label"
Is applied to the component's label node.
*/
&__label {
color: blue;
}
/*
Block "Button", element "label", modifier: "active" (based on props.active), value: true.
Is applied to the component's label node when props.active = true is set.
*/
&__label--active {
color: yellow;
}
/*
Block "Button", element "label", modifier "extraordinary" (based on props.type), value "extraordinary".
Is applied to the component's label node when `props.type = "extraordinary"` is set.
*/
&__label--type_extraordinary {
color: orange;
}
}
Examples of outcome
Having the example above we can get the following results.
bem
decorator adds only classnames that are declared in a stylesheet and
respectively exists in classnames map.
No props:
<Button />
↓ ↓ ↓
<button class="Button">
<span class="Button__label" />
</button>
Prop active
is set:
<Button active={true} />
↓ ↓ ↓
<button class="Button Button--active">
<span class="Button__label Button__label--active" />
</button>
Prop active
and type
are set:
Note that property of a boolean type active={true}
produces Button__label--active
(without mod value), when property of a string type type='extraordinary'
gives us two class names: Button__label--type
(without mod value) and Button__label--type_extraordinary
(with mod value).
<Button active={true} type='extraordinary' />
↓ ↓ ↓
<button class="Button Button--active Button--type Button--type_extraordinary">
<span class="Button__label Button__label--active Button__label--type Button__label--type_extraordinary" />
</button>
Prop active
equals false
No classnames will be produced if boolean property has false
value.
<Button active={false} />
↓ ↓ ↓
<button class="Button">
<span class="Button__label" />
</button>
Clicked state
<Button /> <!-- this.setState({ clicked: true }) -->
↓ ↓ ↓
<button class="Button Button--clicked">
<span class="Button__label Button__label--clicked" />
</button>