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

react-native-bem

v0.3.0

Published

A BEM-inspired method to styling React Native components

Downloads

11

Readme

react-native-bem

A tiny, fast, BEM-inspired method to styling React Native components.

npm version Build Status

The BEM methodology and naming convention allows us to reduce the complexity of styling, and develop with speed and predictability. By following a similar approach to naming, modifiers and states (not to be confused with a component's internal state), we can create self-contained, easily styled components in any situation.

Table of Contents

Installation

npm install react-native-bem --save

Features

react-native-bem allows us to style (the Block and Child) elements of a component based on a kebab-cased name, with optional modifier and state attributes.

It exports three functions:

  • bem(): exported as the default function, which is used to apply styling to components.

    Its signature is bem (selector: string, props: Object, rules: Object). selector is the kebab-cased name of the component to style, props is the component's props and rules is the style object (from styles.js).

  • renderBemChild(): exported as a named function, this can be used in place of rendering {props.children} to pass a BEM selector to a specific child.

    Its signature is renderBemChild (props: Object, style: Array<any>, index: number = 0). props is the component's props, style is the result of calling bem() with a BEM Mix selector, and index is the index of the child element to style.

  • renderBemChildren(): exported as a named function, this can be used in place of rendering {props.children} to pass a BEM selector to all children.

    Its signature is renderBemChildren (props: Object, style: Array<any>) and is identical to renderBemChild() but without index.

Note: As of v0.2.0, react-native-bem now utilizes StyleSheets instead of plain JavaScript objects for improved performance.

Modifiers

As with BEM in CSS, a modifier is a flag on a block or element, which is used to change an element's (or multiple elements') appearance.

react-native-bem supports two types of modifiers with the M prefix:

  1. Boolean: The prop MalignCenter={true} uses the --align-center style definition.
  2. String: The prop Malign="right" uses the --align-right style definition.

States

While not a part of BEM per se, states are an elegant addition to signify that the piece of UI in question is currently styled a certain way because of specific condition.

Common stateful namespaces begin with is and has.

react-native-bem state props uses the S prefix, like so SisDisabled={true} which uses the .is-disabled style definition.

Usage

Creating Components

Unless a component requires internal state or life-cycle hooks, a component should use the Stateless Functional Component (SFC) pattern to increase performance. It is trivial to convert an SFC to as class-based component later on where necessary.

Component Architecture

Generally:

  • Each Component resides in a PascalCase directory under components that matches the BlockName.
  • index.js: contains the component.
  • styles.js: contains styles (if necessary).

However, if a Block requires multiple child components:

  • BlockName.js: contains the outermost block component.
  • BlockName__Element.js: contains a child component.
  • index.js: imports BlockName and all BlockName__Elements, and exports them as named exports.
  • styles.js: contains styles for BlockName and all BlockName__Elements.

Styling Components

styles.js

This file exports a default StyleSheet object which contains keys and style properties for each component.

A component with child components, modifiers and states may look like this:

import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    'component-name': {
        KEY: VALUE
    },
    'component-name--modifier': {
        KEY: VALUE_WHEN_MODIFIED
    },
    'component-name.state': {
        KEY: VALUE_WHEN_IN_THIS_STATE
    },
    'component-name__element': {
        KEY: VALUE_OF_ELEMENT
    },
    'component-name--modifier component-name__element': {
        KEY: VALUE_OF_ELEMENT_WHEN_MODIFIED
    },
    'component-name.state component-name__element': {
        KEY: VALUE_OF_ELEMENT_WHEN_IN_THIS_STATE
    }
});

Similar to using BEM in CSS, component styles are encapsulated within a namespace, and developers can clearly see the styles for all components, children and variations.

Common Usage

Component Definition

components/HelloWorld/index.js

import React from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import bem from 'react-native-bem';
import styles from './styles';

const HelloWorld = (props) => {
    const b = (selector) => bem(selector, props, styles);

    return (
        <View style={b('hello-world')}>
            <Text style={b('hello-world__text')}>
                Hello, world
            </Text>
        </View>
    );
};

HelloWorld.propTypes = {
    MbooleanModifier: PropTypes.bool,
    MstringModifier: PropTypes.string,
    SaStateName: PropTypes.bool
};

export default HelloWorld;
Styles Definition

components/HelloWorld/styles.js

import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    'hello-world': {
        backgroundColor: '#000',
        marginBottom: 5,
        padding: 5
    },
    'hello-world--boolean-modifier': {
        borderColor: '#f00',
        borderWidth: 4
    },
    'hello-world--string-modifier-foo': {
        padding: 20
    },
    'hello-world.a-state-name': {
        opacity: 0.5
    },
    'hello-world__text': {
        color: '#fff',
        fontSize: 20
    },
    'hello-world.a-state-name hello-world__text': {
        color: '#ff0'
    }
});

