Skip to content

Latest commit

 

History

History
199 lines (138 loc) · 5.33 KB

README.md

File metadata and controls

199 lines (138 loc) · 5.33 KB

Bera

Build Status

A simple JavaScript utility for generating classnames following the ABEM principles.

Usage:

import abem from "bera";

abem("button", "label", ["visible", "active"]);
// => 'button__label -visible -active'

The library can also be used directly on the page just including the index.js in a standalone <script> tag; or by RequireJS.

About

Bera is a variant of bero, a utility created by Matteo Ferretti. What bero is to BEM, bera is to ABEM: It follows the same non-invasive approach inspired by classnames to generate classnames for the ABEM naming convention.

Why "bera"?

In line with Matteo's inspiration for naming bero, it's a character from Yōkai Ningen Bem (妖怪人間ベム Yōkai Ningen Bemu, translated officially as Humanoid Monster Bem).

Usage

The abem function is a curried function, takes up to three arguments.

The simplest usage is the basic signature with two arguments, identifier and modifiers:

abem(identifier: String, modifiers: Array|Object) : String

identifier: String

The identifier can be either a block, or an element.

If an element is specified, the full identifier has to be written, in the form of block__elem. For example:

abem("button__label", ["visible", "active"]);
// => 'button__label  -visible -active'

Since abem is a curried function, it also possible write the code above as:

const label = abem("button__label");

label(["visible", "active"]);
// => 'button__label -visible -active'

This form would be rarely used for elements; it's more common having block functions, when the signature with three arguments is used:

abem(block: String, elem: String, modifiers: Array|Object) : String

In this form, the equivalent of the code above would be:

abem("button", "label", ["visible", "active"]);
// => 'button__label -visible -active'

But it would be more common used as curried function for block functions:

const button = abem("button");

button("label", ["visible", "active"]);
// => 'button__label -visible -active'

This form is useful especially in components, where there is likely only one block per component, but multiple elements as children of that block.

modifiers: Array|Object

The modifiers arguments can be either an Array or an Object. The logic is the same of @JedWatson's classnames module.

If it's an Array, every element that is considered truthy, would be added as modifier in the resulting classname:

abem("button__label", [false, "visible", 0, , "", undefined, "active"]);
// =>  button__label -visible -active'

However, modifiers really shines when an Object is given:

abem("button__label", {
  visible: isVisible,
  active: isActive
});
// with `isVisible`: true, `isActive`: true
// => button__label -visible -active'

// with `isVisible`: true, `isActive`: false
// => button__label -visible'

// with `isVisible`: false, `isActive`: true
// => button__label -active'

// with `isVisible`: false, `isActive`: false
// => button__label

With computed property names you can also have modifiers as such:

abem("button__label", {
  [`text-${color}`]: !!color
});
// with `color`: undefined:
// => button__label

// with `color`: "red"
// => button__label -textRed

camelCase modifiers

All modifiers are automatically converted to camel case:

abem("button__label", {
  'has-focus': true
});
// with `has-focus`: true
// => button__label -hasFocus

// with "text-red": red
abem("button__label", ["ColorRed"]);
// => button__label -colorRed

Likely cases of abbreviation will be retained:

abem("button__label", ["DOMLoaded"]);
// => button__label -DOMLoaded

Any number of hyphens at the beginning of the string will be removed:

abem("button__label", ["-foo", "--bar", "---baz"]);
// => button__label -foo -bar -baz

And any numbers of hyphen inside the string will be removed and the first letter of the suffix properly cased:

abem("button__label", ["foo----bar"]);
// => button__label -fooBar

So that even in those edge cases the ABEM naming convention is kept.

The join function

bera comes with an utility function that helps to concatenate several truthy values in one string. That's useful when the generated ABEM classname needs to be concatenate by external strings, such a className passed by props in React. See below for a real-world example.

Usage with React Component.

import abem, { join } from "bero";

const button = abem("button");

export default class Button {
  // ...
  render() {
    const { pressed, hover } = this.state;
    const { className, label, onClick } = this.props;

    return (
      <button
        className={join(button({ pressed, hover }), className)}
        onClick={onClick}
      >
        <label className={button("label", ["strong"])}>{label}</label>
      </button>
    );
  }
}

License

MIT. Copyright (c) 2018 Nuey San Waldman