Class utility to turn your insanely long bem class names into something a little more managable.
This is stupid simple, so don't over-complicate it!
This readme should cover most of the nuances and basics, but for more good reading, check out the litcoffee sources:
- source/bemmer.litcoffee
- source/class.litcoffee
- source/react/index.litcoffee
Also check out the Trello Board
$ cp ./bemmer.js /path/to/your/project/javascripts/
$ bower install bemmer
$ npm install bemmer-node
Note: Apparently bemmer
was already taken on the npm registry, so we have
sided with the name bemmer-node
until we either rename altogether or the
name becomes available. We were unaware of this project previously, and
only came across it when we went to register it with npm.
Bem classes are named using blocks, elements, modifiers. When creating classes, you can end up with a ton of classes for just one element in a block, which may look like this:
.my-block { display: 'inline'; border: 1px black solid; background-color: red; }
.my-block__my-element { font-size: 16pt; font-weight: bold; color: white; }
.my-block--active { background-color: white; }
.my-block__my-element--active { color: red; }
Obviously, the pain comes to implementing it in your HTML/javascripts:
<div class="my-block my-block--active">
<span class="my-block__my-element my-block__my-element--active">
Hello World!
</span>
</div>
In the Javascript world, creating elements with long class names becomes a little more unbearable:
myDiv = document.createElement('div')
myDiv.className = 'my-block my-block--active'
mySpan = document.createElement('span')
mySpan.className = 'my-block__my-element my-block__my-element--active'
myDiv.appendChild(mySpan)
Bem names can get very complicated very quickly, too. The above example deals with only one modifier, but any class could be expected to have any number of modifiers, and on top of that, you may have a block that has several elements inside which means you will be typing your block name (that is almost certainly longer than the string 'my-block') several times, and your element name at least that many, too.
Bemmer intends to take a bit of that pain out, using the Don't Repeat Yourself (DRY) principle. Your bem-related code will become more explicit, making it easier for other developers to get on board without a full understanding of bem.
These examples are written in coffeescript, but I have written them in a way that they more closely resemble plain old javascript. You can safely ignore any arrows ('->'), which just denote functions.
Since you'll be running this from inside the repository, the requires would have to be relative, which isn't what it would look like in production code. Instead, it's in a no-op block just so you can see how it looks.
bemmer = require('bemmer-node') # Export the wrapper function Bemmer = require('bemmer-node/bemmer-class') # Export the class
Bemmer = require('./bemmer-class')
console.log('%cBlue!', 'Basic Usage Example:')
bem = new Bemmer({
block: 'header',
element: 'title',
modifiers: {
'shaded': true
}
})
output = bem.classes()
Produces:
console.log('Expected Output: ', "header__title header__title--shaded")
console.log('Received Output: ', output)
Warning: this module hasn't been tested yet, so it may not even work at all
This works similar to the classes mixin in the React Addons package.
ReactBemmer = require('./react/react-bemmer')
console.log('%cBlue!', 'Bemifier with React and Coffeescript')
div = ReactBemmer.DOM.div
MyComponent = ReactBemmer.createComponent('my-component', {
render: ->
classes = @bemmer.with({
element: 'container'
modifiers: {
active: @props.active
}
})
return div(
{classNames: classes},
@props.children
)
})
ReactBemmer
will take care of setting the displayName
, and instantiating
@bemmer
on your class, which is accessible from any method. Bemmer will
assume your component name is also your block. If this is not the desired
behaviour, although I would highly suggest it means you are going against the
grain, you can override the block name in componentWillMount like this:
@bemmer = new Bemmer({block: 'whatever'})
Taking inspiration from bem.info, your bem objects will look like:
myFirstBemObject = {
block: 'what-a-blocky-mark',
element: 'the-fifth',
modifiers: {
'active': true
}
}
console.log('What does a BEM Object look like?', myFirstBemObject)
- Blocks don't receive modifiers - I don't know of a use case where this is required in CSS. To work around this, make a root element in your block, and modify that.
- mods renamed modifiers for elements - Being explicit is better than typing less! It makes the code more readable, which means more people can understand what it does with less effort.
- Blocks are inlined - bem.info's method for allowing blocks to be modified is to have both type and mods properties on the block, then nested in contents, you just add elements with modifiers. I think this is convoluted, and unnecessary, as addressed in my first point.
Add some custom css classes as the first parameter:
bem = new Bemmer({
block: 'my-custom-button',
modifiers: {
'large': true
},
classNames: 'btn btn-primary'
})
bemClasses = bem.classes()
Should produce something like this:
"btn btn-primary my-custom-button my-custom-button--large"
console.log('CSS class name concatenation with 3rd party classes:', bemClasses)
To run this in your console with node:
npm run readme
To learn more about BEM, check out some of these resources: