БЕМ-методологія організації CSS-коду

Писати CSS-код легко. Масшабіровать і підтримувати його - немає

Це правда. І це неодноразово підтверджувалося в багатьох проектах. Будь то конструктор сайтів з налаштованим темами (проект Getsocio - 28 тисяч рядків CSS) коду або сайт-візитка з порівняно невеликою кількістю стилів. Будь-які хоч трохи складні правки в зв'язку зі змінами дизайну або з появою нових сторінок призводять до довгого рефакторингу, в самому запущеному випадку - до дублювання стилів. При цьому постійно присутній ризик що-небудь зламати в самому несподіваному місці.

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

Чому так відбувається? На початковому етапі, коли розробники отримують макети, вони створюють структурy Ассет, в яку досить добре вписується весь існуючий дизайн. Проблема народжується саме в цей момент, тому що поточний дизайн - це вершина айсберга в порівнянні з майбутнім дизайном. Можна уявити варіанти рішень, що приходять в голову на даному етапі. У нас всього дві сторінки - можна помістити всі стилі в один файл. Або ще. У нас десять сторінок і всі вони несхожі, тому не варто морочитися виділенням компонент всередині сторінок і винесенням їх в окремі файли, простіше створити по файлу стилів на кожну сторінку. Ідеї ​​виглядають цілком здоровими, враховуючи поточні аспекти дизайну. Досвідчені розробники навіть можуть зробити деякі припущення про те, як зміниться дизайн в найближчому майбутньому, але мало хто готовий до того, на що перетвориться проект через, скажімо, п'ять років.

CSS має ряд недоліків, які призводять до перелічених вище проблем. У нещодавній доповіді ( React: CSS in JS ), Яке породило безліч дискусій в середовищі фронтенд розробників, один із співробітників Facebook озвучив проблеми з масштабуванням CSS. Серед них використання глобального простору імен, видалення мертвого коду, ізоляція і т. Д. В результаті він запропонував зберігати стилі в JavaScript. Цікаве, але досить радикальне рішення, не завжди прийнятне до звичайних сайтах, сторінки яких Рендер на сервері. Багато компаній, не тільки Facebook, намагаються вирішити проблему масштабування CSS. Тому на сьогоднішній день існує безліч підходів до визначення стилів. Одна з найбільш цікавих методологій народилася в Yandex.

БЕМ (Блок Елемент Модифікатор) - методологія, яка надає рішення по створенню архітектури проекту. Це комплексне рішення, яке диктує не тільки структуру CSS, але і структуру шаблонів і скриптів. Багатий інструментарій для автоматичної генерації коду, створений розробниками Yandex, також допомагає організувати процес розробки. Нам зараз інтресних та частина, яка стосується стилів. Благо методологія досить гнучка і її розробники заохочують користувачів робити експерименти і брати тільки ті її частини, які допоможуть в розробці їх проектів.

Пройдемося трохи по понятіям. Блок - незалежний компонент сторінки, який інкапсулює в собі поведінку (JavaScript) і зовнішній вигляд (CSS). Завдяки незалежності блоку можливо його повторне використання в будь-якому місці сторінки. Шлях руху - блоку, яка не може використовуватися у відриві від нього. Модифікатор - сутність, яка зраджує зовнішній вигляд блоку або елементу в залежності від стану або вимог дизайну. При бажанні можна використовувати кілька блоків на одному HTML елементі. Такий спосіб в термінології БЕМ має назву Мікс.

Тепер можна повернутися до наших проблем і подивитися яке рішення нам пропонує БЕМ. Перша проблема - це глобальний простір імен. Нехай у нас є навігаційне меню.

<Ul class = "toolbar"> <li class = "item edit"> <a href = "/ edit"> Edit </ a> </ li> <li class = "item delete"> <a href = "/ delete "> Delete </ a> </ li> </ ul>

І стилі до нього

.item {display: inline - block; padding: 5px 10px; margin: 0 10px; } .Edit {padding-left: 20px; background: url ( 'edit-icon.png'); }

Якщо раптом на сторінці з'явиться інший компонент, що містить елемент списку (item), або якийсь елемент пов'язаний з редактірваніем (edit), то нові стилі вплинуть на вже існуючі.

<Div class = "user-profile"> <span class = "email"> [email protected] </ span> <a class= "edit"> Change email </a> </ div>. edit {background: url ( 'edit-email-icon.png'); }

У цьому прикладі проблема вирішується виділенням двох компонент. toolbar і user-profile - імена цих компонент в глобальному просторі імен. Далі ми визначаємо стилі внутрішніх елементів в межах цих компонент.

. toolbar. edit {...}. toolbar. item {...}. user - profile. edit {...}

