Skip to content

@bem-react/classname: Support css-modules [RFC] #545

Open

Description

@yarastqt

Пример использования

Подключаем хелпер и передаем в него название компонента а так же объект со стилями:

// src/components/Button/Button.js
import React from 'react'
import { createCn } from '@bem-react/classname'
import styles from './Button.module.css'

export const { merge, cn } = createCn('Button', styles)

export const Button = ({ children }) => {
  return (
    <button className={cn()}>
      <span className={cn('Text')}>{children}</span>
    </button>
  )
}

Далее на уровне проекта или эксперимента мы можем переопределить нужные нам стили:

// src/experiments/Button/Button.js
import { merge, cn, Button } from '../../components/Button/Button'
import styles from './Button.module.css'

merge(styles)

export { merge, cn, Button }

Пример реализации

Используем фабрику для генерации классов, в замыкании храним доопределения для компонента и его элементов (в данной реализации не поддержаны модификаторы и миксы):

export function createCn(component, styles) {
  const styleOverrides = []
  return {
    merge(overrides) {
      styleOverrides.push(overrides)
    },
    cn(element) {
      const target = element ? `${component}-${element}` : component
      const className = [styles]
        .concat(styleOverrides)
        .map(shape => shape[target])
        .join(' ')
      return className
    }
  };
}

В таком кейсе нам не нужно будет докидывать микс на элементы снаружи, т.к. вся логика будет инкапсулирована внутри merge функции.

Activity

  1. yarastqt commented on Mar 12, 2020

    @yarastqt
    MemberAuthor

    Возможно стоит делать это как новую фабрику, а не патчить существующую, в таком случае можно будет не нести эту логику в бандл, если она не нужна, т.к. будет три-шейкинг.

    Нужно ещё продумать различные кейсы использования, возможно что-то не учли.

  2. yarastqt commented on Mar 12, 2020

    @yarastqt
    MemberAuthor

    После реализации можно будет постепенно компоненты переводить на css-модули.

  3. yarastqt commented on Mar 13, 2020

    @yarastqt
    MemberAuthor

    Для createCn можно использовать тапл, чтобы для каждого компонента можно было назвать cn уникально.

    export const [cn, merge] = createCn('Button', styles)
  4. yarastqt commented on Mar 13, 2020

    @yarastqt
    MemberAuthor

    Так же нужно подумать про то, как это будет работать с платформами (desktop, touch) при SSR, не будет ли проблем с тем, что у нас общая память.

  5. stenin-nikita commented on Apr 14, 2020

    @stenin-nikita
    Contributor

    Глобально мержить может быть не совсем хорошая идея - стили могут начать применяться там, где не нужно. Я поэкспериментировал и получилось примерно следующее
    Из плюсов здесь то, что такой подход может позволить гибко переопределять библиотечные стили. А библиотеки компонентов можно будет писать вообще без стилей, а при использовании можно будет прокидывать стили с разными темами и т.д.

    В данном случае, в @bem-react/classname достаточно будет реализовать фабрику, которая принимает на входе мапу с классами:

    const Button: FC<ButtonProps> = (props) => {
      const { className, styles, size, disabled, ...other } = props;
      const cn = useMemo(() => createCn('Block', props.styles), [props.styles]);
    
      return <button className={cn({ styles, disabled }, [className])}  {...other} />
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions