CSS-архитектура Блок-Элемент-Модификатор (БЭМ)


Содержание материала:

Frontender Magazine

Благодаря компании Moove-it пару дней назад у нас была возможность встретиться с Гарри Робертсом (Harry Roberts) — опытным фронтенд-разработчиком. Гарри вёл курсы для компаний вроде Google, Etsy и BBC. Во время нашего двухдневного воркшопа фокус делался на том, чтобы улучшить навыки в области фронтенд-разработки, но на практике мы получили намного больше, чем просто технические знания и умения.

Писать CSS очень просто. Выучить язык — не проблема. Написание трёх строчек CSS для оформления DOM-элемента займет не больше двух минут, но давайте смотреть шире: что если приложение вырастет больше, чем ожидалось в начале работы над проектом? Легко ли поддерживать его код? Что нужно учитывать, чтобы писать более расширяемый и поддерживаемый CSS?

Давайте рассмотрим два технических момента, о которых мы узнали во время воркшопа.

БЭМ. Блок, Элемент, Модификатор

Когда мы разрабатываем стандартный сайт, процесс разработки основывается на предположении, что дизайн и технические характеристики сайта в процессе не изменятся. Разработчик и дизайнер работают практически не взаимодействуя друг с другом. Обычно мы предполагаем, что дизайн будет готов до того, как подключится разработчик.

Такой подход предполагает следующий процесс:

  • сначала дизайнер готовит серию макетов, визуализирующих дизайн сайта;
  • затем на их основе создаются статические страницы;
  • и наконец, программист оживляет сайт, подключая бэкенд и добавляя JS-логику;

Этот подход хорошо работает, если предположить, что все перечисленные шаги соответствуют истине. Однако так достаточно редко происходит на практике — обычно сайт начинает развиваться и жить своей жизнью. Дизайн постепенно изменяется, добавляются новые страницы и разделы.

Одна из наиболее важных особенностей использования БЭМ состоит в том, что он гарантирует, что все разработчики сайта работают с одной и той же базой кода и используют одну и ту же терминологию. Если потребуется, дизайн сайта может быть изменён в любой момент — без проблем и конфликтов.

Принципы БЭМ

Если говорить о методологиях в программировании, одна из наиболее широко распространенных — объектно-ориентированное программирование (ООП). Мы все отлично с ним знакомы, и БЭМ определенным образом схож с ООП. БЭМ — способ выражения реальности с помощью кода, некоторого набора шаблонов, а также способ думать о сущностях в рамках программы вне зависимости от используемого языка программирования.

Компоненты БЭМ

  • Блок: блок — это независимая сущность, «строительный блок» приложения. Он может быть простым или составным (содержащим другие блоки). Например, блоком может быть форма поиска.
  • Элемент: элемент — это часть блока, которая выполняет некие функции. Элементы контекстно-зависимы: они имеют смысл только в контексте блока, который их содержит. Например, элемент — это поле ввода или кнопка в форме поиска.
  • Модификатор: если мы хотим добавить элементу конкретный атрибут, нам нужно использовать модификаторы. Например, если мы хотим добавить две поисковые формы, но одну с большей кнопкой, мы можем добавить кнопке модификатор -big .

В данном случае получится что-то вроде:

И CSS вроде такого:

Как видите, этот подход дает нам возможность изменить блок, удалить его, добавить новые компоненты — и всё это без непредвиденных изменений в приложении. Код будет удобно поддерживать, легко изучать, в конце концов, он будет просто потрясающим.

ITCSS. Разумная, масштабируемая, управляемая CSS-архитектура от Гарри Робертса

Официальный сайт Гарри имеет весьма говорящий заголовок: CSS Wizardry (CSS-колдовство). Воркшоп заставил нас задуматься о наших приложениях и обратить внимание на то, как важно построить свою собственную CSS-архитектуру. Обычно мы создаем большие и сложные приложения, которые изменяются с течением времени. Формирование четко определенной архитектуры просто неизбежно, тем более что это позволяет писать код лучше и быстрее.

Управление CSS при масштабировании приложения — непростая задача, но иногда мы усложняем её больше необходимого. Обратный треугольник CSS (Inverted Triangle CSS) — простая, эффективная и пока слабо освещённая методология, которая помогает управлять, поддерживать и масштабировать CSS в проектах любого размера. ITCSS — это не библиотека, это образ мышления, метафреймворк, если хотите. Он помогает справляться с проблемами, которые возникают при масштабировании CSS, и сохранить здравый рассудок. ITCSS основывается на следующем утверждении: «каждый блок в CSS должен представлять что идет до него и что может быть после». CSS — это огромное дерево зависимостей, и нам нужно управлять ими на очень низком уровне.

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

Прочее…

Последнее, но не менее важное: давайте поговорим о Гарри. Мы встретили не только CSS-эксперта, но и просто отличного парня. Можно многое сказать о воркшопе, ITCSS-архитектуре, БЭМ и отличных технических приемах, но это ещё не всё. Воркшоп дал нам возможность встретить человека, который работал со всемирно известными компаниями, и мы смогли многое почерпнуть из его опыта.

Кроме того, мы отправились пить пиво и говорить о культуре, спорте, технологиях и тому подобных вещах с человеком, которого встретили за день до этого. Это был отличный опыт, который дал нам огромное количество технических знаний и возможность обрести нового друга.

Простые правила разметки

Как писать классы по БЭМ?

БЭМ расшифровывается как «Блок Элемент Модификатор». На самом деле, это целый стэк технологий, из которого мы воспользуемся только соглашением по именованию классов.

  • БЭМ позволяет создавать абсолютно независимые блоки. Блоки и элементы получают уникальные имена, так что стили для одного элемента ничего не поломают в другом.
  • БЭМ помогает легко придумывать любое количество классов, не повторяющихся между собой.
  • БЭМ помогает писать самодокументирующийся код, в классе любого элемента содержится информация о нём.

Подробнее можно почитать в разделах Быстрый старт и Часто задаваемые вопросы на сайте bem.info.

Ниже показаны примеры кода.

Простой пример: Блок + Элемент #

Допустим, у вас есть блок с заголовком, текстом и кнопкой внутри, например, это всплывающее окно — попап. Разметка:

Добавляем класс содержащий назначение элемента: .popup :

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

Классы удобные, но не уникальные. Если на странице будут ещё элементы с классами .title и .text , их стили могут затронуть элементы в попапе. Селектор типа .popup .title может в будущем создать проблемы со специфичностью. Можно придумать другие классы, но чем больше похожих по смыслу элементов, тем сложнее придумывать новые классы.

А теперь применим БЭМ-нотацию: каждому элементу внутри блока добавим префикс с классом родителя, например, для заголовка это будет popup__title :

Теперь эти классы легко решают сразу две задачи: во-первых, благодаря уникальным классам стили для них никогда не пересекутся с другими подобными элементами на странице, а во-вторых, по таким классам сразу видно, что это элементы блока .popup .

Пример посложнее: Блок + Элемент + Модификатор #

Для примера возьмём сервисное сообщение на сайте. Обычно такие сообщения бывают разных видов, например, сообщение об успешном завершении действия или об ошибке.

Логично использовать одну и ту же разметку, но с разными цветовыми темами. Именно здесь очень пригодятся модификаторы.

Обоим элементам можно добавить одинаковые стили используя общий класс .message и так же легко можно добавить отдельные стили для каждого из них, используя уникальный класс с модификатором:

Оба сообщения будут иметь рамку толщиной один пиксель, но для сообщения об успешной операции она будет зелёной, а для сообщения об ошибке — красной.

Ещё сложнее: что делать, если хочется сделать элемент элемента? #

Например, на странице есть блок новостей:

Заголовок блока логично получает класс .news__title , список — .news__list , а отдельная новость — .news__item :

Тут никаких проблем возникнуть не должно. Теперь добавим разметку отдельной новости:

Нам нужно добавить класс заголовку новости. Первым делом приходит в голову .news__title , но такой класс уже занят. Предположим, что второй элемент будет не .title , а .subject , тогда в CSS получается такое:

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

Следующий вариант — .news__item__title , но в БЭМ нельзя создавать элемент элемента, и это понятно, потому что получается каша. Ещё вариант: .news__item-title — тоже не годится, потому что может быть неочевидным как title соотносится с item . Как же быть?

Решение простое: на уровне элемента .news__item можно объявить новый блок (например, .news-item ), и строить вложенные классы уже от него. Да, это не самостоятельный переиспользуемый блок, здесь объявление блока нужно только для того, чтобы разгрузить селекторы. Что получается:

Проблема решена: нам больше не нужно использовать монструозные классы, при этом класс точно описывает элемент, и в CSS будет сразу понятно какой класс за что отвечает:

Простой и удобный выход из неудобной ситуации.

Больше примеров разметки можно увидеть здесь.

Ещё одно хорошее руководство по использованию БЭМ есть здесь.

БЭМ-методология организации CSS-кода

Писать CSS-код легко. Масшабировать и поддерживать его — нет

Это правда. И это неоднократно подтверждалось во многих проектах. Будь то конструктор сайтов с настраиваемыми темами (проект Getsocio — 28 тысяч строк CSS) кода или сайт-визитка со сравнительно небольшим количеством стилей. Любые сколь-нибудь сложные правки в связи с изменениями дизайна или с появлением новых страниц приводят к долгому рефакторингу, в самом запущенном случае — к дублированию стилей. При этом постоянно присутствует риск что-нибудь сломать в самом неожиданном месте.

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

Почему так происходит? На начальном этапе, когда разработчики получают макеты, они создают структурy ассетов, в которую довольно хорошо вписывается весь существующий дизайн. Проблема рождается именно в этот момент, потому что текущий дизайн — это вершина айсберга по сравнению с будущим дизайном. Можно представить варианты решений, приходящих в голову на данном этапе. У нас всего две страницы — можно поместить все стили в один файл. Или еще. У нас десять страниц и все они непохожи, поэтому не стоит заморачиваться выделением компонент внутри страниц и вынесением их в отдельные файлы, проще создать по файлу стилей на каждую страницу. Идеи выглядят вполне здравыми, учитывая текущие аспекты дизайна. Опытные разработчики даже могут сделать некоторые предположения о том, как поменяется дизайн в ближайшем будущем, но немногие готовы к тому, во что превратится проект через, скажем, пять лет.

CSS имеет ряд недостатков, которые приводят к вышеперечисленным проблемам. В недавнем докладе (React: CSS in JS), породившем множество дискуссий в среде фронтенд разработчиков, один из сотрудников Facebook озвучил проблемы с масштабированием CSS. Среди них использование глобального пространства имен, удаление мертвого кода, изоляция и т. д. В итоге он предложил хранить стили в JavaScript. Интересное, но довольно радикальное решение, не всегда применимое к обычным сайтам, страницы которых рендерятся на сервере. Многие компании, не только Facebook, пытаются решить проблему масштабирования CSS. Поэтому на сегодняшний день существует множество подходов к написанию стилей. Одна из наиболее интересных методологий родилась в Yandex.

БЭМ (Блок Элемент Модификатор) — методология, которая предоставляет решение по созданию архитектуры проекта. Это комплексное решение, которое диктует не только структуру CSS, но и структуру шаблонов и скриптов. Богатый инструментарий для автоматической генерации кода, созданный разработчиками Yandex, также помогает организовать процесс разработки. Нам сейчас интресна та часть, которая касается стилей. Благо методология довольно гибкая и ее разработчики поощряют пользователей делать эксперименты и брать только те ее части, которые помогут в разработке их проектов.

Пройдемся немного по понятиям. Блок — независимый компонент страницы, который инкапсулирует внутри себя поведение (JavaScript) и внешний вид (CSS). Благодаря независимости блока возможно его повторное использование в любом месте страницы. Элемент — составная часть блока, которая не может использоваться в отрыве от него. Модификатор — сущность, изменяющая внешний вид блока или элемента в зависимости от состояния или требований дизайна. При желании можно использовать несколько блоков на одном HTML элементе. Такой способ в терминологии БЭМ имеет название Микс.

Теперь можно вернуться к нашим проблемам и посмотреть какое решение нам предлагает БЭМ. Первая проблема — это глобальное пространство имен. Пусть у нас есть навигационное меню.

Если вдруг на странице появится другой компонент, содержащий элемент списка ( item ), или некий элемент связанный с редактирванием ( edit ), то новые стили повлияют на уже существующие.

В этом примере проблема решается выделением двух компонент. toolbar и user-profile — имена этих компонент в глобальном пространстве имен. Дальше мы определяем стили внутренних элементов в пределах этих компонент.

Но такое решение очень плохо масштабируется. Если представить себе более сложные компоненты ( page , company-preview , user-post ), то мы получим ту же самую проблему, но уже в пределах одного компонента. Уточнение селекторов, например .page > .edit — очень плохая идея, так как создает связанность между HTML шаблонами и представлением. Меняя шаблон, нам нужно будет поменять и стили тоже. Вдобавок ко всему добавляя класс к селектору мы меняем его специфичность, а это в свою очередь усложняет переопределение CSS правил для элемента. В других местах появляются классы для увеличения специфичности, начинается гонка селекторов и головная боль для верстальщика.

БЭМ предлагает отказаться от каскадности, тем самым однозначно определяя элемент.

Вложенные элементы, принадлежащие блоку, будут использовать это имя блока toolbar в качестве префикса. __ служит разделителем между блоком и элементом, а _ — разделителем между БЭМ-сущностью (в данном случае элементом) и модификатором. Здесь toolbar__item_edit и toolbar__item_edit — это модификаторы элемента toolbar__item .

Незаметно для себя мы одним махом решили целый ряд проблем. Элементы теперь инкапсулированы внутри блоков и изолированы от других элементов. Для описания стилей блока и его элементов можно выделить отдельный файл или даже каталог на файловой системе. Таким образом, только лишь взглянув на структуру проекта, можно сказать что находится в глобальном пространстве. Становится легче отслеживать иерархические связи в пределах блока. CSS код становится самодокументируемым. Упрощается поиск селекторов по проекту, а также модификация стилей и удаление неиспользуемого кода.

Осталось поговорить о самодисциплине. Чтобы не свести на нет полученные приимущества нужно следовать некоторым правилам и рекоммендациям. Можно держать в голове следующий чеклист.

  • Позиционирование блока задается родителем
  • Для описания сущностей используются классы, но не id
  • Нельзя создавать элементы элементов ( block__elem1__elem2 )
  • В именах модификаторов и элементов всегда присутствует имя блока
  • Из предыдущего пункта следует, что нельзя создавать глобальные модификаторы
  • Блоки могут не содержать вложенных элементов ( link )
  • Блоки могут заключать в себе все содержимое страницы или крупные ее части ( page , page-section )

Чтобы не утратить возможность переносимости блока между разными частями страницы желательно чтобы позиционирование и размеры блока ( margin , top , left , width ) задавались родителем.

Предположим что наш toolbar находится внутри блока header и должен занимать его правую половину. Решение может выглядеть следующим образом.

CSS код блока toolbar при этом остается неизменным.

Еще один непраздный вопрос. В каком случае создавать блок, в каком — элемент? Если фрагмент кода не зависит от других компонент страницы, то необходимо создавать блок, если же вне контекста родителя он не имеет смысла, то создается элемент.

Разберем еще один пример. Предположим к нам пришел такой дизайн тулбара.

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

Здесь для того чтобы не дублировать общие стили для каждого элемента блока toolbar мы выделяем элемент toolbar__item и используем микс с остальными элементами: toolbar__button , toolbar__spacer и toolbar__text . Некоторые элементы являются вложенными, но при этом внутренняя структура блока все еще остается плоской, то есть не нарушается правило о том, что нельзя создавать элементы элеметов. И еще одно — все элементы, требующие стилизации имеют классы, в том числе и img . Ссылка на реализацию со стилями.

В заключение можно сказать что следование принципам БЭМ позволяет получить тот самый поддерживаемый и масштабируемый CSS, что увеличит скорость разработки и упростит понимание кода новыми разработчиками. При этом используется только соглашение об именовании элементов без какого-либо дополнительного инструментария. Для того чтобы применить идеи БЭМ в своем проекте нам достаточно того стека технологий который там уже наверняка есть.

Архитектура CSS

Мерой хорошего знания CSS для многих веб-разработчиков является то, насколько идеально ты можешь воссоздать в коде данный тебе макет. Ты не используешь таблицы и гордишься собой, когда сводишь к минимуму графику. Если ты действительно хорош, то работаешь с самыми новыми и лучшими техниками — вроде медиавыражений, переходов и трансформаций. Это правда, хорошие CSS-разработчики действительно всё это умеют. Однако существует совсем другая сторона CSS, которая редко упоминается, когда речь заходит об оценке чьих-то способностей.

Что интересно, обычно мы не упускаем из вида эту сторону, когда дело касается других языков. Разработчик на Rails не считается хорошим только потому, что его код работает по спецификации. Это считается базовым уровнем. Конечно, он должен работать по спецификации, но его качество измеряется другим: насколько код читаемый, легко ли его изменить или расширить, достаточно ли он отделён от других частей приложения, будет ли он масштабироваться?

Эти вопросы вполне естественны при оценке кода, и CSS здесь не должен быть исключением. Сегодняшние веб-приложения становятся всё более крупными, поэтому слабая и прямолинейная архитектура CSS может сильно усложнить разработку. Пришло время отнестись к CSS так же, как мы относимся ко всем другим частям наших приложений. Это не та вещь, которая может быть оставлена на потом или сброшена со счетов как «дизайнерская проблема».

Принципы хорошей архитектуры CSS

CSS-сообществу редко удаётся прийти к договорённости, что и как нужно делать. Даже если взглянуть только на комментарии с Hacker News или на реакцию разработчиков на выход CSS Lint, станет ясно, что многие не согласны друг с другом даже по поводу самых базовых вещей, которые стоит или не стоит делать при разработке.

Поэтому вместо того, чтобы разворачивать перед вами мой собственный список правил, я скажу, что нам стоит определиться с принципами. Если мы сможем договориться о принципах, возможно, мы начнем замечать плохой CSS не только потому, что он нарушает наши принципы и убеждения о том, что есть хорошо, но и потому, что он на самом деле усложняет процесс разработки.

Мне кажется, что принципы хорошей архитектуры CSS не сильно отличаются от целей разработки хорошего ПО. Я хочу, чтобы мой CSS был предсказуемым, его можно было использовать повторно, легко поддерживать и масштабировать.

Предсказуемость

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

Это вряд ли необходимо маленьким сайтам, которые редко изменяются, но для больших сайтов с десятками или сотнями страниц предсказуемый CSS необходим.

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

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

Поддержка

Когда нужно добавить или изменить новые компоненты или возможности, это не должно приводить к рефакторингу существующего CSS. Добавление компонента А на страницу не должно своим появлением ломать компонент Б.

Масштабируемость

Растущий в размерах и сложности сайт обычно требует для поддержки большего числа разработчиков. Масштабируемость для CSS означает, что он с лёгкостью может поддерживаться как одним человеком, так и большой командой разработчиков. Также это значит, что архитектура CSS вашего сайта достаточно доступна и не требует чрезмерных усилий для понимания. То, что вы сегодня единственный разработчик, имеющий дело с CSS, не значит, что так будет всегда.

Распространённые ошибки

Прежде чем перейти к способам соответствия нашим принципам с помощью хорошей архитектуры CSS, мне кажется, будет полезно взглянуть на распространённые ошибки, которые противоречат этим принципам. Часто бывает, что только через повторение ошибок мы можем прийти к пониманию альтернативного пути.

Следующие примеры обобщают код, который я писал сам. Технически они верны, но каждый из них привёл к проблемам и головной боли. Несмотря на мои самые лучшие намерения и обещания себе, что на этот раз всё будет по-другому, описанные подходы снова приводили к неприятностям.

Топ-пост этого месяца:  10 лучших онлайн-конструкторов логотипов как сделать лого за 5 минут

Изменение компонентов в зависимости от родителя

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

На первый взгляд этот код выглядит вполне безобидно, но давайте проверим его на соответствие принципам, которые мы определили выше.

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

Также этот блок плохо масштабируется и не слишком подходит для повторного использования. Что случится, когда его вид на главной понадобится на какой-нибудь другой странице? Придётся добавить новый селектор.

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

Представьте, если бы такой код был написан на любом другом языке. Вы создаёте обычное определение класса и потом, в другой части кода, берёте это определение класса и меняете его для определённого случая. Это прямо противоречит принципу открытости и закрытости для разработки ПО:

