x
 
Ксения
20 апреля 2017
Советы почтой каждую неделю
Пожалуйста, получите наше письмо, чтобы подтвердить свой адрес:
Вы подписаны на «Советы за неделю»:

Руст, какую систему именования выбрать для небольшого проекта — БЭМ, SMACSS или что-то другое?

(Продолжение)


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

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

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

Модули

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

В файловой системе это выглядит так:

modules/
├─ image/
│  ├─ image.css
│  ├─ image.js
│  └─ image.html
├─ spread/
│  ├─ spread.css
│  └─ …
├─ caption/
│  ├─ caption.css
│  └─ …
└─ …
Все файлы, связанные с модулем, лежат в одной директории

Название модуля отражает его предназначение. Это название используется и в именах файлов, и в качестве класса для описания стилей. Благодаря этому, зная только класс, легко найти файл с его описанием.

<div class="project">
  <h2 class="project-title">Типографика и вёсртка</h2>
  <div class="project-description">…</div>
</div>
Если встретим класс project в разметке, то понятно, что его описание в project.css

Элементы

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

Для отражения связи элемента с модулем мы строим классы элементов по схеме «модуль-элемент». По сути, модуль задаёт пространство имён для своих элементов.

<div class="project">
  <h2 class="project-title">Типографика и вёсртка</h2>
  <div class="project-description">…</div>
</div>
У проекта есть заголовок и описание

Мы не вкладываем элемент в элемент, поскольку это затрудняет их перемещение внутри модуля:

<div class="project">
  <div class="project-date">
    <span class="project-date-day">17</span>
    <span class="project-date-month">сентября</span>
  </div>
</div>
Плохо: элемент относится к другому элементу. Если мы захотим перенести месяц в другое место модуля, придётся переименовывать класс.
<div class="project">
  <div class="project-date">
    <span class="project-day">17</span>
    <span class="project-month">сентября</span>
  </div>
</div>
Хорошо: элемент относится напрямую к модулю…
<div class="project">
  <div class="project-month">Сентябрь</div>
</div>
…поэтому структуру легко изменить без переименований

Модификаторы

Для изменения поведения модуля или элемента мы используем модификаторы. Название модификатора отражает смысл изменения. Чтобы визуально выделить модификаторы на фоне других классов, мы используем префикс с двойным подчёркиванием. Например: is__selected, has__title, in__view.

.project.is__featured {
  font-size: 27px;
}

.menu-item.is__active {
  background: black;
  color: white;
}
Хорошо: названия сообщают об изменениях

Глобальные модификаторы запрещены:

.is__selected {
  background: red;
}
Плохо: в другом модуле может использоваться модификатор с таким же именем
.menu-item.is__selected {
  background: red;
}
Хорошо: модифицируем пункт меню

Запрещается стилизация модификаторами от родительских модулей.

<!-- header.html -->
<div class="header is__inverted">
  <div class="searchForm has__error is__dirty">
    <input type="text" class="searchForm-input">
  </div>
</div>
/* header.css */
.header.is__inverted .searchForm {
  background: white;
}

/* searchForm.css */
.searchForm.has__error.is__dirty {
  background: red;
}
Плохо: мы описываем форму в другом файле. Непонятно, какой стиль применится в итоге.
/* searchForm.css */
.header.is__inverted .searchForm {
  background: white;
}

.searchForm.has__error.is__dirty {
  background: red;
}

Плохо: все стили в одном файле, но мы ссылаемся на внешний модуль. Изменение во внешнем модуле может повлиять на нашу форму.
<!-- header.html -->
<div class="header is__inverted">
  <div class="searchForm has__error is__dirty is__inverted">
    <input type="text" class="searchForm-input">
  </div>
</div>
/* searchForm.css */
.searchForm.is__inverted {
  background: white;
}

.searchForm.has__error.is__dirty {
  background: red;
}
Хорошо: мы добавили модификатор самой форме и используем его для стилизции. Форма полностью независима от внешних модулей

При этом мы разрешаем изменение элемента с помощью модификатора модуля. Стили модуля хранятся в одном файле и предсказать результат возможно:

.form.is__invalid .form-input {
  border-color: red;
  color: red;
}
Можно: все модификации в одном месте

Составные имена классов

Если для описания модуля или элемента требуется больше одного слова, то используем кэмелКейс:

.newsItem {
  width: 30%;
  margin-bottom: 40px;
}

.newsItem-subTitle {
  font-size: .9em;
  font-weight: bold;
}

Проблемы

К сожалению, все проблемы этот набор правил не решает. Самая острая сейчас — смешение ролей: один и тот же объект одновременно быть самостоятельным модулем и элементом другого модуля:

<div class="article">
  <h1 class="article-title">Интерфейс — зло</h1>
  <div class="article-lead lead">
    Ни одна компания, продающая дизайн интерфейсов, не признается,
    что интерфейс — зло. Они никогда не скажут, что он мешает
    клиенту зарабатывать.
  </div>
</div>

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

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

О модульной системе в «Техноведре»
P. S. Это был совет о веб-разработке. Хотите знать всё о коде, тестах, фронтенд-разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.
Вёрстка и прототипирование — дисциплина Школы дизайнеров. Набор весной. Оставьте почту, и мы напишем вам, когда откроется следующий набор.
 

Поделиться
Отправить

Комментарии

Пётр Сохин
20 апреля 2017

> Самая острая сейчас — смешение ролей: один и тот же объект одновременно быть самостоятельным модулем и элементом другого модуля
> Проблема в том, что определения lead и article-lead будут находиться в разных файлах и непонятно, какое из них применится последним.

