Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yarastqt opened this issue Mar 12, 2020 · 5 comments
Open

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

yarastqt opened this issue Mar 12, 2020 · 5 comments

Comments

@yarastqt
Copy link
Member

yarastqt commented Mar 12, 2020

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

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

// 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 функции.

@yarastqt yarastqt added the RFC label Mar 12, 2020
@yarastqt
Copy link
Member Author

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

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

@yarastqt
Copy link
Member Author

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

@yarastqt
Copy link
Member Author

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

export const [cn, merge] = createCn('Button', styles)

@yarastqt
Copy link
Member Author

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

@stenin-nikita
Copy link
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
Projects
None yet
Development

No branches or pull requests

2 participants