https://github.com/textkernel/bem
✨ Magically generates class names for React components
https://github.com/textkernel/bem
bem css-modules react
Last synced: 7 months ago
JSON representation
✨ Magically generates class names for React components
- Host: GitHub
- URL: https://github.com/textkernel/bem
- Owner: textkernel
- License: mit
- Created: 2019-05-28T15:48:40.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-04T21:47:21.000Z (about 3 years ago)
- Last Synced: 2025-06-09T11:49:25.999Z (8 months ago)
- Topics: bem, css-modules, react
- Language: TypeScript
- Homepage:
- Size: 2.28 MB
- Stars: 0
- Watchers: 5
- Forks: 1
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://travis-ci.com/textkernel/bem)
[](https://coveralls.io/github/textkernel/bem?branch=master)
[](https://lgtm.com/projects/g/textkernel/bem/context:javascript)



[](https://www.npmjs.com/package/@textkernel/bem)
BEM
===

**Magically generates class names for React component.**
Installation
------------
```sh
npm install @textkernel/bem --save
```
or
```sh
yarn add @textkernel/bem
```
Usage
-----
## 1. Create and export your own **bem** function using `make`.
```js
// 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
```js
// 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) => (
{props.children}
{props.children}
);
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.
```ts
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.
```ts
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.
```css
/* 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:
```html
↓ ↓ ↓
```
### Prop `active` is set:
```html
↓ ↓ ↓
```
### 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).
```html
↓ ↓ ↓
```
### Prop `active` equals false
No classnames will be produced if boolean property has `false` value.
```html
↓ ↓ ↓
```
### Clicked state
```html
↓ ↓ ↓
```