1. Сборщик пакетов
lead — это модуль. article-lead — элемент, который является модулем lead или состоит из модуля lead. Может быть, элемент содержит модификации модуля.

Налицо зависимость article-lead от lead, значит, должен применяться последним. Об этом заботится сборщик пакетов, если он умеет обрабатывать зависимости.

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

/* lead.css */
.lead {….}

/* article-lead.css */
.article-lead.lead {….}

Этим: 
— Явно обозначили зависимость article-lead от lead. Видно, что стиль элемента не с неба свалился, а переопределяет аналогичный стиль модуля. 

— Даём гарантию переопределения за счёт веса селектора.

Ксения Пелипенко
20 апреля 2017

1. Думаю, что нет необходимости в конструкциях вида:

.searchForm.has__error.is__dirty {
  background: red;
}

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

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

.searchForm.has__error {
  background: red;
}

.searchForm.is__dirty {
  border: 1px solid #000000;
}


2. Проблема в том, что определения lead и article-lead будут находиться в разных файлах и непонятно, какое из них применится последним.

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

.lead {
  font-size: 18px;
  color: #000080;
}

.article-lead {
  margin: 20px 0 30px 10px;
}

Дима Шишкин
20 апреля 2017

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

Если нужно использовать какие-то особенности, относящиеся к родительскому модулю, то я использую модификатор.

Например:

<div class="article">
  <h1 class="article-title">Интерфейс — зло</h1>
  <div class="lead lead--article">
    Ни одна компания, продающая дизайн интерфейсов, не признается, что интерфейс — зло. Они никогда не скажут, что он мешает
клиенту зарабатывать.
  </div>
</div>

Сережа Громков
21 апреля 2017

Предлагаю запретить смешение ролей.

Плохо:
<div class="article-lead lead"></div>

Хорошо:
<div class="article-lead">
  <div class="lead lead__article"></div>
</div>

Если я правильно понял ваш принцип модификаторов, то вот так тоже хорошо:
<div class="article-lead">
  <div class="lead in__article"></div>
</div>

.article-lead будет «подставкой» для вашего .lead, и будет отвечать за его позиционирование. Сам же .lead, вооружившись модификатором .lead__article, будет отвечать за внешний вид.

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

Пример с несколькими вложенными блоками:
<div class="header">
  <div class="header-logo">
    <a href="" class="logo"></a>
  </div>
  <div class="header-menu">
    <div class="menu menu__header"></div>
  </div>
  <div class="header-search">
    <div class="search search__header">
      <div class="search-button">
        <a class="button button__search">Искать</a>
      </div>
    </div>
  </div>
</div>

Александр Бизиков
22 апреля 2017

Руст, расскажите подробнее, чем отличается элемент .project-title от модификатора has__title?

Владимир Кузнецов
6 мая 2017

В том, что на одном <div> висят два класса (lead и article-lead) нет проблемы, так как эти классы служат различным идеологическим целям, скорее всего.

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

acticle-lead — это элемент блока article.

Следуя правилу «один блок не может влиять на внеший вид другого блока», можно предположить, что назначение класса acticle-lead исключительно в том, чтобы задать позиционирование блока lead. Вот и разрешилось противоречие.

Тимур Арефьев
12 июня 2017

1. Зачем использовать двойное подчёркивание, если одинарное нигде не используется?

2. Раз мы используем в качестве индикатора подчёркивание, использовать дополнительные слова вроде is и has не очень красиво.

3. БЭМ предлагает повторять название класса, дописывая к нему название модификатора не просто так. У элемента могут быть два класса, обозначающие его отношение к разным блокам, как у вас в последнем примере:

<div class="article">
  <h1 class="article-title">Интерфейс — зло</h1>
  <div class="article-lead lead">
    Ни одна компания, продающая дизайн интерфейсов, не признается, что интерфейс — зло. Они никогда не скажут, что он мешает клиенту зарабатывать.
  </div>
</div>

Если мы допишем к article-lead модификатор is__active, мы получим проблему: непонятно, стал активным класс article-lead или lead.

Лучше делать так: article-lead_active или lead_active в зависимости от необходимости.

4. При использовании скриптов для работы с блоками лучше не использовать классы, описывающие внешний вид блока. Я обычно добавляю специальные классы только для js используя префикс j-. Здесь возникает одна проблема: мы вынуждены работать не только с j- классами, чтобы модифицировать внешний вид блоков, поэтому можно использовать j- классы только для событий.

Тимур Арефьев
12 июня 2017

И ещё у меня есть вопрос про вложенность.

Хорошо, когда у нас есть один элемент, вложенный в другой, по типу примера:

<div class="project">
  <div class="project-date">
    <span class="project-day">17</span>
    <span class="project-month">сентября</span>
  </div>
</div>

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

<div class="project">
  <div class="project-left">
    <span class="project-item">Предмет</span>
    <span class="project-item">Предмет</span>
  </div>
  <div class="project-right">
    <span class="project-item">Предмет в правой колонке</span>
    <span class="project-item">Предмет в правой колонке</span>
  </div>
</div>

Мы будем писать стили как ниже?

.project .project-right .project-item


Цель рубрики — обсуждение вопросов дизайна всех видов, текста в дизайне и взаимоотношений дизайнеров с клиентами.

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

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

Вот такой веб 2.0.

EM или REM? 6 Можно ли достичь эффекта переливания без встраивания видео на сайт? 1




Недавно всплыло

1 Как вы верифицируете оценку сроков от сотрудника? 1 1 7