Части ПО (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения.

Далее в статье мы рассмотрим, как изменять компоненты, не опираясь на родительский селектор.

Слишком сложные селекторы

Удивительно, но эта статья обойдёт стороной демонстрацию всей мощи CSS-селекторов и не расскажет о том, как оформить целый сайт, не используя ни единого класса или ID.

Технически это возможно, но чем больше я работаю с CSS, тем больше стараюсь держаться подальше от сложных селекторов. Чем сложнее селектор, тем больше он привязан к HTML. Опираясь на HTML-теги и комбинаторы в селекторе, вы, может быть, и получите чистый до блеска HTML, но CSS от этого станет грязным и раздутым.

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

, а последний пример, скорее всего, добавляет отступ к первому абзацу в блоках боковой колонки.

Если бы этот HTML никогда не изменился, можно было бы согласиться с таким подходом. Но насколько реалистичным будет предположить, что HTML никогда не изменится? Чересчур сложные селекторы могут выглядеть впечатляюще и даже помогут обойтись без презентационной разметки в HTML, но вряд ли они помогут соответствовать нашим принципам хорошей архитектуры CSS.

Упомянутые примеры совсем не готовы для повторного использования. Поскольку селектор указывает на очень конкретную часть разметки, то как может другой компонент с иной структурой HTML использовать эти стили? Возьмём для примера первый селектор (выпадающее меню) — что, если похожее меню понадобится на другой странице, на которой не будет элемента #main-nav ? Тогда вам придётся повторить все стили.

Эти селекторы также очень непредсказуемы в случае, когда меняется оригинальный HTML. Представим, что разработчик решил поменять тег

И наконец: поскольку эти селекторы работают, только когда HTML остаётся неизменным, они по определению не поддерживаемы и не масштабируемы.

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

Слишком общие имена классов

При создании повторно используемых компонентов зачастую принято ограничивать область действия (ага, если бы) вложенных элементов в рамках имени класса компонента. Например:

Идея состоит в том, что классы вложенных элементов .title , .contents и .action можно безопасно оформить, не боясь, что стили повлияют на элементы с такими же классами. Это, конечно, так, но совсем не значит, что эти стили не повлияют на элементы с таким же классом, которые могут оказаться внутри.

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

Слишком общие имена классов приводят к непредсказуемому CSS.

Когда правило делает слишком много

Однажды вы создаёте визуальный компонент, который должен отстоять на 20 пикселей от верхнего левого угла блока на вашем сайте:

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


Проблема в том, что вы делаете с помощью одного селектора сразу слишком многое. Вы описываете внешний вид блока вместе с раскладкой и позиционированием в одном правиле. Внешний вид можно использовать повторно, раскладку и позиционирование — нет. И поскольку они описаны вместе, всё правило скомпрометировано.

На первый взгляд это кажется безобидным, но часто приводит к копированию кода из одного места в другое, особенно менее опытными разработчиками. Если новый член команды захочет сделать что-то похожее на конкретный компонент, скажем .infobox , вероятнее всего, он начнёт с того, что применит этот класс. Но если это не сработает из-за того, что в других условиях этот блок спозиционируется совсем не так, как нужно, то что он скорее всего сделает? По моему опыту, большинство молодых разработчиков не разобьют правило на удобные для повторного использования части. Вместо этого они просто скопируют строки кода, необходимые для этого конкретного случая, в новый селектор, дублируя код без необходимости.

Причина

Упомянутые выше проблемные примеры объединяет одна особенность: все они слишком полагаются на оформление CSS.

Это заявление может прозвучать странно. В конце концов, это же стили. Разве не должны они нести большую (если не всю) нагрузку для оформления? Не этого ли мы хотим?

Простой ответ на этот вопрос «да», но, как обычно, всё не так просто. Разделять содержимое и представление хорошо, но содержимое не отделяется от представления только потому, что ваш CSS отделён от HTML. Скажем иначе: вы не достигнете цели, если просто уберёте всё представление из HTML, но при этом для работы вашего CSS потребуется подробнейшее знание структуры HTML.

Более того, HTML редко состоит только из содержимого, почти всегда в нём есть и структура. И часто эта структура состоит из контейнеров, единственной задачей которых является объединение некоторой группы элементов для работы CSS. Даже без презентационных классов такие структуры добавляют представление в HTML-код. Но действительно ли это смешивает содержимое с представлением?

Я уверен, что на текущем уровне развития HTML и CSS необходимо и зачастую разумно использовать HTML и CSS в качестве единого слоя представления. Слой содержимого в таком случае может быть отделён с помощью шаблонизаторов или подключаемых фрагментов (как partials в Ruby).

Решение

Если HTML и CSS будут работать одним слоем для создания представления вашего веб-приложения, они должны делать это так, чтобы соответствовать всем принципам хорошей архитектуры CSS.

Лучший подход, который мне удалось найти, состоит в том, чтобы как можно меньше опираться на структуру HTML в CSS. CSS должен определять как выглядит набор визуальных элементов. Для минимизации влияния HTML эти элементы должны выглядеть ровно так, как они описаны, независимо от того, где они находятся в HTML. Если некоторые компоненты должны выглядеть по-разному в разных ситуациях, они должны быть вызваны по-другому, и отвечать за этот вызов должен HTML.

Например, в CSS с помощью класса .button описан компонент кнопки. Если в HTML понадобится конкретный элемент, который выглядит как кнопка, то нужно использовать этот класс. Если в другой ситуации этой кнопке понадобится выглядеть иначе (скажем, больше и в полную ширину), в CSS нужно определить этот новый вид, также с помощью класса, и тогда его можно будет добавить в HTML для достижения нужного вида.

CSS определяет, как выглядит ваш компонент, а HTML применяет этот вид к элементам на странице. Чем меньше CSS «знает» про структуру HTML, тем лучше.

Большой плюс указания всего, что нужно, прямо в HTML в том, что это позволяет другим разработчикам, глядя на разметку, точно понимать, как должен выглядеть этот элемент. Мотивы здесь очевидны. Без этого подхода невозможно сказать, является ли вид элемента намеренным или случайным, что приводит к непониманию в команде разработчиков.

Дополнительные усилия для написания классов в разметке обычно становятся главным аргументом против. Одно правило в CSS может быть направлено на тысячи экземпляров конкретного компонента. Действительно ли стоит писать эти классы тысячу раз только для того, чтобы явно их указать в разметке?

Несмотря на то, что эти сомнения справедливы, они могут привести к ошибочным выводам. Вывод такой: либо вы используете родительский селектор в CSS, либо вам придётся написать этот HTML-класс тысячу раз руками. Но, очевидно, есть и другие варианты. Уровень абстракции компонентов в Rails и других фреймворках легко позволяет явно описывать внешний вид прямо в HTML без необходимости снова и снова писать один и тот же класс.

Правильный подход

Совершив описанные выше ошибки снова и снова, заплатив позднее за их последствия, я пришёл к некоторым принципам. Не претендуя не всеобъемлемость, мой опыт показывает, что эти принципы помогут достичь хорошей архитектуры CSS.

Будьте точнее

Лучший способ добиться того, чтобы ваши селекторы не влияли на ненужные элементы — это не дать им такой возможности. Со временем вы можете обнаружить, что селектор вроде #main-nav ul li ul li div применяется уже совсем не к тому элементу из-за изменившейся разметки. Класс .subnav , напротив, имеет очень мало шансов быть случайно применённым не к тому элементу. Назначать классы прямо элементам, которые вы хотите оформить — лучший способ сохранить ваш CSS предсказуемым.

Если взять два примера выше, то первый больше напоминает гранату, а второй снайперскую винтовку. Граната может прекрасно сработать сегодня, но кто может гарантировать, что завтра в радиус поражения не попадёт невинный гражданский?

Разделяйте ответственность

Я уже упоминал, что хорошо организованный компонентный слой может уменьшить зависимость CSS от структуры HTML. Помимо этого, сами компоненты должны быть модульными. Компоненты должны знать только, как оформить себя, и делать это хорошо. Но они совсем не должны отвечать за раскладку, позиционирование или делать слишком много предположений насчёт того, в каком окружении они будут располагаться.

В общем случае компоненты должны определять свой внешний вид, а не раскладку и позиционирование. Будьте осторожны, когда видите свойства вроде background , color и font в одном правиле с position , width , height и margin .

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

Задайте пространство имён

Мы уже выяснили, почему родительские селекторы не всегда на 100% эффективны для ограничения действия и пересечения стилей. Гораздо лучший подход — добавить пространство имён к самим классам. Если элемент является частью визуального компонента, то каждый из классов его вложенных элементов должен использовать имя класса базового компонента в качестве пространства имён.

Добавление пространства имён к классам делает ваши компоненты самодостаточными и модульными. Это также уменьшает вероятность конфликтов с уже существующими классами и снижает специфичность, необходимую для оформления дочерних элементов.

Расширяйте компоненты модификаторами классов

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

Мы уже рассмотрели все недостатки изменения компонентов на основе их родительских элементов, но для закрепления: модификатор класса может использоваться где угодно, переопределение на основе вложенности — только в конкретной ситуации. Модификатор класса также может использоваться сколько угодно раз. Наконец, модификаторы класса очень ясно выражают намерения разработчика прямо в HTML. Классы, основанные на вложенности, напротив, полностью скрыты для разработчика, который смотрит только на HTML, что сильно увеличивает вероятность, что они от него ускользнут.

Организуйте CSS в логическую структуру

Джонатан Снук в своей замечательной книге SMACSS убеждает разделять CSS-правила на четыре отдельные категории: базовые, раскладку, модули и состояния. Базовые состоят из сбросов и умолчаний для элементов. Раскладка — для расположения глобальных элементов сайта, а также общих вспомогательных вещей вроде модульных сеток. Модули — это визуальные элементы для повторного использования и стили состояний для оформления того, что можно включить или выключить с помощью JavaScript.

В системе SMACSS модули (эквивалентные тому, что я называю компонентами) составляют большинство от всех правил в CSS, поэтому я часто прихожу к необходимости разбить их ещё больше, отделив абстрактные шаблоны.

Компоненты — это отдельные визуальные элементы. Шаблоны, напротив, скорее строительные блоки. Шаблоны не живут сами по себе и редко описывают внешний вид. Они являются одиночными повторяемыми элементами, из которых можно собрать компонент.

Приведём конкретный пример: компонент может быть модальным диалоговым окном. Это окно может иметь специальный для этого сайта градиент в заголовке, какую-то тень вокруг, кнопку закрытия в правом верхнем углу, а также фиксированное положение и выравнивание в центре по вертикали и горизонтали. Все четыре свойства этого окна могут быть повторно использованы по всему сайту, поскольку вы вряд ли захотите описывать эти свойства снова и снова. Таким образом, все они являются шаблонами, которые вместе образуют компонент диалогового окна.

Обычно я не использую классы шаблонов прямо в HTML, если для этого нет веских причин. Вместо этого я использую препроцессоры для включения стилей шаблона в описание компонента. Далее мы обсудим подробнее этот подход и мои причины для его использования.

Используйте классы строго для оформления

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

Проблема в том, что во фронтенд-разработке принято наделять классы слишком многими ролями. Они оформляют HTML, на них опирается JavaScript, их добавляют в HTML для определения поддержки возможностей, используют для автоматических тестов и т.д.

Это проблема. Когда к классам обращаются слишком многие составляющие приложения, становится страшно удалять их из HTML.

Тем не менее, устоявшиеся договорённости помогут полностью избежать этой проблемы. Когда вы видите класс в HTML, вы должны быть способны чётко сказать, для чего он предназначен. Мой совет — добавлять префикс ко всем неоформительским классам. Я использую .js- для JavaScript и .supports- для классов Modernizr. Все классы без префикса — для оформления и только для оформления.

Это позволяет искать неиспользуемые классы и удалять их из HTML простым поиском по папке со стилями. Вы можете даже автоматизировать этот процесс с помощью JavaScript, сравнивая классы в HTML с классами в объекте document.styleSheets . Классы, которых нет в document.styleSheets , можно безопасно удалять.

Итак, полезно разделять содержимое и представление, и столь же важно отделять представление от поведения. Использование классов с оформлением в качестве основы для JavaScript так сильно связывает CSS с JavaScript, что становится сложно или даже невозможно обновить оформление некоторых элементов, не сломав их поведение.

Логическая структура в именах классов

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

Николас Галлахер недавно написал о своем решении этой проблемы, которое я тоже (с некоторыми изменениями) с большим успехом применил. Чтобы проиллюстрировать необходимость в договорённости об именовании, рассмотрим следующий пример:

Глядя на классы выше, нельзя сказать, к какому типу правил они применяются. Это не только добавляет путаницы при разработке, но и усложняет автоматическое тестирование вашего HTML и CSS. Структурированная договорённость об именовании позволяет с первого взгляда на имя класса точно понять, в каких отношениях он состоит с другими классами и в каком месте HTML должен располагаться — что, в отличие от обычной практики, упрощает именование и делает возможным тестирование.

Переработанный первый пример:

Инструменты

Поддержка эффективной и хорошо организованной архитектуры CSS может быть очень сложной, особенно в больших командах. Несколько плохих правил здесь, несколько там — и снежный ком неразрешимых проблем уже покатился. Когда CSS вашего приложения уже дошел до войн за специфичность и сомнительных побед за счёт !important , может оказаться, что пути назад уже нет, разве что переписать всё с нуля.

К счастью, есть инструменты, которые позволят гораздо проще контролировать архитектуру CSS вашего сайта.

Препроцессоры

Сегодня сложно говорить про инструменты для CSS и не упомянуть препроцессоры, и эта статья не будет исключением. Но прежде чем я воспою их ценность, мне следует коротко предостеречь вас.

Препроцессоры помогают писать CSS быстрее, но не качественнее. В конце концов код превращается в обычный CSS, к которому применяются все те же правила. Если препроцессор помогает вам писать CSS быстрее, он так же помогает быстрее писать плохой CSS, поэтому важно понимать, что такое хорошая архитектура CSS, а не просто думать, что препроцессор решит все ваши проблемы.

Некоторые так называемые «фичи» препроцессоров на самом деле могут навредить архитектуре CSS. Следующих возможностей я категорически стараюсь избегать (хотя основная идея применима ко всем препроцессорам, эти правила относятся в большей степени к Sass):

  • Никогда не вкладывайте правила только для организации кода. Вкладывайте только тогда, когда это нужно в CSS на выходе.
  • Никогда не используйте примеси (mixin), если не передаёте аргумент. Примеси без аргументов гораздо лучше использовать в качестве шаблонов, которые можно расширить.
  • Никогда не используйте @extend для селектора, который не является одиночным классом. Это не имеет смысла с точки зрения дизайна и раздувает скомпилированный CSS.
  • Никогда не используйте @extend для компонентов интерфейса в модификаторе компонента, иначе вы нарушаете цепь наследования (подробнее об этом дальше).

Лучшая часть препроцессоров — это функции, вроде @extend и %placeholder . Обе они позволяют просто управлять абстракциями в CSS, при этом не раздувая код и обходясь без добавления в HTML огромного количества базовых классов, с которыми потом очень сложно управиться.

@extend нужно использовать с осторожностью, потому что иногда эти классы будут нужны в HTML. Например, когда вы впервые узнаёте про @extend , то его сразу хочется применить ко всем классам-модификаторам как-то так:

Проблема с этим подходом в том, что вы теряете цепь наследования в HTML. Теперь довольно сложно выбрать все экземпляры кнопки с помощью JavaScript.

Как правило, я не расширяю компоненты интерфейса или что-то другое, тип чего мне может пригодиться знать в будущем. Для этого случая как раз и придуманы компоненты, и это ещё один способ, помогающий отличать шаблоны от компонентов. Шаблон — это нечто, к чему вы никогда не обратитесь в логике вашего приложения, и поэтому его можно безопасно расширить с помощью препроцессора.

Как это может выглядеть на примере модального окна, о котором шла речь выше:

CSS Lint

Николь Салливан и Николас Закас создали CSS Lint, инструмент для контроля за качеством кода, который помогает разработчикам находить плохие подходы в их CSS. На сайте он описан так:

CSS Lint указывает на проблемы в вашем CSS-коде. Он делает базовую проверку синтаксиса, а также применяет к коду набор правил, которые позволяют выделить в нём проблемные подходы или признаки неэффективности. Все правила расширяемы, потому вы легко можете написать свои или пропустить ненужные.

И если набор правил по умолчанию может не подходить идеально для большинства проектов, главная особенность CSS Lint в том, что он может быть настроен в точности как вам нужно. Это значит, что вы можете взять правила из списка по умолчанию и выбрать нужные, а также написать собственные.

Инструмент вроде CSS Lint необходим любой большой команде, чтобы поддерживать в коде, как минимум, базовую последовательность и соответствие договорённостям. Как я уже упоминал раньше, одна из главных ценностей таких договорённостей в том, что с помощью инструментов вроде CSS Lint можно просто выявить то, что их нарушает.

Основываясь на договорённостях, которые я предложил выше, очень легко написать правила для определения неверных подходов. Вот несколько правил, которые я использую:

  • Не допускайте ID в своих селекторах.
  • Не используйте несемантические типы селекторов (div и span, напр.) для любого неодиночного правила.
  • Не используйте больше двух комбинаторов в селекторе.
  • Не допускайте использование классов, начинающихся с .js- .
  • Внимание, если раскладка и позиционирование применяются в правилах для элементов без префикса .l- в названии класса.
  • Внимание, если класс, определённый сам по себе, позднее переопределяется как дочерний или иначе.

Это, конечно, всего лишь возможные варианты, но они предложены для того, чтобы направить ваши мысли в сторону контроля стандартов, использующихся в коде вашего проекта.

HTML Inspector

Ранее я предположил, что будет довольно просто пройтись по вашим HTML-классам и всем подключенным стилям и выдать предупреждение, если класс был использован в HTML, но не определён ни в одном файле стилей. Сейчас я разрабатываю инструмент, который называется HTML Inspector, чтобы сделать этот процесс проще.

HTML Inspector проходит по вашему HTML и (почти как CSS Lint) позволяет вам написать собственные правила, которые вызывают ошибки или предупреждения, когда какие-то договорённости нарушаются. Сейчас я использую следующие правила:

  • Внимание, если один и тот же ID используется на странице два раза.
  • Не используйте классы, не упомянутые ни в одном из файлов стилей или передайте список разрешённых (с префиксом .js- , напр.)
  • Классы модификаторов не должны использоваться без их базовых классов.
  • Классы вложенных объектов не должны использоваться в отсутствие родительского базового класса.
  • Простые элементы

Заключение

CSS — это не просто средство визуального дизайна. Не стоит забывать о правильных подходах к программированию только потому, что вы пишете CSS. Подходы вроде ООП, DRY, принцип открытости и закрытости, разделение ответственности и т.п. также применимы к CSS.

Главная мысль вот в чем: что бы вы ни делали для организации вашего кода, убедитесь, что вы оцениваете свои действия по тому, помогают ли они сделать проще разработку и поддержку кода в будущем.

Перевод оригинальной записи «CSS Architecture» Филипа Уолтона (Philip Walton). Переведено и опубликовано с разрешения автора.

Комментарии +

  1. Сергей Васильев 20 февраля 2014 в 13:27

В подписи ссылка на оригинальную статью неверная, должна быть эта — http://philipwalton.com/articles/css-architecture/.

Сергей, спасибо, поправили.

Да уж. Сколько людей, столько и мнений

Действительно, модульный подход к написанию стилей и разметки намного облегчает жизнь при верстке крупных проектов, таких как интернет-магазины, и когда версткой занимается команда более двух человек.
Только полностью использовать тот или иной подход (SMACSS, БЭМ) не получается. Выходит такой себе симбиоз, что в принципе тоже не плохо.

Думаю, что со временем в SMACSS и БЭМ отпадет все лишнее и мы получим отличный и удобный подход.

Отличная статья. Прекрасная методология, вобравшая в себя всё лучшее, что есть на данное время.

Перевод очень качественный, было приятно читать. Побольше бы таких статей!

Пример из статьи

Я так и не понял, какой выход из этой ситуации. Я например так часто делаю. Например, задается какой то общий вид для кнопки на всем сайте

Но если код в форме, то width: 200px и следовательно

Но вы говорите что это плохо ? А какова альтернатива ?

Дмитрий, я в таких случаях пишу так:
.button <
color:#fff;
background:#f00;
>
.w_100 <
width:100px;
>
.w_200 <
width:200px;
>

Надя, и зачем это нужно? А если высота должны быть 200px, добавите h_200? А цвет розовый? Не лучше уже в style сразу закинуть, чем городить непонятные классы?

В дополнении к «.js-» и «.l-«, хочу добавить, что мы в свое практике используем «.qa-» для обозначения классов используемых исключительно для автоматического тестирования (функционального в частности). В статье это упоминалось, но примера не было.

Дмитрий, на вопрос в статье ответили и показали как правильнее расширять такой код сразу:

Почему высокий риск пересечения имен классов ??

Опечаточка «Тогда вам придётся повторить всё стили.»

Почему высокий риск пересечения имен классов ??

talgautb, высокий риск пересечения классов в состоит том, что класс .title может использоваться в качестве отдельного блока\виджета где-то еще помимо виджета widget и произойдет то самое пересечение, когда один селектор указывается несколько раз в CSS. В статье и в комментариях уже отвечено на этот вопрос и предложен вариант именования дочерних элементов виджета, путем расширения имени дочернего элемента именем самого виджета.

Хотя статья — это очередная эволюция к БЭМу. Удивительно наблюдать, как многие авторы эволюционируют в своих рассуждениях к БЭМ-подходу 🙂

Начал писать CSS с применением типовых классов, например:
.footnote <>
.title-4 <>
.v-aln_tl
.w33p
.head-pad Подобные классы я применял к разным элементам HTML на любой странице сайта. Однако вскоре контроль за использованием таких классов стал не возможен.
Во-первых, при изменении свойств конкретного селектора приходилось просматривать соотнесенные с этим классом элементы HTML на многих страницах.
Во-вторых, некоторые селекторы так широко применялись на страницах сайта, что их поиск стал затруднительным.
Тогда я столкнулся с необходимостью создания карты используемых стилей. Эта карта должна была указывать, какие селекторы на каких страницах сайта применены.
И уже в процессе работы над картой стилей я выработал для себя следующие принципы:
1. Для каждого элемента HTML должен быть определен собственный набор стилей (через класс). Копии одного элемента в рамках шаблона веб-страницы являются одним элементом HTML. Похожие элементы в разных шаблонах являются разными элементами.
2. Общая структура CSS должна отражать шаблонную организацию страниц сайта, а комментарии должны группировать классы в соответствии со структурой шаблонов. Таким образом CSS должна точно указывать место применения селектора в коде HTML.
3. CSS должна гарантировать возможность автономного редактирования стилей для каждого отдельного элемента HTML.
4. При редактировании свойств конкретного селектора CSS должна максимально точно указывать на элемент HTML, использующий данный селектор.
5. Каждый самостоятельный (не входящий в группу) элемент HTML необходимо описывать полным набором стилевых правил для получения достаточного контроля за оформлением данного элемента.
В результате, я отказался от создания карты стилей, так как структура самой CSS стала своеобразной картой, по которой легко найти в HTML-коде страниц сайта любое стилевое правило. Теперь, редактируюя CSS, я точно представляю, где и в каком элементе HTML произойдут изменения. И эти изменения не затронут ничего лишнего на странице или в шаблонах, кроме указанного в селекторе объекта.
Для наглядности привожу фрагмент того, что у меня получилось:

CSS создан для каскадов, почему нельзя использовать каскады для переопределения стилей? Это же киллер-фича всей технологии. Да, это усложняет код, но как же усложнится код, если отказаться от каскадов? Упрощать CSS путем усложнения HTML атрибутов – сомнительный путь.

БЭМ для начинающих. Очевидные и неочевидные вопросы верстки

Эта статья написана по мотивам БЭМапа — митапа по БЭМ — с одноименным названием БЭМ для начинающих.

Нас часто спрашивают о верстке по БЭМ и про технологии, которые мы используем: почему нужно делать так, а не иначе? Зачем мы создали целый стек новых технологий, когда можно пользоваться готовыми? Зачем придумали такие длинные имена в классах?

На БЭМапе Владимир Гриненко дал ответы на эти вопросы. Мы решили сохранить их в статье.

Немного теории и практики:

  • Основные почему
  • Основы БЭМ
  • Модификаторы и миксы
  • Удобство параллельной разработки и верстка блоками
  • Блоки в файловой структуре
  • Шаблоны в БЭМ
  • Тестирование верстки
  • Сборка проекта
  • С чего начинать?

Зачем вам БЭМ

БЭМ предоставляет единые правила создания и хранения кода, которые помогают масштабировать и повторно использовать код, увеличить производительность и упростить командную работу. Даже если вся ваша команда — это вы сами, БЭМ может быть вам полезен.

Основные почему

  • Почему не используем >ID определяет уникальное имя HTML-элемента. Если имя уникально, второй раз в интерфейсе его использовать не получится. Это мешает повторно использовать код.

1. ID обязателен для работы с JavaScript.

На практике современным браузерам не важно, с какими данными работать: ID или классами. Браузер одинаково быстро обрабатывает любой селектор.

Если поместить в контрол, > Текст можно написать так: Текст .

Не используем селекторы тега

HTML-разметка страниц нестабильна: новый дизайн сайта может поменять вложенность разделов, уровень заголовоков (например, с

) или превратить абзац

Даже если дизайн не меняется, набор тегов ограничен. Чтобы использовать существующую верстку в другом проекте, придется решать конфликты стилей, написанных на одни и те же теги.

Расширенный набор семантических тегов также не может выразить все потребности верстки.

Например: в шапке страницы расположен логотип, по клику на который открывается главная страница сайта ( index ).

Напишем разметку с помощью тегов: для картинки используем тег , для ссылки — тег .

Чтобы отличать ссылку с логотипа от обычной ссылки в тексте, понадобятся дополнительные стили. Отменим подчеркивание и синий цвет для ссылки в логотипе:

На главной странице ссылка в логотипе не нужна, поэтому изменим разметку для index -страницы:

Для тега отменять подчеркивание и синий цвет не нужно. Поэтому вынесем общие правила для ссылки в логотипе с разных страниц:


На первый взгляд, такой код имеет право на существование. Но представьте ситуацию, в которой дизайнер убирает логотип из макета. Такие имена селекторов не помогут понять, какие стили удалять из проекта вместе с логотипом. Селектор header a не показывает связь ссылки и логотипа. Такой селектор может принадлежать ссылке в меню шапки или, например, ссылке на профиль автора, а селектор header span может относиться вообще к любой части шапки.

Чтобы избежать путаницы, достаточно записать стили для логотипа с помощью селектора класса с именем logo :

Не используем общий сброс стилей (reset)

Общий сброс стилей — это глобальные CSS-правила, созданные для всей страницы. Они оказывают влияние на все узлы верстки, нарушают независимость компонентов и затрудняют их повторное использование.

В БЭМ не принято использовать «reset» и «normalize» даже для отдельно взятого блока.

Во время сброса или нормализации существующие стили отменяются, а затем подменяются другими, которые в процессе работы все равно приходится изменять и дорабатывать. В итоге разработчик вынужден писать стили, которые перекрывают то, что только что сбросили.

Не используем универсальный селектор (*)

Универсальный селектор сообщает, что в проекте создан стиль, который влияет на все узлы в верстке. Это накладывает ограничения на повторное использование верстки в другом проекте:

  • Необходимо дополнительно переносить в проект стили под «звездочкой». Но в таком случае универсальный селектор может повлиять на стили в новом проекте.
  • Необходимо добавлять стили со «звездочкой» в переносимую верстку.

Кроме того, универсальный селектор может сделать код проекта непредсказуемым. Например, повлиять на стили компонентов из универсальной библиотеки.

Общие стили не выигрывают время: часто разработчик начинает с того, что сбрасывает все отступы для универсальных компонентов ( * < margin: 0; padding: 0; >), а потом все равно задает их как в макете (например, margin: 12px; padding: 30px; ).

Не используем вложенные селекторы

Вложенные селекторы увеличивают связность кода и затрудняют его повторное использование.

БЭМ не запрещает вложенные селекторы, но рекомендует не злоупотреблять ими.

Например, вложенность уместна, если нужно изменить стили элементов в зависимости от состояния блока или заданной темы:

Не используем комбинированные селекторы

Комбинированные селекторы имеют более высокую специфичность, чем одиночные, поэтому переопределять блоки становится сложнее.

Рассмотрим такой код:

Зададим CSS-правила в селекторе .button.button_theme_islands ради экономии в записи.

Теперь добавим блоку модификатор active :

Селектор .button_active не переопределит свойства блока, записанные как .button.button_theme_islands , так как специфичность .button.button_theme_islands выше, чем у .button_active . Для успешного переопределения селектор модификатора блока также должен быть скомбинирован с селектором .button и объявлен ниже .button.button_theme_islands , так как специфичность обоих селекторов одинакова:

Если использовать простые селекторы классов, переопределение стилей не вызовет проблем:

Не совмещаем тег и класс в селекторе

Совмещение тега и класса (например, button.button ) повышает специфичность CSS-правил и затрудняет их переопределение.

Рассмотрим такой код:

Зададим CSS-правила в селекторе button.button .

И добавим блоку модификатор active :

Селектор .button_active не переопределит свойства блока, записанные как button.button , так как специфичность button.button выше чем у .button_active . Чтобы повысить специфичность, селектор модификатора блока также нужно скомбинировать с тегом button.button_active .

В результате развития проекта могут, например, появиться блоки с селекторами input.button , span.button и a.button . В таком случае все модификаторы блока button и вложенные в него элементы потребуют четыре разные декларации для каждого случая.

Методология в редких случаях допускает объединение селекторов тега и класса, например, для стилизации комментариев в CMS-системах, которые не позволяют генерировать правильную разметку.

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

Не используем селекторы атрибутов

Селекторы атрибутов по информативности уступают селекторам классов. Чтобы доказать это, рассмотрим пример с формой поиска в шапке:

Воспользуемся селекторами атрибутов, чтобы записать стили для формы:

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

Можно записать, например, так:

Код стал более однозначным: теперь понятно, что стили относятся к форме, которая отвечает за поиск.

Но вложенность селекторов по-прежнему повышает специфичность CSS-правил и мешает безболезненно переносить верстку из проекта в проект. Чтобы избавиться от вложенности, воспользуемся принципами БЭМ.

Короткие выводы

Классы — единственный селектор, который позволяет изолировать стили каждого компонента в проекте, повысить читаемость кода и не ограничивать повторное использование верстки.

БЭМ. От теории к практике

  • Основы БЭМ
  • Модификаторы и миксы
  • Удобство параллельной разработки и верстка блоками
  • Блоки в файловой структуре
  • Шаблоны в БЭМ
  • Тестирование верстки
  • Сборка проекта
  • С чего начать?

Основы БЭМ

Методология БЭМ — это набор универсальных правил, которые можно применять независимо от используемых технологий, будь то CSS, Sass, HTML, JavaScript или React.

БЭМ помогает решить следующие задачи:

  • повторно использовать верстку
  • безболезненно менять верстку местами в одном проекте
  • переносить готовую верстку из проекта в проект
  • создавать стабильный, предсказуемый и понятный код
  • сократить время на отладку проекта

В БЭМ-проекте любой интерфейс делится на блоки, которые могут содержать элементы. Блоки — это независимые компоненты страницы. Элементы не существуют вне блока. Каждый элемент может принадлежать только одному блоку.

Именно блокам и элементам посвящены первые две буквы в аббревиатуре БЭМ.

Имя блока всегда уникально. Оно задает пространство имен для элементов и устанавливает видимую связь между всеми составляющими блока. Длинные, но понятные имена блоков и элементов позволяют определить связь между компонентами и не потерять составные части этих компонентов при переносе верстки.

Чтобы показать всю силу БЭМ-нейминга, рассмотрим пример с формой. По БЭМ-методологии форма будет представлена блоком form . В HTML имя блока всегда записывается в атрибуте class :

Обратите внимание, что имя блока отделяется от имени элемента специальным разделителем. В классической схеме именования в БЭМ для разделителя используются два подчеркивания. Разделители могут быть любыми. Существуют альтернативные схемы, и каждый разработчик выбирает наиболее удобную для себя. Важно, чтобы разделители давали возможность на программном уровне отличать блоки от элементов и модификаторов.

Из имен селекторов очевидно, что для переноса формы в другой проект, необходимо скопировать все ее составляющие:

Запись имен в классах с помощью блоков и элементов решает еще одну важную проблему: избавляет от вложенности селекторов. У всех селекторов в БЭМ-проекте одинаковый вес. То есть переопределять стили, написанные по БЭМ, гораздо удобнее.

Теперь, чтобы использовать такую же форму в другом проекте, достаточно просто скопировать ее разметку и стили.

Суть именования компонентов в БЭМ в том, что в имени можно явно указать связь блока и его элементов.

Третья буква в аббревиатуре БЭМ

Официально буква «М» означает «модификатор», но негласно под нее попадает еще одно очень важное понятие в БЭМ — «микс». И модификаторы и миксы изменяют блок и его элементы. Давайте рассмотрим подробнее.

Модификатор определяет внешний вид, состояние и поведение блока либо элемента.

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

Разберемся, как работают модификаторы.

Предположим, в проекте нужна такая же форма поиска, которую мы рассмотрели выше. Она будет выполнять те же функции, но выглядеть по-другому (например, разный вид у форм поиска в шапке и в подвале страницы). Первое, что можно сделать, чтобы изменить внешний вид формы, написать дополнительные стили:

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

В БЭМ можно добавить блоку новые стили с помощью модификатора:

Запись говорит о том, что блоку form присвоен модификатор type со значением original . В классической схеме имя модификатора отделяется от имени блока или элемента одним подчеркиванием.

Форма может быть уникальна по разным показателям — цвету, размеру, типу, теме оформления. Все эти параметры можно задать с помощью модификатора:

Одна и та же форма может выглядеть по-разному и при этом быть одного размера:

При этом селекторы для каждого модификатора все равно будут иметь одинаковый вес:

Важно! Модификатор содержит только дополнительные стили, которые как-то изменяют исходную реализацию блока. Это позволяет один раз написать, как должен выглядеть универсальный блок, и добавить в стили модификатора только те свойства, которые отличают блок от его исходного вида.

Поэтому модификатор всегда должен находиться на одном DOM-узле с блоком или элементом, к которому он относится.

Модификатор позволяет получать очень частные случаи использования универсальных компонентов. При этом код блока или элемента не меняется, на DOM-узле просто создается нужная комбинация из модификаторов.

Микс позволяет одинаково форматировать разные HTML-элементы, совмещать поведение и стили нескольких сущностей без дублирования кода и решать задачи абстрактных блоков-оберток.

Миксом называется одновременное размещение нескольких БЭМ-сущностей (блоков, элементов, модификаторов) на одном DOM-узле.

Миксы, как и модификаторы, используются, чтобы изменять блоки. Разберем примеры, когда нужно применять миксы.

Различия блоков могут быть не только визуальными: блоки могут быть семантически разными. Например, форма поиска, форма регистрации и форма заказа товаров — это все формы, и в верстке они будут выражены блоком form , хотя общих стилей у них практически не будет. Отобразить такие отличия с помощью модификаторов невозможно.

Можно вынести общие стили для таких блоков, но повторно использовать такой код не получится.

Рассмотрим, как можно создать семантически разные блоки с помощью микса на примере все той же формы:

В селекторе класса .form описаны все стили, которые могут быть у любой формы (заказов, поиска или регистрации):

Теперь предстоит сделать из универсальной формы форму поиска. Для этого в проекте необходимо создать дополнительный класс search , который отвечает только за поиск. Чтобы объединить стили и поведение класса .form и .search , нужно разместить эти классы на одном DOM-узле:

В данном случае класс .search — это отдельный блок, который определяет поведение. У этого блока не может быть модификаторов, отвечающих за форму, темы, размеры. Такие модификаторы уже есть у универсальной формы. Микс помогает совместить стили и поведение этих разных блоков.

Рассмотрим еще один пример, когда меняется семантика компонента. Для примера возьмем навигационное меню в шапке страницы, в котором все пункты должны быть ссылками:

Нужная функциональность для ссылок уже реализована в блоке link . Но ссылки в меню должны отличаться визуально от ссылок в тексте. Существует несколько способов изменить ссылки в меню:

1. Создать модификатор для пункта меню, который превратит пункт в ссылку:

В таком случае для реализации модификатора придется скопировать поведение и стили блока link . Это приведет к дублированию кода.

2. Воспользоваться миксом универсального блока link и элемента item блока menu :

Микс двух БЭМ-сущностей позволит применить базовую функциональность ссылок из блока link и дополнительные CSS-правила из блока menu , и не копировать код.

Внешняя геометрия и позиционирование. Отказываемся от абстрактных HTML-оберток

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

В БЭМ стили, отвечающие за внешнюю геометрию и позиционирование, задаются через родительский блок.

Рассмотрим на примере универсального блока меню, который нужно разместить в шапке. В верстке блок меню должен отступать от родительского блока на 20px.

Существует несколько решений для этой задачи:

1. Написать стили с отступами самому блоку меню:

В таком случае блок menu перестанет быть универсальным. Если понадобится разместить меню в подвале страницы, придется править стили, потому что отступы скорее всего будут другими.

2. Создать модификатор для блока меню:

В таком случае в проекте появятся два типа меню, хотя это не так. Меню остается одно и то же.

3. Определить внешнее позиционирование блока — вложить блок menu в абстрактную обертку (например, блок wrap ), где задать все отступы:

Чтобы окончательно отказаться от соблазна создавать модификаторы и менять стили самого блока для позиционирования его на странице, нужно понять простую вещь:

Отступ от родительского блока — это не свойство вложенного блока быть с таким отступом. Это свойство родительского блока — знать, что вложенный в него блок должен отступать от границы на определенное количество пикселей.

4. Использовать микс. Знания про позиционирование вложенных блоков описываются в элементах родительского блока. Затем элемент родительского блока миксуется к вложенному блоку. В таком случае вложенный блок не специфицирует никакие отступы и может быть легко переиспользован в любом месте.

Продолжим рассматривать пример:

Здесь внешняя геометрия и позиционирование блока menu задана через элемент header__menu . Блок menu не специфицирует никакие отступы и может быть легко использован повторно.

Элемент родительского блока (в нашем случае это header__menu ) полностью решает задачу абстрактных блоков-оберток, отвечающих за внешнее позиционирование блока.

Удобство параллельной разработки

В БЭМ любой макет делится на блоки. Благодаря тому, что блоки не зависят друг от друга напрямую, они могут разрабатываться параллельно разными разработчиками. Разработчик создает блок как универсальный компонент, который может быть переиспользован в любом другом проекте.

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

Поблочная верстка проекта помогает сократить время на интеграцию кода разных разработчиков, гарантирует уникальность имен каждого компонента и позволяет тестировать блоки на стадии разработки.

Блоки в файловой структуре

Все БЭМ-проекты имеют схожую файловую структуру. Привычное для разработчиков расположение файлов облегчает навигацию по проекту, упрощает переключение между проектами и перенос блоков из одного проекта в другой.

Реализация каждого блока хранится в отдельной папке проекта. Каждой технологии (CSS, JavaScript, тестам, шаблонам, документации, картинкам) соответствует отдельный файл.

Например, если внешний вид блока input задан с помощью CSS, то код будет сохранен в файле input.css .

Код модификаторов и элементов также хранится в отдельных файлах блока. Такой подход позволяет подключать только те модификаторы и элементы, которые необходимы для данной реализации блока.

Чтобы улучшить навигацию в проекте, модификаторы блока со множественными значениями также можно объединять в отдельные директории.

Файловая структура любого БЭМ-проекта состоит из уровней переопределения. Уровни переопределения позволяют:

Поблочная разработка и хранение всех технологий блока в одной папке облегчает перенос блока из проекта в проект. Чтобы вместе с версткой перенести все стили и поведение блока, достаточно скопировать папку этого блока в новый проект.

Шаблонизация в БЭМ

В HTML разметка блока повторяется каждый раз, когда блок встречается на странице. Если разработчик пишет HTML вручную, исправлять ошибку или вносить дополнительные изменения необходимо в каждом экземпляре блока в разметке. Чтобы генерировать HTML-код и применять правки автоматически, в БЭМ используются шаблоны: блоки сами отвечают за то, как они будут представлены в HTML.

  • Сократить время на отладку проекта, так как изменения в шаблоне автоматически применятся ко всем блокам проекта.
  • Изменять разметку блока.
  • Переносить блоки с текущей разметкой в другой проект.

В БЭМ используется шаблонизатор bem-xjst, который содержит два движка:

  • BEMHTML — преобразует BEMJSON-описание страницы в HTML. Шаблоны описываются в файлах с расширением .bemhtml.js .
  • BEMTREE —преобразует данные в BEMJSON. Шаблоны описываются в BEMJSON-формате в файлах с расширением .bemtree.js .

Если шаблоны к блокам не написаны, шаблонизатор по умолчанию установит блокам тег


Сравните декларацию блоков и выходной результат HTML:

Чтобы изменить разметку блока menu , необходимо написать шаблоны для блока:

1. Меняем тег блока menu :

По аналогии с CSS, шаблон будет применен ко всем блокам menu на странице.

2. Добавляем дополнительный элемент ( menu__inner ), который выполнит функцию внутренней обертки и будет отвечать за расположение элементов внутри блока menu . Изначально элемент menu__inner не указывался в декларации, поэтому необходимо добавить его на этапе сборки шаблонов.

Шаблоны в БЭМ написаны на JavaScript, поэтому добавить новый элемент в шаблон также можно с помощью JavaScript:

3. Изменяем теги всем элементам inner и item :

5. Изменяем существующий шаблон. Правила в шаблонах применяются так же, как в CSS: нижнее правило перекрывает верхнее. Добавим новые правила в шаблон, изменим тег ссылкам с на :

Тестирование верстки

Протестировать работу всей страницы проблематично. Особенно в динамическом проекте, который связан с базой данных.

В БЭМ каждый блок покрывается тестами. Тесты — это такая же технология реализации блока, как JavaScript или CSS. Блоки тестируются на этапе разработки. Проще проверить правильность работы одного блока и потом собрать проект из гарантированно протестированных блоков. После этого останется только убедится, что обвязка для блоков работает правильно.

Сборка проекта

Для удобства работы с кодом в БЭМ-проекте все блоки и технологии разложены по отдельным папкам и файлам. Чтобы объединить исходные файлы в один (например, все CSS-файлы в project.css , все JS-файлы в project.js и т. п.), используется сборка.

Сборка решает следующие задачи:

  • Объединяет исходные файлы, разложенные по файловой структуре проекта.
  • Подключает в проект только необходимые блоки, элементы и модификаторы (БЭМ-сущности).
  • Учитывает порядок подключения.
  • Обрабатывает код исходных файлов в процессе сборки (например, компилирует LESS-код в CSS-код).

Чтобы включить в сборку только необходимые БЭМ-сущности, необходимо составить список блоков, элементов и модификаторов, используемых на странице. Такой список называется декларацией.

Так как в БЭМ блоки разрабатываются независимо, находятся в разных файлах файловой системы, они ничего не знают друг о друге. Чтобы одни блоки могли строиться на основании других, необходимо указывать зависимости. За это отвечает отдельная технология в БЭМ — файлы deps.js . По файлам зависимостей инструмент сборки понимает, какие блоки дополнительно подключить в проект.

С чего начать?

Разработчики БЭМ создали шаблонный проект project-stub, в который по умолчанию подключены технологии и библиотеки БЭМ. Он содержит необходимый минимум конфигурационных файлов и директорий, чтобы быстро развернуть проект с нуля.

Верстка по БЭМ методологии

BEM – это методология, разработанная в яндексе, расшифровывается, как:

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

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

Какие правила необходимо соблюдать, делая верстку по БЭМ методологии?

  1. Придумывая название блоку или элементу, ответьте себе на вопрос – Что это? (карточка: card)
  2. Имя элемента соответствует схеме: имя-блока__имя-элемента (через двойное подчеркивание).
  3. Давая имя модификатору, ответьте на вопрос – Какой?
  4. Имя модификатора отделяется от блока или элемента одним подчеркиванием: имя-блока_модификатор или имя-элемента_модификатор.
  5. В стилях блока с элементами не должны присутствовать свойства, влияющие на внешнее окружение и размеры (отступы, границы, позиционирование). Создайте внешний блок-обертку, которому и будете задавать опасные свойства.
  6. Не используйте в качестве селекторов названия тегов и id, только классы. Это дает взаимозаменяемость тегов, без необходимости изменять названия селекторов.

Пример верстки блока статистики:

В стилях блока с классом stat, у нас нет никаких паддингов и маржинов. Мы поместили его в специальный блок с классом container и прописали в стилях поля и отступы. Почему мы так сделали? Помните о независимости блоков? В случае необходимости, можно легко скопировать участок кода вместе с CSS:

Затем вставить в другое место на странице или сайте и у вас ничего там не поедет. Грубо говоря, вы не притащите на новое место ненужные отступы и позиционирование.

Вёрстка по БЭМ

Вёрстка, да и просто сам CSS, являются отдельным пунктом нелюбви разработчиков к фронтенду. Поддержка CSS в больших проектах может превратиться в хождение по минному полю, даже если применяются современные библиотеки/фреймворки, поддерживающие компонентный подход.

Если же вы будете придерживаться методологий вёрстки, то сможете облегчить поддержку большого проекта и локализовать изменения при решении задач.

Существует несколько различных CSS-методологий: 1. “Как быстрее” — да, это тоже методология; 2. SMACSS; 3. OOCSS; 4. FUN; 5. Atomic CSS; 6. БЭМ, про который мы и будем сейчас рассказывать.

Что же такое БЭМ?

БЭМ — это сокращение от «Блок-Элемент-Модификатор». В рамках данной методологии компоненты и части страниц логически разбиваются на блоки и элементы, а их CSS-классы пишутся специальным образом, например:

Сразу скажем, что правила именования могут отличаться от проекта к проекту. Как и некоторые другие вольности. Поэтому мы разберём только основы БЭМ и наиболее частые ошибки, возникающие при знакомстве с БЭМ.

Блок — это отдельный функциональный элемент, который может использоваться повторно.

Главная страница (home-page) — хороший пример блока, хоть он и не будет использоваться повторно.

Кнопка (button) — замечательный пример блока. То, что это блок, можно легко сказать по тому, что он будет присутствовать в самых разных местах.

В терминах React, Angular и Vue каждый блок будет реализовываться соответствующим компонентом.

Итак, предположим, что наш блок — это «логотип компании» (company-logo). В HTML он будет иметь следующую разметку:

Блок не должен зависеть от того, где он находится, поэтому в CSS не должно быть, например, margin . Приведём пример CSS:

Элемент

Элемент — часть блока. У блока может быть несколько элементов, а может и не быть совсем. Допустим, у нашего блока есть два элемента — совёнок (owl) и текст логотипа (text).

Итак, наш блок с элементами будет иметь следующий вид. Обратите внимание на имена классов:

Обратите внимание на разделитель в имени класса — два подчёркивания __ . Он как раз и отделяет имена блоков и элементов.

Элемент обязательно должен находиться внутри блока, но элементы могут находиться «друг в друге» в разметке в рамках одного блока.

Итак, пример неправильного БЭМ:

Элементы могут быть только у блоков. Приведём пример частой ошибки при использовании БЭМ:

Этот код может быть исправлен следующим образом:

С первого взгляда, выглядит «не очень поддерживаемо». Со второго взгляда — можно код сократить, используя препроцессоры. На LESS эта простыня будет выглядеть куда приятнее:

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

Модификатор

Предположим, что скоро Новый год, и нам необходимо нашего совёнка одеть в новогоднюю шапочку. И при этом нам не хочется менять уже написанный код (собственно, мы и хотим поддерживаемости). Для этого и существуют модификаторы:

CSS код будет выглядеть следующим образом:

Обратите внимание, что в новом классе мы модифицировали только те свойства, которые необходимо. Это достигается за счёт того, что в тэге написаны классы company-logo__owl company__logo—new-year — исходные свойства применяются, потому что они есть в company-logo__owl , а перегруженные — потому что класс модификатора написан после.

В действительности, писать модификаторы непосредственно у элементов — не самая лучшая практика. Best practice состоит в том, чтобы писать модификаторы именно у блоков:

Миксин (микс, миксина, примесь)

Про это часто забывают, но в действительности БЭМ — это БЭММ — «Блок-Элемент-Модификатор-Миксин». Блок часто является элементом другого блока, плюс отображение может отличаться от того места, где он находится (точнее — от того блока, где блок является элементом).

Обратите внимание на порядок следования классов, а именно, что header__logo стоит последним. Это позволяет изменять отображение блока как элемента в другом блоке.

Приведём пример CSS:

Если же мы напишем классы в другом порядке, то не сможем поменять цвет фона без !important :

Учитывайте это, когда будете разрабатывать React/Angular/Vue компоненты.

Вот мы и рассмотрели основы и некоторые ошибки. Если вас заинтересовал БЭМ, то настоятельно рекомендуем погрузиться в мир БЭМ здесь: http://getbem.com/ https://ru.bem.info/

И не забывайте оставлять комментарии!

Блок-элемент-модификатор (BEM) & Atomic CSS — SitePoint

Наш CSS может выглядеть так:

В обеих наших разметках и CSS поиск — обратный и search__label — inverse — дополнительные имена классов . Они не являются заменой для поиска и search__label . Названия классов — это единственный тип селектора, используемый в системе BEM. Селекторы детей и потомков могут использоваться, но потомки также должны быть именами классов. Селекторы элементов и идентификаторов являются verboten. Обеспечение уникальности уникальности блоков и элементов также предотвращает коллизии имен, что может стать проблемой среди команд.

Существует несколько преимуществ такого подхода:

  • новым членам команды легко прочитать разметку и CSS и понять ее поведение
  • добавление большего количества разработчиков повышает производительность группы
  • последовательное присвоение имен уменьшает вероятность столкновений с классом имен и побочных эффектов
  • CSS не зависит от разметки
  • CSS очень многоразовый

В BEM есть намного больше, чем удобно разместить в разделе главы. Сайт BEM описывает эту методологию гораздо более подробно, а также содержит инструменты и руководства для начала работы. Чтобы узнать больше об аспекте соглашения об именах BEM, еще одним фантастическим ресурсом является Get BEM.

Atomic CSS

Если BEM — любитель индустрии, Atomic CSS — его непослушный maverick. Названный и объясненный Тьерри Кобленцем из Yahoo в его статье 2013 года, «Оспаривая лучшие рекомендации CSS», Atomic CSS использует плотную библиотеку имен классов. Эти имена классов часто сокращаются и оторваны от содержимого, которое они затрагивают. В системе Atomic CSS вы можете указать, что такое имя класса, но нет никакой связи между именами классов (по крайней мере, не теми, которые используются в таблице стилей) и типами контента.

Давайте проиллюстрируем пример. Ниже приведен набор правил, которые мы могли бы назвать обычной архитектурой CSS. В этих наборах правил используются имена классов, которые описывают контент, к которому они применяются, — глобальное поле сообщения и стили для сообщений об ошибках «успех», «предупреждение» и «ошибка»:

Чтобы создать окно с сообщением об ошибке, нам нужно добавить имена классов msg и msg-error в атрибут класса :

Давайте сравним это с атомной системой, где каждое объявление становится его собственным классом:

Это намного больше CSS. Давайте теперь воссоздаем наш компонент сообщения об ошибке. Используя Atomic CSS, наша разметка становится:

Наша разметка также более многословна. Но что происходит, когда мы создаем компонент предупреждающего сообщения?

Два названия классов были изменены: bg-d и bc-d были заменены на bg-c и bc-c . Мы повторно использовали пять наборов правил. Теперь давайте создадим кнопку:

Эй сейчас! Здесь мы повторно использовали четыре набора правил и избегали добавления каких-либо правил в нашу таблицу стилей. В надежной атомной архитектуре CSS добавление нового HTML-компонента, такого как боковая панель статьи, не потребует добавления большего количества CSS (хотя на самом деле может потребоваться добавить немного больше).

Atomic CSS немного напоминает использование служебных классов в вашем CSS, но доведено до крайности. В частности, это:

  • сохраняет CSS-обрезку, создавая очень гранулированные, многократно используемые стили, а не набор правил для каждого компонента
  • значительно уменьшает конфликты специфичности с помощью системы селекторов низкой специфичности
  • позволяет быстро разрабатывать HTML-компонент после определения исходных наборов правил

Однако Atomic CSS не без споров.

Дело против атомного CSS

Atomic CSS противоречит всему, чему нас учили писать CSS. Он чувствует себя почти так же плохо, как приклеивание style атрибутов во всем мире. Действительно, одна из главных критических замечаний по методологии Atomic CSS заключается в том, что она размывает границы между контентом и презентацией. Если плавает элемент слева и добавляет десятикратный край, что мы будем делать, когда мы больше не хотим, чтобы этот элемент оставался слева?

Один из ответов, конечно, состоит в том, чтобы удалить класс fl из нашего элемента. Но теперь мы меняем HTML. Вся причина использования CSS заключается в том, что разметка не подвержена влиянию презентации и наоборот. (Мы также можем решить эту проблему, удалив правило .fl из нашей таблицы стилей, хотя это повлияет на каждый элемент с именем класса fl .) , обновление HTML может быть небольшой ценой за триммер CSS.

В оригинальном посте Кобленца он использовал названия классов, такие как .M-10 для поля : 10px и .P-10 для дополнения : 10px . Проблема с таким соглашением об именах должна быть очевидной. Изменение на 5 или 20 пикселей означает, что нам необходимо обновить наш CSS и наш HTML-код или риск иметь имена классов, которые не могут точно описать их эффект.

Использование имен классов, таких как p-1x как это сделано в этом разделе, решает эту проблему. Часть 1x имени класса указывает отношение, а не определенное количество пикселей. Если базовое дополнение составляет пять пикселей (то есть .p-1x ), тогда .p-2x установит десять пикселей отступов. Да, это меньше описать, что такое имя класса, но это также означает, что мы можем изменить наш CSS без обновления нашего HTML-кода и без создания вводящего в заблуждение имени класса.

Атомная архитектура CSS не мешает нам использовать имена классов, которые описывают контент в нашей разметке . Вы можете добавить к вашему коду .button-close или .accordion-trigger . Такие имена классов на самом деле предпочтительны для манипуляций JavaScript и DOM.

BEM против Atomic CSS

BEM работает лучше всего, когда у вас есть большое количество разработчиков, которые строят CSS и HTML-модули параллельно. Это помогает предотвратить ошибки и ошибки, которые создаются значительными командами. Он хорошо масштабируется, отчасти потому, что соглашение об именах является описательным и предсказуемым. BEM не только для больших команд, но он отлично работает для больших команд.

Atomic CSS работает лучше, когда есть небольшая команда или один инженер, ответственный за разработку набора правил CSS, с полными компонентами HTML, построенными более крупной командой. С помощью Atomic CSS разработчики могут просто взглянуть на руководство по стилю — или на источник CSS — определить, какой набор имен классов потребуется для определенного модуля.

Знайте, когда идти своим путем

На практике ваш CSS может включать сочетание подходов. У вас могут быть имена классов, которые описывают контент или компоненты в дополнение к именам классов утилит, которые влияют на макет. Если у вас нет полного контроля над разметкой, как с CMS, то ни один из этих подходов не может быть полезен. Возможно, вам даже понадобится использовать длинные и специальные селектора, чтобы достичь желаемого.

Разработка расширяемых компонентов на HTML и CSS

8 Сентября 2020

Ушли те дни, когда мы делали страницы фиксированной ширины из макетов фотошопа по принципу pixel-perfect. Для того, чтобы делать раскладку, адаптирующуюся к любой ширине экрана, наши рабочие процессы изменились и стали более последовательными и гибкими. Мы пришли к пониманию важности модульности и того, как она способствует гибкости, необходимой нам, чтобы оставаться быстрыми в браузере. Для меня как дизайнера и фронтенд-разработчика, гибкость является неотъемлемой частью моего подхода к веб-проектам. Также я заметил, что занимаюсь вопросами дизайна в браузере намного чаще, чем с файлами и для поддержки этого рабочего процесса мне нужна возможность строить модули интерфейса, которые можно легко расширять.

Расширяемые модули

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

Шаг 1: определите тип модуля

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

Шаг 2: определите основу для модуля

Следующим шагом будет поиск основных правил для компонента или паттерна, которые будут наследовать все их вариации. Эти правила должны быть относительно минимальными и затрагивать свойства, которые редко меняются. Наиболее часто в моей практике это были правила для свойств margin , padding , position и display .

Все примеры кода в статье используют систему именования БЭМ (блок, элемент, модификатор). БЭМ дает множество преимуществ, но главным из них для меня является то, что я могу узнать, что делает фрагмент разметки, просто взглянув на его название. Если вы хотите узнать больше об этой методологии, я советую в качестве введения эту статью.

Шаг 3: определите общие элементы

Если вы создаете компонент, то вы можете сразу перейти к следующему шагу, но если вы создаете паттерн с дочерними элементами, то следующим шагом вы должны определиться с ними. Эти элементы тематически связаны с родительским блоком (но могут существовать и вне паттерна как самостоятельные компоненты).

Шаг 4: расширяйте при помощи модификаторов

Финальный шаг это расширение вашего компонента или паттерна с помощью модификаторов. Модификаторы это по сути вариации, расширяющие основной блок вместе с его потомками, которые могут создаваться по необходимости.

Пример модификатора HTML

Пример модификатора CSS

Примеры

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

О компонентах

Следующее демо показывает несколько традиционных компонентов и их вариации. Каждый компонент состоит из родительского блока и модификаторов, расширяющих стили этого блока. Это позволяет быстро создавать такие вариации, дает ва гибкость при разработке и адаптации компонентов к любому пользовательскому интерфейсу.

Образцы обычных компонентов

Компоненты по своей природе должны быть сравнительно простыми, так как не содержат дочерних элементов. Теперь перейдем к более сложному вопросу.

О паттернах

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

Дефолтный медиа-паттерн

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

  1. Стили флекс-контейнера.
  2. Внешние отступы для контейнера и его содержимого.

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

Паттерн медиа-карточка (Media Card)

Хотя дефолтный медиа-паттерн может быть достаточен в некоторых случаях, во многих ситуациях вам нужно значительно изменить его внешний вид. Следующий шагом надо разобраться с вариациями, которые позволят нашему паттерну адаптироваться к разным ситуациям. Начнем с вариации, которая сильно отличается от дефолтного внешнего вида — медиа-карточки. Нам практически не придется менять разметку и мы можем изменить внешний вид нашего паттерна просто добавив родительскому блоку класс-модификатор:

Паттерн медиа-объект (Media Object)

Предположим, что далее нам понадобился паттерн, в котором изображение и текст выводятся рядом при наличии достаточного пространства. Это паттерн обычно известен как “медиа-объект”. Для его создания мы просто расширим медиа-паттерн, который у нас уже есть, чтобы минимизировать избыточный код.

Паттерн медиа-планка (Media Slat)

Попробуем пойти еще дальше создав вариант паттерна, с которым наш подход будет проверяться по-настоящему. Созданные нами вариации прекрасно реализуют мои потребности в дизайне, но мне нужен еще один вариант с большей модификацией. В этом варианте будет заполняться все пространство окна — половина текстом и половина изображением. В дополнение к этому мне нужно, чтобы текст выравнивался на одном уровне с остальным контентом страницы. Мы называем это медиа-планка.

Теперь у нас есть несколько вариантов медиа-паттерна: дефолтный вариант, медиа-карточка, медиа-объект и полноэкранная планка. Эти варианты будут полезны в разных обстоятельствах и они все используют в качестве основы одинаковый код. Также хорошо, что любое изменение базового паттерна повлияет на все паттерны, делая все экземпляры синхронизированными и единообразными.

Заключение

Мы рассмотрели, почему расширяемые компоненты и паттерны хороши при построении интерфейсов, требующих гибкости и поддерживаемости. Для иллюстрации этого мы изучили шаги, необходимые при создании расширяемых компонентов. Преимущество построения интерфейсов таким способом станет очевидным сразу, потому что вы будете тратить меньше времени на рефакторинг при неожиданных изменениях или дополнениях дизайна, а стили компонентов будет легче поддерживать.

Принципы концепции БЭМ (блок — элемент — модификатор)

Здравствуйте. Доводилось ли кому нибудь реализовывать проекты с применением БЭМ методологии? Пытаюсь разобраться в принципах концепции. Опишу свой порядок суждений, если я где не прав — поправьте меня. Вот, например, имею следующего вида header (прикрепленное изображение).

В хедере располагается три других блока: logo, menu и info. Вот как я рассуждаю — являются ли данные блоки элементами для блока header? Нет, не являются, т.к. «Элемент – это часть блока, отвечающая за отдельную функцию. Он может находиться только в составе блока и не имеет смысла в отрыве от него.» (с оффсайта БЭМа). Любой из этих элементов мы может вынести из блока header (например, расположить меню в сайдбаре справа от контента). Соответственно, мы даем им обычные, не составные имена классов.
А вот уже каждый пункт меню — является элементом для блока меню и его класс я именую: menu__item. Если мне нужно сделать особый стиль для активного пункта меню, добавляется класс-модификатор: menu__item_curren_yes.
А как быть в случае, в меню есть подменю, т.е.

11.09.2014, 11:56

Смешивание концепции БЭМ с фреймворком для верстки
Здравствуйте, переписываю верстку приложения под БЭМ. До модификаций верстка была выполнена с.

БЭМ
Никто не подскажет хорошее руководство по БЭМ’у?Желательно, сайты на которых именно вы обучались, а.

БЭМ ли это?
С точки зрения БЭМ, правильно ли такое именование классов? Или новый блок не должен называться, как.

Верстка, методология БЭМ
Помогите пожалуйста, перерыла весь инет, не могу найти толковое объяснение, как оформлять.

11.09.2014, 14:39 2

Если вам принципиально, чтобы ваше меню функционально вело себя как один блок, то можно просто добавить элемент submenu и включить в него описание элементов item.

Лучше же просто вложить один блок в другой, например определить для блока menu модификатор submenu и вкладывать его в самого себя.

11.09.2014, 14:39

Верстка адаптивной галереи с использованием БЭМ
Не буду просить помочь сделать. А кто нибудь может обьяснить русским языком что требуется? .

Нуждаюсь в квалифицированном пинке по написанию классов по БЭМ
Я тот код настрочил. У меня есть вопросы. В общем хотел бы его показать. Просто в будущем он пойдёт.

Концепции ООП
Товарищи, помогите. Пользоваться гуглом не умею судя по всему. Я знаю такие концепции ООП как.

Топ-пост этого месяца:  Изучаем PHP определение массива и быстро по домам!
Добавить комментарий