Usage within a View

View.js

import HelloWorld from './components/HelloWorld';

…

render() {
    return (
        <HelloWorld />
        <HelloWorld MbooleanModifier={true} />
        <HelloWorld MstringModifier="foo" />
        <HelloWorld SaStateName={true} />
        <HelloWorld MbooleanModifier={true} MstringModifier="foo" SaStateName={true} />
    );
}

which results in:

So, you can see that with adding and removing props, either at run-time or by default, you will be able to quickly adapt your components' appearance to reflect the current state of the application.

Advanced Usage

Manipulating Children

There may be times where you wish to pass in child components from within your view, like so:

render() {
    return (
        <HelloWorld>
            <HelloWorld__Text />
        </HelloWorld>
        <HelloWorld MbooleanModifier={true}>
            <HelloWorld__Text />
        </HelloWorld>
        <HelloWorld MstringModifier="foo">
            <HelloWorld__Text />
        </HelloWorld>
        <HelloWorld SaStateName={true}>
            <HelloWorld__Text />
        </HelloWorld>
        <HelloWorld MbooleanModifier={true} MstringModifier="foo" SaStateName={true}>
            <HelloWorld__Text />
        </HelloWorld>
    );
}

Out of the box, the children wouldn't be affected by parent modifier and state props (meaning the text color would always be white in the above example). however, by changing the HelloWorld component to accept children enhanced with BEM like so:

import bem, { renderBemChild } from 'react-native-bem';

const HelloWorld = (props) => {
    const b = (selector) => bem(selector, props, styles);

    return (
        <View style={b('hello-world')}>
            {renderBemChild(props)}
        </View>
    );
};

where HelloWorld__Text is:

import React from 'react';
import { Text } from 'react-native';
import bem from 'react-native-bem';
import styles from './styles';

const HelloWorld__Text = (props) => {
    const b = (selector) => bem(selector, props, styles);

    return (
        <Text style={b('hello-world__text')}>
            Hello, world
        </Text>
    );
};

export default HelloWorld__Text;

we get the exact same output as before, just with the added flexibility of being able to control which child components are rendered from within the view.

As mentioned earlier, renderBemChild() also accepts a second parameter of a styles object (like those returned from a bem() selector) and an index.

This allows us to render children with additional styles applied, which can be useful when a component is consumed by another (known as a BEM Mix), and its appearance needs to change to reflect that.

If we update our existing files to this:

styles.js

import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    … existing styles
    'bem-mix__text': {
        color: '#0ff'
    }
});

HelloWorld.js

const HelloWorld = (props) => {
    const b = (selector) => bem(selector, props, styles);

    return (
        <View style={b('hello-world')}>
            {renderBemChild(props, b('bem-mix__text'))}
        </View>
    );
};

We can see that the additional styles were applied to the HelloWorld__Text components, turning them cyan.

Using a Component's Internal State

Some components, such as a <TextInput />, need to be styled differently based on an internal state, like when it has focus. Instead of cumbersomely alerting parent components to whether it's focused or not and changing a state prop in the view, we can merge in the component's state to bem() at render() time.

With the following component:

components/TextBox/index.js

import React, { Component } from 'react';
import { TextInput } from 'react-native';
import bem from 'react-native-bem';
import styles from './styles';

class TextBox extends Component {
    constructor () {
        super();

        this.state = {
            SisFocused: false
        };
    }

    _onBlur = () => {
        this.setState({
            SisFocused: false
        });
    }

    _onFocus = () => {
        this.setState({
            SisFocused: true
        });
    }

    b = (selector) => bem(selector, { ...this.props, ...this.state }, styles)

    render () {
        return (
            <TextInput
                {...this.props}
                style={this.b('text-box')}
                onBlur={this._onBlur}
                onFocus={this._onFocus}
            />
        );
    }
}

export default TextBox;

and its related styles.js

import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    'text-box': {
        backgroundColor: '#fff',
        borderColor: '#000',
        borderWidth: 2,
        color: '#333',
        fontSize: 14,
        height: 40,
        paddingHorizontal: 10,
        paddingVertical: 0
    },
    'text-box.is-focused': {
        borderColor: '#00f'
    }
});

Because the TextBox component's b() helper merges the component's props with its internal state (which has a correctly named SisFocused property), it apply styles for when the TextInput is focused!


Copyright (c) 2018 Matt Stow
Licensed under the MIT license (see LICENSE for details)