Skip to content

Repository files navigation

Build StatusCoverage StatusLanguage grade: JavaScriptPackage dependenciesnpm bundle sizeMaintenancenpm version

BEM

css modules + bem + react = love

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.jsimportmakefrom'bem';// `make` allows you to customize bem prefixesexportdefaultmake({elemPrefix: '__',modPrefix: '--',valuePrefix: '_',});

2. Import bem into a React component, create block and elem functions and use them in render method

// Button.jsimportReact,{Component}from'react';importPropTypesfrom'prop-types';importbemfrom'./initBem';importclassnamesMapfrom'./Button.scss';const{ block, elem }=bem('Button',// Block nameclassnamesMap// Class names dict generated by CSS modules loader);constButton=(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,};exportdefaultButton;

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.

constresult=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.

constresult=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/>
↓ ↓ ↓
<buttonclass="Button"><spanclass="Button__label" /></button>

Prop active is set:

<Buttonactive={true}/>
↓ ↓ ↓
<buttonclass="Button Button--active"><spanclass="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).

<Buttonactive={true}type='extraordinary' />
↓ ↓ ↓
<buttonclass="Button Button--active Button--type Button--type_extraordinary"><spanclass="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.

<Buttonactive={false}/>
↓ ↓ ↓
<buttonclass="Button"><spanclass="Button__label" /></button>

Clicked state

<Button/><!-- this.setState({ clicked: true }) -->
↓ ↓ ↓
<buttonclass="Button Button--clicked"><spanclass="Button__label Button__label--clicked" /></button>

About

✨ Magically generates class names for React components

Topics

Resources

Stars

0 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages