@SolidMike

Когда использовать микс, а когда модификатор?

Здравствуйте.
Использую бэм уже продолжительное время, но до сих пор иногда оказываюсь просто в растерянности в некоторых ситуациях
Есть 2 кейса:

1. Первый кейс У нас есть блок .contacts, который используется в header, footer и на странице контакты

.contacts
    .contacts__item
        .contacts__link

Во всех 3-х случаях его элементы выглядят и позиционируются немного по разному. Для позиционирования я делаю микс

.contacts.header__contacts
    .contacts__item
        .contacts__link

Но встает вопрос, как стилизовать элементы:
Давать миксы header__item и header__link не вариант, т.к. в хэдере могут уже существовать эти классы вне контекста .contacts

1) Можно создать еще один пустой микс класс

.contacts.header__contacts.header-contacts
    .contacts__item.header-contacts__item
        .contacts__link.header-contacts__link

Раньше я поступал именно так. Но мне все больше не нравится такой подход. Во-первых сам класс выглядит так будто пытается выдать себя за два. Во-вторых мы создаем новый блок просто ради того, чтобы задать косметические изменения, что как мне кажется не верно.

2) Можно делать кучу модификаторов для каждого элемента в отдельности, но тогда страшно представить сколько в проекте будет модификаторов, большинство из которых будет использоваться только 1 раз.

3) Можно сделать модификатор для блока и стилизовать каскадом

.contacts.contacts--theme--header.header__contacts
    .contacts__item
        .contacts__link

.contacts--theme--header .contacts__link {
}


Это вроде как противоречит идеям бэм, но мне кажется наиболее удачным решением.

2. Второй кейс. У нас есть базовый блок карточки

.card
   .card__img
   .card__inner
      .card__title
      .card__content
   .card__btn


Допустим она используется в каталоге. Но так же на странице с портфолио, но выглядит немного иначе.
Создавать пустой микс класс .portfolio-card мне кажется плохой практикой, т.к. мы привязываемся к контексту. А что если эта модифицированная карточка будет еще на какой-то странице? Тогда само слово portfolio уже будет ни к селу ни к городу.
Микс portfolio__img, portfolio__inner и т.д. тоже не жизнеспособны, т.к. нам нужно менять элементы именно в контексте карточки.

Я пришел к тому, что лучше всего будет давать карточкам абстрактные модификаторы

.card.card--type--1
   .card__img
   .card__inner
      .card__title
      .card__content
   .card__btn

.card--type--1 .card__img{
}


И стилизовать каскадом. Это дает нам полную модульность и свободу. Мы можем использовать модифицированные карточки где угодно в проекте. Более того, можно комбинировать модифицированные карточки с модификаторами элементов как угодно, например:

.card.card--type--1
   .card__img.card__img--hover--green
   .card__inner.card__inner--color--red
      .card__title
      .card__content
   .card__btn

.card--type--1 .card__img{
}


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

Был бы очень рад услышать аргументированную критику и ваши мысли на этот счет.
  • Вопрос задан
  • 731 просмотр
Решения вопроса 2
Realetive
@Realetive
MODX Ambassador России, самозванный БЭМ-евангелист
Первое, что нужно научиться делать — находить повторяющиеся сущности (которые в последствии станут Блоками и Элементами). Вы уже пытаетесь сделать это, но пока получается слишком многословно, судя по количеству классов и это, очевидно, сбивает с толку.
Я уже рассказывал на последнем БЭМапе (https://youtu.be/d4dcGj8abv0) о том, как я ищу эти повторяющиеся закономерности, поэтому постараюсь кратко:
1) Собираем и группируем «данные»
Разберём «шапку» и «подвал». И там, и там есть логотип, какая-то навигация, контактная информация, ссылки на соц. сети, в шапке есть кнопка:
1.1) «Шапка»

{
  logo: {
    img: 'путь до картинки',
    text: '…',
  },
  contacts: [
    'телефон',
    'email',
    'график работы',
    'адрес',
    'соц. сети',
    'кнопка обратной связи',
  ],
  navigation: [
    'пункт меню…',
    'пункт меню…',
    'пункт меню…',
  ]
}


1.2) В «подвале» почти то же самое, что-то в другом порядке, но это неважно, и нет «кнопки обратной связи» из секции «Контактов».

1.3) Есть ещё самостоятельные секции «Свяжитесь с нами» на главной странице и на странице контактов, которые тоже используют пункты из секции «Контактов» (теперь уже понятно, что такое частое повторение позволяет выделить это в блок).

1.4) Логотип и навигацию тоже выделим в блоки (я разделил логотип на два элемента — изображение и текст, потому что на малых экранах текст превращается в замыленое нечитаемое говно, поэтому его лучше убрать).

1.5) Получается какая-то такая структура:

// Блок логотипа
{
  logo: [
    { img: '…' },
    { text: '…' },
  ]
}

// Блок контактов
{
  contacts: [
    { tel: '…' },
    { email: '…' },
    { schedule: '…' },
    { address: '…' },
    { socials: [
      { vk: '…' },
      { telegram: '…' },
      { instagram: '…' },
    ] },
    { map: '…' },
    { feedback: '…' },
  ]
}

// Блок навигации
{
  navigation: [
    { item: '', link: '…' },
    { item: '', link: '…' },
    { item: '', link: '…' },
  ]
}

// Блок «Шапки»
{
  header: [
    {
      logo: [] // Тут всё без изменений
    },
    {
      contacts: [
        'tel',
        'email',
        'schedule',
        'address',
        'socials',
        'feedback',
      ]
    },
    {
      navigation: [] // Тут тоже без изменений
    }
  ]
}

// Блок «Подвала»
{
  footer: [
    {
      logo: [] // Тут всё без изменений
    },
    {
      navigation: [ // Тут почти без изменений
        // …
        'Марки автомобилей' // Без выпадающего списка
        // …
      ]
    },
    {
      contacts: [
        'tel',
        'email',
        'address',
        'socials',
        'schedule', // Без иконки
      ]
    },
  ]
}



2) Вроде, пока хватит. «БЭМизируем» эти данные.
Структура в формате JSON

// Блок логотипа
{
  block: 'logo'
  content: [
    { elem: 'img', src: '…' },
    { text: '…' },
  ]
}

// Блок контактов
{
  block: 'contacts'
  content: [
    { elem: 'tel', content: '…' },
    { elem: 'email', content: '…' },
    { elem: 'schedule', content: '…' },
    { elem: 'address', content: '…' },
    { elem: 'socials', content: [
      { elem: 'vk', content: '…' },
      { elem: 'telegram', content: '…' },
      { elem: 'instagram', content: '…' },
    ] },
    { elem: 'map', content: '…' },
    { elem: 'feedback', content: '…' },
  ]
}

// Блок навигации
{
  navigation: [
    { item: '', link: '…' },
    { item: '', link: '…' },
    { item: '', link: '…' },
  ]
}

// Блок «Шапки»
{
  header: [
    {
      block: 'logo'
    },
    {
      block: 'contacts',
      // У этого блока определённо свой контент,
      // будем отличать его от других вариантов
      // этого блока с помощью модификатора
      mods: { view: 'header' },
      content: [
        { elem: 'tel' },
        { elem: 'email' },
        { elem: 'schedule' },
        { elem: 'address' },
        { elem: 'socials' },
        { elem: 'feedback' },
      ]
    },
    {
      block: 'navigation',
      // Этот блок по внешнему виду отличается от блока в «Подвале»,
      // поэтому обозначим его модификатором
      mods: { view: 'header' },
      content: [
        // …
        // У одного пункта выпадающий список
        {
          elem: 'item',
          item: 'Марки автомобилей',
          elemMods: { dropdown: true },
          content: [
            // …
            { elem: 'subitem', item: 'Infinity', href: '…' }
          ]
        },
        // …
      ]
    }
  ]
}

// Блок «Подвала»
{
  footer: [
    {
      block: 'logo'
    },
    {
      block: 'navigation',
      mods: { view: 'footer' },
      content: [ // Тут почти без изменений
        // …
        { elem: 'Марки автомобилей', link: '…' }, // Без выпадающего списка
        // …
      ]
    },
    {
      contacts: [
        'tel',
        'email',
        'address',
        'socials',
        { elem: 'schedule', elemMods: { noIcon: true } }, // Без иконки
      ]
    },
  ]
}


2.1) Базовый лэйаут: https://codepen.io/Realetive/pen/PoGRjZo
2.2) Добавляем обёртки для группировки некоторых элементов: https://codepen.io/Realetive/pen/KKgoBdN
Про обёртки и зачем они нужны уже обсуждалось в https://ru.bem.info/forum/656/, очень советую.
2.3) Добавим отступы: https://codepen.io/Realetive/pen/bGwvjow
Как видите, миксы по-прежнему не нужны.
3) У контента есть фиксированная ширина, обозначим её через класс-элемент блока page: например, .page__layout:
https://codepen.io/Realetive/pen/WNGzgjJ
Но вёрстка закономерно сбилась, потому что классы, которые мы назначали для родительского уровня, «поднялись» ещё н один уровень. Добавим ещё обёртки, чтобы «выровнять» лэйаут: стили header перенесутся в header__body, из footer в footer__body: https://codepen.io/Realetive/pen/wvzmEpy
4) Получилось достаточно много «лишних» обёрток только лишь для позиционирования. Можно было бы этого избежать, если бы стили были описаны в одном классе. Но тогда мы лишимся пользы разделения логики благодаря БЭМ-неймингу. Вместо этого объединим эти слои с помощью миксов: page__header + header, header__body + page__layout, page__footer + footer, footer__body + page__layout: https://codepen.io/Realetive/pen/RwGMYyV

Вот наконец и пригодились миксы. Они лишь позволяют объединить две сущности по схожему признаку (например, лэйаут и позиционирование) на одном теге. А модификаторы обозначают различие между двумя одинаковыми сущностями (например, в одном месте у элемента есть иконка, в другом — нет, хотя это всё тот же элемент).

Блок с контактами делается аналогичным способом:
.page__section.page__section_view_feedback
  .page__layout
    .form.form_view_feedback.page__column_width_half (миксуем блок формы обратной связи и лэйаут 1/2 колонки)
      …
    .contacts_view_feedback.page__column_width_half
      .contacts__info
        .contacts__address
        .contacts__email
        .contacts__tel
        .contacts__schedule
      .contacts__map
Ответ написан
Get-Web
@Get-Web Куратор тега CSS
Front-End Developer
В целом вы правильно мыслите, но пришли немного к неправильному выводу.

- Миксы лучше использовать для позиционирования блока либо модификации блока в контексте другого блока. Например у нас есть contacts и помещая его в header, даже если ничего не менять, желательно сразу задать ему header__contacts потом может пригодиться.

- Если необходимо менять элементы блока внутри контекста другого блока, блока то можно миксовать элементы. Например: contacts__img header__contacts-img немного тавтология, но в небольших порциях уместно

- Но если модификации могут быть неоднократными, то нужно модифицировать сами элементы: contacts__img contacts__img_type_1, тут уже нужно смотреть на контекст проблемы, например если у вас иконка и она где-то слева и где-то справа, то можно задать ей правила с отступами слева и с права в независимости от того какой тип блока: contacts__ico_margin-right_small или contacts__ico_margin-left_large. Это модификатор КЛЮЧ-ЗНАЧЕНИЕ где в ключе мы указываем что у нас существует отступ _margin-left и такой отступ может быть только один, а значение, это размер отступа _large

Придерживаясь правил выше можно полностью решить практически любую задачу, но и такое иногда приходиться применять .card_type_1 .card__img, но обычно это хотелки заказчика/бекендщика, мол хочу чтобы я добавил класс и все изменилось, тут как бы выбора не остается.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы