Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/textkernel/bem

✨ Magically generates class names for React components
https://github.com/textkernel/bem

bem css-modules react

Last synced: about 1 month ago
JSON representation

✨ Magically generates class names for React components

Awesome Lists containing this project

README

        

[![Build Status](https://travis-ci.com/textkernel/bem.svg?branch=master)](https://travis-ci.com/textkernel/bem)
[![Coverage Status](https://coveralls.io/repos/github/textkernel/bem/badge.svg?branch=master)](https://coveralls.io/github/textkernel/bem?branch=master)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/textkernel/bem.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/textkernel/bem/context:javascript)
![Package dependencies](https://img.shields.io/david/textkernel/bem.svg?style=flat)
![npm bundle size](https://img.shields.io/bundlephobia/minzip/@textkernel/bem.svg)
![Maintenance](https://img.shields.io/maintenance/yes/2019.svg)
[![npm version](https://img.shields.io/npm/v/@textkernel/bem.svg)](https://www.npmjs.com/package/@textkernel/bem)

BEM
===

![css modules + bem + react = love](https://raw.githubusercontent.com/textkernel/bem/master/docs/logo.svg?sanitize=true)

**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

↓ ↓ ↓

```