{"id":14990666,"url":"https://github.com/monar/bem-names","last_synced_at":"2025-04-12T02:38:13.192Z","repository":{"id":66150261,"uuid":"76370893","full_name":"Monar/bem-names","owner":"Monar","description":"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 a transition between classic classnames as well as css-Modules.","archived":false,"fork":false,"pushed_at":"2019-02-08T11:55:15.000Z","size":279,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T02:37:54.494Z","etag":null,"topics":["bem","classnames","css-modules","transition"],"latest_commit_sha":null,"homepage":"https://monar.github.io/bem-names/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Monar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-13T15:22:19.000Z","updated_at":"2023-12-30T23:57:52.000Z","dependencies_parsed_at":"2023-02-21T09:01:09.202Z","dependency_job_id":null,"html_url":"https://github.com/Monar/bem-names","commit_stats":{"total_commits":87,"total_committers":5,"mean_commits":17.4,"dds":"0.22988505747126442","last_synced_commit":"5413184c1b1931d9fece26d7861d8d8772d2b43d"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monar%2Fbem-names","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monar%2Fbem-names/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monar%2Fbem-names/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monar%2Fbem-names/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Monar","download_url":"https://codeload.github.com/Monar/bem-names/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248507106,"owners_count":21115538,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bem","classnames","css-modules","transition"],"created_at":"2024-09-24T14:20:33.764Z","updated_at":"2025-04-12T02:38:13.165Z","avatar_url":"https://github.com/Monar.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bemNames\n\n[![npm version](https://badge.fury.io/js/bem-names.svg)](https://badge.fury.io/js/bem-names)\n[![Build Status](https://travis-ci.org/Monar/bem-names.svg?branch=master)](https://travis-ci.org/Monar/bem-names)\n[![Test Coverage](https://codeclimate.com/github/Monar/bem-names/badges/coverage.svg)](https://codeclimate.com/github/Monar/bem-names/coverage)\n\nAdvanced generator of BEM class names. `bemNames` can follow any BEM\nnaming convention and allow easy transition between any of them. It also\nsupports a transition between classic [classnames][classnames] as well as\n[css-Modules](https://github.com/css-modules/css-modules).\n\n## Install\n\n```bash\nyarn add bem-names\nnpm install bem-names --save\n```\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/bem-names/dist/bem-names.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/bem-names/dist/bem-names.js\"\u003e\u003c/script\u003e\n```\n\n## Basic usage\n\nThe `bemNames` function takes any number of arguments, which can be a string,\narray or object. The first two arguments must be strings and are treated\naccordingly as the name of the block and the element. In the default\nconfiguration, the modifiers must be wrapped with `[]` or `{}`, in order to\nmaintain clarity of what is a block, element or modifier. This and many other\nbehaviours can be changed, check [advanced usage](#advanced-usage).\n\n```js\nimport bemNames from 'bem-names';\n\nbemNames('block');\n// 'block'\nbemNames('block', ['mod']);\n// 'block block--mod'\nbemNames('block', 'element', ['mod']);\n// 'block__element block__element--mod'\nbemNames('block', 'element', { mod2: true, mod3: false });\n// 'block__element block__element--mod2'\n```\n\nWith factory:\n\n```js\nimport { bemNamesFactory } from 'bem-names';\n\nconst bem = bemNamesFactory('block');\n\nbem();\n// 'block'\nbem(['mod']);\n// 'block block--mod'\nbem('element', ['mod']);\n// 'block__element block__element--mod'\nbem('element', { mod2: true, mod3: false });\n// 'block__element block__element--mod2'\n```\n\nWith [secondary API](#secondary-api):\n\n```js\nimport bemNames from 'bem-names';\n\nconst bem =  bemNames.factory(\n  'block'\n  { stringModifiers: 'passThrough' },\n);\n\nbem(['blue'], 'extra-class');\n// 'block block--blue extra-class'\n```\n\n```js\nbemNames.custom({ state: { blue: 'is-blue' } }, 'block', ['blue']);\n// 'block is-blue'\n```\n\n## Motivation\n\nWhen I tried to add a BEM-like styled component\n[react-select](https://www.npmjs.com/package/react-select) to project with\ndifferent BEM-naming conventions I've encounter two obstacles:\n\n* There was a class name collision with existing components\n* The component followed different class naming convention, and did not fit\n  with existing [scss](http://sass-lang.com/) helper functions.\n\nTo work with these kind of components you need to accept their global namespace\npresents and/or wrap with dummy element just for styling.\n\nIdeal solution would be if the component had an option to pass a class name\ngenerator ([sample code][bemNames-react-select]), and this is where I've\ndecided to start. My first step was writing this generator, with option to\ntransitions to [css-modules](https://github.com/css-modules/css-modules) in the\nnear future, and this is what this library is all about.\n\n\n### Other projects\n\nThere are many great generators of BEM-like class names. But none of them can\nbe used as a generic generator covering various conventions. Below are several\nprojects I've tested.\n\n* **[bem-classname](https://www.npmjs.com/package/bem-classname)** Very basic\ngenerator covering single convention.\n\n* **[bem-classnames](https://www.npmjs.com/package/bem-classnames)** Basic\ngenerator covering single convention with troublesome need of setting up\nconfiguration for every component. The slowest of presented here.\n\n* **[b_](https://www.npmjs.com/package/b_)** Very fast generator allowing basic\nconfiguration but limited in BEM-flexibility.\n\n* **[bem-cn](https://www.npmjs.com/package/bem-cn)** Versatile BEM class name\ngenerator. Does not have flexible API allowing to apply other conventions.\nPersonally prefer API similar to \"classic\" [classnames][classnames].\n\n* **[classnames](https://www.npmjs.com/package/bem-cn)** Good class name\ngenerator but without support form BEM-like naming convention. :]\n\n### Performance\nI've performed some performance tests. Each packaged received same parameters,\nand bemNames was configured to match output for each of the packages.\n\n*Implementation with de-duplication was about 18% slower.*\n\n\n| |10K | bemNames |\n|:-:|--:|:-:|\n|b_              | 2ms   | 12ms |\n|bem-classname   | 13ms  | 11ms  |\n|bem-classanmes  | 101ms | 13ms  |\n|bem-cn          | 23ms  | 26ms  |\n|classnames      | 3ms   | 7ms  |\n\n\n## Advanced usage\n\n`bemNames` was create with castomization in mind. Configuration object is\nmerged with the default configuration so there is no need to specify all of the\noptions every time.\n\n```js\nimport { customBemNames } from 'bem-names';\n\nconst config = {\n  separators: { element: '-' },\n  states = { mod1: 'is-mod1' },\n};\n\ncustomBemNames(config, 'block', 'element', ['mod1']);\n// 'block-element is-mod1'\n```\n\nThe `customBemNames` is created for in-line usage, for more generic approuch\nthere is `bemNamesFactory`.\n\n```js\nimport { bemNamesFactory } from 'bem-names';\n\nconst config = {\n  separators: { element: '-' },\n  states = { mod1: 'is-mod1' },\n};\n\nconst bem = bemNamesFactory('block', config);\n\nbem('element', ['mod1']);\n// 'block-element is-mod1'\n```\n\n### Secondary API\n\n```js\n//bemNames.factory = bemNamesFactory;\n//bemNames.custom = customBemNames;\n//bemNames.StringModifiers = StringModifiers;\n//bemNames.StylesPolicy = StylesPolicy;\n\nconst config = {\n  separators: { element: '-' },\n  states = { mod1: 'is-mod1' },\n};\n\nbemNames.custom(config, 'block', 'element', ['mod1']);\n// 'block-element is-mod1'\n\nconst bem = bemNames.factory('block', config);\nbem('element', ['mod1']);\n// 'block-element is-mod1'\n```\n\n### Config object\n\n```js\nconst defaultConfig = {\n\n  /**\n  * When set to false generator will behave\n  * like \"classic\" classnames, also will not use these configuration options::\n  * seperators, states, keyValue, stringModifiers, parseModifier.\n  */\n  bemLike: true,\n\n  separators: { element: '__', modifier: '--', keyValue: '-' },\n\n  /**\n  * This configuration option is handled in the default parseModifier. If\n  * a modifier will match a key from this object, then instead returning\n  * bemName conjuction with modifier, the parser will return appropriate value.\n  */\n  states: {},\n\n  joinWith: ' ',\n\n  /**\n  * When set to true, modifiers from objects will be combined with its\n  * values, unless value is type of boolean.\n  */\n  keyValue: false,\n\n  /**\n  * This defines how to handle modifiers not wrap with [] or {}.\n  */\n  stringModifiers: StringModifiers.WARN,\n\n  /**\n  * This function is generating modifier strings. The default implementation is\n  * replaces modifiers with values from states and if modifier is not defined\n  * in states object then returns bemName joined with the modifier.\n  *\n  * (config:object, bemName:str, modifier:str) =\u003e string\n  */\n  parseModifier: defaultParseModifier,\n\n  /**\n  * This configuration option allows to apply css-modules, just set here object\n  * import from styles file.\n  */\n  styles: undefined,\n\n  /**\n  * Determines what to do when given modifer is missing in styles definitions.\n  */\n  stylesPolicy: StylesPolicy.WARN,\n};\n```\n\n```js\nexport const StringModifiers = {\n\n  /**\n  * Prints warning and ignores modifiers not wrapped with [] or {}.\n  */\n  WARN: 'warn',\n\n  /**\n  * Allows modifiers not wrapped with [] or {}.\n  */\n  ALLOW: 'allow',\n\n  /**\n  * Modifiers not wrapped with [] or {} are not parsed and are joined at the end of\n  * the generated string.\n  */\n  PASS_THROUGH: 'passThrough',\n};\n```\n\n```js\nexport const StylesPolicy = {\n\n  /**\n  * Prints warning for missing class name definitios in style objects.\n  */\n  WARN: 'warn',\n\n  /**\n  * Ignores class names not defined in style objects.\n  */\n  IGNORE: 'ignore',\n};\n```\n\n### Sample configurations\n\n#### emulate classNames like behaviour\n\n```js\nimport { customBemNames } from 'bem-names';\n\nconst cn = (...args) =\u003e customBemNames({ bemLike: false }, ...args);\n\ncn(['block', 'element'], { mod1: false }, ['mod2'], 'mod3');\n// 'block element mod2 mod3'\n```\n\n#### BEM with regular classes\n\n```js\nimport { bemNamesFactory, StringModifiers } from 'bem-names';\n\nconst config = {\n  stringModifiers: StringModifiers.PASS_THROUGH,\n};\n\nconst bem = bemNamesFactory('block', config);\n\nbem('element', { mod1: true }, 'mod3');\n// 'block__element block__element--mod1 mod3'\nbem('element', 'mod3');\n// 'block__element mod3'\nbem('hmmm');\n// 'block__hmmm'\n```\n\n#### BEM with states\n\n```js\nimport { bemNamesFactory } from 'bem-names';\n\nconst config = {\n  states = { disabled: 'is-disable', values: 'has-values' },\n};\n\nconst bem = bemNamesFactory('block', config);\n\nbem({ disabled: true, mod: true });\n// 'block is-disabled block--mod'\n```\n\n#### BEM with keyValues\n\n```js\nimport { bemNamesFactory } from 'bem-names';\n\nconst config = {\n  keyValue = true,\n};\n\nconst bem = bemNamesFactory('block', config);\n\nbem({ disabled: true, mod: false, key: 'value' });\n// 'block block--disabled block--key-value'\n```\n\n####  css-modules\n\n```js\nimport { bemNamesFactory } from 'bem-names';\n\nconst config = {\n  styles: { block: '123', 'block--disabled': 234 },\n};\n\nconst bem = bemNamesFactory('block', config);\n\ncn('block', { disabled: true, mod: false });\n// '123 234'\n\ncn('block', { disabled: true, key: 'value' });\n// '123 234'\n// console: 'Key \"key\" is missing in styles'\n```\n\n[classnames]: https://www.npmjs.com/package/classnames\n[bemNames-react-select]: https://github.com/Monar/react-select/commit/0f8983d02b26754a5bf1d45d8ea298500c663ecd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonar%2Fbem-names","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonar%2Fbem-names","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonar%2Fbem-names/lists"}