bem-cn
Version:
Friendly BEM class names generator, greate for React
184 lines (130 loc) • 4.43 kB
Markdown
[](https://travis-ci.org/albburtsev/bem-cn) [](https://github.com/prettier/prettier)
Friendly [BEM](https://en.bem.info/) class names generator. Great for [React](http://facebook.github.io/react/).
**Bem-cn** (aka BEM Class Name) is extra small (minified+gzipped less than 1.6Kb) and extremely simple client-side library and Node.js module.
**Important!** Only `bem-cn@3.x+` compatible with `react@16+`.
Please do not use version 2.x or lower.
[More](https://github.com/facebook/react/issues/10857) [details](https://github.com/facebook/react/issues/10756) about the problem.
Inspired by [b_](https://github.com/azproduction/b_).
## Justification
I spent a lot of time finding [BEM](https://en.bem.info/) class name generator, that meets my needs:
* Simple usage with React
* Support modifiers without value
* Mix multiple blocks
* Friendly API
When my efforts had led to naught I've created this micro library.
## Install
With Node.js:
```bash
npm i --save bem-cn
yarn add bem-cn
```
Works with [webpack](http://webpack.github.io/) and [browserify](http://browserify.org/):
```js
// CommonJS
var { block } = require('bem-cn');
// ES6
import { block } from 'bem-cn';
```
```js
const b = block('button');
// Block
b(); // 'button'
// Element
b('icon'); // 'button__icon'
// Modifier
b({ type: 'text' }); // 'button button_type_text'
b({ onlykey: true }); // 'button button_onlykey'
b({ without: false }); // 'button'
b('icon', { name: 'check' }); // 'button__icon button__icon_name_check'
// Mix another classes
b('icon').mix('another'); // 'button__icon another'
b('icon').mix(['one', 'two']); // 'button__icon one two'
// States like in SMACSS: https://smacss.com/book/type-state
b.state({ hidden: true }); // 'button is-hidden'
b.state({ hidden: false }); // 'button'
b.state({ hidden: true, error: true }); // 'button is-hidden is-error'
// More states!
b.is({ loading: true }); // 'button is-loading'
b.has({ content: true }); // 'button has-content'
```
```js
// Setup custom delimiters
import { setup } from 'bem-cn';
const block = setup({
el: '~~',
mod: '--',
modValue: '-'
});
const b = block('block');
b('element'); // 'block~~element'
b({ mod: 'value' }); // 'block block--mod-value'
```
```js
// Setup namespace
const block = setup({ ns: 'ns-' });
const b = block('block');
b(); // 'ns-block'
b('element'); // 'ns-block__element'
b({ mod: 'value' }); // 'ns-block ns-block_mod_value'
```
```js
import block from 'bem-cn';
import React from 'react';
import ReactDOM from 'react-dom';
const b = block('popup');
const Popup = React.createClass({
render() {
const { skin, children } = this.props;
return (
<div className={b()}>
<span className={b('icon')} />
<div className={b('content', { skin })}>
{children}
</div>
</div>
);
}
});
ReactDOM.render(<Popup skin="bright">Hello!</Popup>, target);
/*
<div class="popup">
<span class="popup__icon"></span>
<div class="popup__content popup__content_skin_bright">
Hello!
</div>
</div>
*/
```
@todo
`bem-cn@2.x` or lower has specific chainable API. As a result, each call returns function for a further call. But most components are expecting property `className` as a string and using `propTypes` object for check this. In this case, you will see a warning. There are the couple of ways to avoid these warnings below.
Use final call without arguments to get a string
```jsx
<CustomComponent className={b('icon')()} />
```
Use explicit call of method `toString()`:
```jsx
<CustomComponent className={b('icon').toString()} />
```
Use less specific propTypes rules:
```js
let CustomComponent = React.createClass({
propTypes: {
className: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.func
])
},
// ...
});
```
`bem-cn` is fully compatible with ES5 browsers. If you are going to support ES3 browsers than just use [es5 shim](https://github.com/es-shims/es5-shim).