bemNames
![npm version](https://badge.fury.io/js/bem-names.svg)
Advanced generator of bem-like class names. bemNames
can follow any BEM
naming convention and allow easy transition between any of them. It also
supports the transition between classic classnames as well as
css-Modules.
Install
yarn add bem-names
npm install bem-names --save
Basic usage
The bemNames
function takes any number of arguments, which can be a string,
array or object. The first two arguments must be strings and are treated
accordingly as the name of the block and the element. In the default
configuration, the modifiers must be wrapped with []
or {}
, in order to
maintain clarity of what is a block, element or modifier. This and many other
behaviours can be changed, check advanced usage.
import bemNames from 'bem-names';
bemNames('block');
bemNames('block', ['mod']);
bemNames('block', 'element', ['mod']);
bemNames('block', 'element', { mod2: true, mod3: false });
With factory:
import { bemNamesFactory } from 'bem-names';
const bem = bemNamesFactory('block');
bem();
bem(['mod']);
bem('element', ['mod']);
bem('element', { mod2: true, mod3: false });
Motivation
When I tried to add a BEM-like styled component from the npm to project
with different BEM-naming conventions I've encounter two obstacles:
- There was a class name collision with existing components
- The component followed different class naming convention, and did not fit
with existing scss helper functions.
Ideal solution would be if the component had an option to pass a class name
generator (same way I'm working in the mentioned project). Unfortunately this
was not the case, so I've decided to change it. My first step was writing such
a flexible generator, with option to use
css-modules in the near future.
Done :D
Other projects
There are many great generators of BEM-like class names. But none of them can
be used as a generic generator covering various conventions. Below are several
projects I've tested.
-
bem-classname Very basic
generator covering single convention.
-
bem-classnames Basic
generator covering single convention with troublesome need of setting up
configuration for every component. The slowest of presented here.
-
b_ Very fast generator allowing basic
configuration but limited in BEM-flexibility.
-
bem-cn Versatile BEM class name
generator. Does not have flexible API allowing to apply other conventions.
Personally prefer API similar to "classic" classnames.
-
classnames Good class name
generator but without support form BEM-like naming convention. :]
Performance
I've performed some performance tests. Each packaged received same parameters,
and bemNames was configured to match output for each of the packages.
Implementation with de-duplication was about 18% slower.
| 10K | bemNames |
---|
b_ | 2ms | 12ms |
bem-classname | 13ms | 11ms |
bem-classanmes | 72ms | 11ms |
bem-cn | 23ms | 12ms |
classnames | 3ms | 7ms |
Advanced usage
bemNames
was create with castomization in mind. Configuration object is
merged with the default configuration so there is no need to specify all of the
options every time.
import { customBemNames } from 'bem-names';
const config = {
separators: { element: '-' },
states = { mod1: 'is-mod1' },
};
customBemNames(config, 'block', 'element', ['mod1']);
The customBemNames
is created for in-line usage, for more generic approuch
there is bemNamesFactory
.
import { bemNamesFactory } from 'bem-names';
const config = {
separators: { element: '-' },
states = { mod1: 'is-mod1' },
};
const bem = bemNamesFactory('block', config);
bem('element', ['mod1']);
Config object
const defaultConfig = {
bemLike: true,
separators: { element: '__', modifier: '--', keyValue: '-' },
states: {},
joinWith: ' ',
keyValue: false,
stringModifiers: StringModifiers.WARN,
parseModifier: defaultParseModifier,
styles: undefined,
stylesPolicy: StylesPolicy.WARN,
};
export const StringModifiers = {
WARN: 'warn',
ALLOW: 'allow',
PASS_THROUGH: 'passThrough',
};
export const StylesPolicy = {
WARN: 'warn',
IGNORE: 'ignore',
};
Sample configurations
emulate classNames like behaviour
import { customBemNames } from 'bem-names';
const cn = (...args) => customBemNames({ bemLike: false }, ...args);
cn(['block', 'element'], { mod1: false }, ['mod2'], 'mod3');
BEM with regular classes
import { bemNamesFactory, StringModifiers } from 'bem-names';
const config = {
stringModifiers: StringModifiers.PASS_THROUGH,
};
const bem = bemNamesFactory('block', config);
bem('element', { mod1: true }, 'mod3');
bem('element', 'mod3');
bem('hmmm');
BEM with states
import { bemNamesFactory } from 'bem-names';
const config = {
states = { disabled: 'is-disable', values: 'has-values' },
};
const bem = bemNamesFactory('block', config);
bem({ disabled: true, mod: true });
BEM with keyValues
import { bemNamesFactory } from 'bem-names';
const config = {
keyValue = true,
};
const bem = bemNamesFactory('block', config);
bem({ disabled: true, mod: false, key: 'value' });
css-modules
import { bemNamesFactory } from 'bem-names';
const config = {
styles: { block: '123', 'block--disabled': 234 },
};
const bem = bemNamesFactory('block', config);
cn('block', { disabled: true, mod: false });
cn('block', { disabled: true, key: 'value' });