npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@textkernel/bem

v0.2.4

Published

Automatically generates list of classnames based on its props/state for your React component.

Downloads

1,366

Readme

Build Status Coverage Status Language grade: JavaScript Package dependencies npm bundle size Maintenance npm version

BEM

css modules + bem + react = love

Magically generates class names for React component.

Installation

npm install @textkernel/bem --save

or

yarn add @textkernel/bem

Usage

1. Create and export your own bem function using make.

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

// 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) => (
    <button
        {/* If needed, `props` should be spread before `block` or `elem in order to avoid className overwrite. */}
        {...props}
        {/* Spread `block` to add class names to the top level node */}
        { ...block(props) }
    >
        <span
            {/* Spread `elem` to add class names to an element. */}
            { ...elem('label', props) }
        >
            {props.children}
        </span>
        <span
            {/* Custom modifiers combined with props. */}
            {...elem('icon', {
                ...props,
                almostRandomValue: 42
            })}
        >
            {props.children}
        </span>
    </button>
);


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.

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.

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.

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

<Button />
 ↓ ↓ ↓
<button class="Button">
    <span class="Button__label" />
</button>

Prop active is set:

<Button active={true} />

    ↓ ↓ ↓

<button class="Button Button--active">
    <span class="Button__label Button__label--active" />
</button>

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).

<Button active={true} type='extraordinary' />

    ↓ ↓ ↓

<button class="Button Button--active Button--type Button--type_extraordinary">
    <span class="Button__label Button__label--active Button__label--type Button__label--type_extraordinary" />
</button>

Prop active equals false

No classnames will be produced if boolean property has false value.

<Button active={false} />

    ↓ ↓ ↓

<button class="Button">
    <span class="Button__label" />
</button>

Clicked state

<Button /> <!-- this.setState({ clicked: true }) -->

    ↓ ↓ ↓

<button class="Button Button--clicked">
    <span class="Button__label Button__label--clicked" />
</button>