Але таке рішення дуже погано масштабується. Якщо уявити собі більш складні компоненти (page, company-preview, user-post), то ми отримаємо ту ж саму проблему, але вже в межах одного компонента. Уточнення селектор, наприклад .page> .edit - дуже погана ідея, тому що створює зв'язаність між HTML шаблонами і поданням. Змінюючи шаблон, нам потрібно буде поміняти і стилі теж. Додатково до всього додаючи клас до селектору ми міняємо його специфічність, а це в свою чергу ускладнює перевизначення CSS правил для елемента. В інших місцях з'являються класи для збільшення специфічності, починається гонка селекторів і головний біль для верстальника.

БЕМ пропонує відмовитися від каськадності, тим самим однозначно визначаючи елемент.

<Ul class = "toolbar"> <li class = "toolbar__item toolbar__item_edit"> <a href = "/ edit"> Edit </ a> </ li> <li class = "toolbar__item toolbar__item_delete"> <a href = "/ delete "> Delete </ a> </ li> </ ul>. toolbar {...}. toolbar__item {...}. toolbar__item_edit {...}. toolbar__item_delete {...}

Вкладені елементи, що належать блоку, будуть використовувати це ім'я блоку toolbar як префікс. __ служить роздільником між блоком і елементом, а _ - роздільником між БЕМ-сутністю (в даному випадку елементом) і модифікатором. Тут toolbar__item_edit і toolbar__item_edit - це модифікатори елемента toolbar__item.

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

Залишилося поговорити про самодисципліну. Щоб не звести нанівець отримані приимущества потрібно дотримуватись деяких правил і рекомендаціям. Можна тримати в голові наступний чекліст.

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

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

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

<Header class = "header"> <div class = "header__toolbar"> <ul class = "toolbar"> <li class = "toolbar__item toolbar__item_edit"> <a href = "/ edit"> Edit </ a> </ li > <li class = "toolbar__item toolbar__item_delete"> <a href = "/ delete"> Delete </ a> </ li> </ ul> </ div> </ header> .header {...} .header__toolbar { float: right; width: 50%; }

CSS код блоку toolbar при цьому залишається незмінним.

Ще один непусте питання. В якому випадку створювати блок, в якому - елемент? Якщо фрагмент коду не залежить від інших компонент сторінки, то необхідно створювати блок, якщо ж поза контекстом батька він не має сенсу, то створюється елемент.

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

Розібравшись з вимогами ми з'ясували, що перший елемент тулбару - це текстова інформація про поточну сторінку. Наступні елементи - це кнопки, при цьому деякі з них можуть знаходиться у включеному стані. Останні два елементи тулбару завжди повинні знаходиться праворуч, тобто між ними і іншими елементами стоятиме роздільник. Спробуємо написати розмітку для цього блоку.

<Div class = "toolbar"> <div class = "toolbar__item toolbar__text"> android </ div> <a class= "toolbar__item toolbar__button"> ▼ </a> <a class= "toolbar__item toolbar__button"> ✓ Mark as read < / a> <a class= "toolbar__item toolbar__button toolbar__button_toggled"> ∀ View all </a> <a class= "toolbar__item toolbar__button"> ↑ </a> <a class= "toolbar__item toolbar__button"> ↓ </a> <div class = "toolbar__item toolbar__spacer"> </ div> <a class= "toolbar__item toolbar__button"> <img class = "toolbar__icon" alt = "errors" src = "/errors.svg" /> </a> <a class = "toolbar__item toolbar__button"> <img class = "toolbar__icon" alt = "avatar" src = "/avatar.jpeg" /> <span class = "toolbar__text"> dra1n </ span> </a> </ div>

Тут для того щоб не дублювати загальні стилі для кожного елемента блоку toolbar ми виділяємо елемент toolbar__item і використовуємо мікс з іншими елементами: toolbar__button, toolbar__spacer і toolbar__text. Деякі елементи є вкладеними, але при цьому внутрішня структура блоку все ще залишається плоскою, тобто не порушується правило про те, що не можна створювати елементи елеметов. І ще одне - всі елементи, що вимагають стилізації мають класи, в тому числі і img. Посилання на реалізацію зі стилями.

На закінчення можна сказати що дотримання принципів БЕМ дозволяє отримати той самий підтримуваний і масштабований CSS, що збільшить швидкість розробки і спростить розуміння коду новими розробниками. При цьому використовується тільки угода про іменування елементів без будь-якого додаткового інструментарію. Для того щоб застосувати ідеї БЕМ в своєму проекті нам досить того стека технологій який там вже напевно є.

Чому так відбувається?
В якому випадку створювати блок, в якому - елемент?