Skip to content

WonderNetwork/react-bem

Repository files navigation

React BEM template functions

Installation

npm install @puck/react-bem

Usage

Note

Learn how to inject the bem prop in the next chapter

Simplest way to create a block with some elements

function Acme({ bem: { className, element } }) {
  return <div className={className}>
    <h1 class={element`heading`}>Hello</h1>
  </div>
}
<div class="acme">
  <h1 class="acme__heading">Hello</h1>
</div>

BEM helper as a shorthand if there are no elements

function Acme({ bem }) {
  return <div className={bem}>Hello</div>
}
<div class="acme">Hello</div>

Adding block modifiers

function Acme({ bem: { block } }) {
  const [toggle, setToggle] = useState(true);
  const onClick = useCallback(
      () => setToggle(current => !current),
      [setToggle],
  );

  return <div className={block`${{ toggle }} always-enabled`}>
    <button onClick={onClick}>Toggle</button>
  </div>
}
<div class="acme acme--toggle acme--always-enabled"/>

Mixing the block with other classes

function Acme({ bem: { block } }) {
  return <div className={mix`me-2 d-flex`}>
  </div>
}
<div class="acme me-2 d-flex">...</div>

Mixing with parent block

To mix a block with a parent element just pass the element name as className and it will be appended automatically

function Child({ bem: { block } }) {
  return <div className={block`${{ active: true }}`.mix`me-2`}/> 
}

function Parent({ bem: { className, element } }) {
  return <div className={className}>
    <Child className={element`element`}/>
  </div>
}
<div class="parent">
  <div class="child parent__element child--active me2"/>
</div>

Using elements with modifiers

function Acme({ bem: { block, element } }) {
  return <div className={block}>
    <div class={element`item ${{ selected: true }} me-2`} />
    <div class={element`item ${{ variant: 'primary' }}`} />
    <div class={element`item ${['theme-dark']}`} />
    <div class={element`item`.mix`d-flex`} />
  </div>
}
<div class="acme">
  <div class="acme__item acme__item--selected me-2" />
  <div class="acme__item acme__item--variant-primary" />
  <div class="acme__item acme__item--theme-dark" />
  <div class="acme__item d-flex" />
</div>

Adding the BEM helper to your components

Let’s assume you have a component you’d like to use BEM with:

type Props = {
    title: string;
}

export default function AcmeBanner({ title }: Props) {
    return <div>
        Hello {title}
    </div>;
}

BEM is a naming strategy, so let’s reuse the same name between your React component display name and your CSS block name. The CSS name will be converted to snake-case.

  1. Import the withBem Higher Order Component
  2. Add a displayName to your component
  3. (Typescript): Wrap your prop types with withBem.props<> type
  4. Wrap your export in a withBem() HOC
import { withBem } from '@puck/react-bem'

type Props = {
    title: string;
}

function AcmeBanner({ bem: { className }, title }: withBem.props<Props>) {
    return <div className={className}>
        Hello {title}
    </div>;
}
AcmeBanner.displayName = 'AcmeBanner';
export default withBem(AcmeBanner);

Optionally, you might pass the component name explicitly using withBem.named():

export default withBem.named('AcmeComponent', ({ bem, title }) => {
    return <div className={bem}>
        Hello {title}
    </div>;
});