Блок — это независимая сущность со своим собственным смыслом, он представляет на странице отдельный кирпичик интерфейса.
Например, блоками могут быть:
- заголовок
- кнопка
- навигационное меню
Чтобы задать блок, нужно придумать ему имя и определить его назначение. В интерфейсе может одновременно присутствовать несколько экземпляров одного и того же блока (например, разные кнопки или несколько меню).
Элемент — это часть блока, связанная с ним и по смыслу, и функционально. Элемент не существует и не используется без блока, к которому относится. Но не у всех блоков должны быть элементы.
Примеры элементов:
- навигационное меню (блок), содержащее пункты меню (элементы);
- таблица (блок), внутри которой строки, ячейки и заголовки (элементы).
У элементов тоже есть названия. Если внутри блока несколько одинаковых элементов, то они идут под одним именем (например, ячейки таблицы или пункты списка). Элементы определяются по смыслу, а не исходя из HTML-верстки блока; так, отдельный элемент может быть представлен сложной HTML-структурой.
Модификаторы — флаги у блоков или элементов, они определяют вариации или состояния. У сущности может быть несколько модификаторов, если эти модификаторы описывают разные вещи.
- меню с модификатором, чтобы выглядеть различно в шапке и подвале
- поле поиска с кнопкой в виде иконки
Существует несколько вариаций использования именований. Мы используем следующую:
.block {}
.block__element {}
.block--modifier {}
.block__element--modifier {}
- имена блоков, элементов и модификаторов должны быть недлинными и семантически значимыми;
- используются только маленькие латинские буквы, символ тире и цифры;
- символ подчёркивания используется как специальный разделитель.
Все файлы проекта лежат в директории scss
(придумать новое название и переписать), которая является корневой для всех подпроектов. Каждый проект может иметь произвольное количество подпроектов. Каждый подпроект это набор файлов scss, изображений и скриптов, сгруппированных по слоям и блокам, компилируемых в конечном счете в свой отдельный набор css-файлов, которые могут подключаться независимо друг от друга.
Примером подпроекта могут быть основные стили вебсайта, css-файлы которого подключаются на всех страницах сайта, пример другого подпроекта - промо-раздел вебсайта, css-файлы которого подключаются только на страницах этого промо-раздела.
Для создания подпроекта, в корневой директории проектов создается файл с именем подпроекта и расширением scss
и директория с таким же именем. Все файлы, относящиеся к данному подпроекту размещаются внутри соответствующей ему директории.
Подпроекты структурируются согласно методологии ITCSS (http://itcss.io/).
Видео и слайды, отлично раскрывающие тему:
- https://speakerdeck.com/dafed/managing-css-projects-with-itcss
- https://www.youtube.com/watch?v=1OKZOV-iLj4
Согласно этой методологии, каждая директория подроекта подразделяется на еще ряд директорий, называемых слоями (layers). Каждый слой подключаются в определенной последовательности, указанной ниже. С каждым последующем слоем специфичность стилей возрастает, при этом они затрагивают все меньшее количество DOM-узлов.
В большинстве подпроектов используются следующие слои, но их количество может менться от проекта к проекту.
- settings
- tools
- generic
- base
- objects
- components
- trumps
Файлы с глобальными переменными и настройками, затрагивающими весь подпроект, хранятся в этой директории.
Файлы с scss @mixin
s и @function
s, глобально используемые в подпроекте.
Обычно все подпроекты используют один и тот же общий набор инструментов, поэтому почти всегда директория tools
вынесена в корневую папку на один уровень с папками подпроектов и имеет название _tools
. Нижнее подчеркивание используется, чтобы избежать ошибки и не принять эту папку за название подпроекта. При этом все подпроекты ссылаются на одну и туже корневую папку _tools
.
Нулевые стили проекта, глобальные резеты, normilize.css, box-sizing и т.п.
HTML теги без классов, очень базовые стили для ссылок, списков, заголовоков и т.п. Это последний слой, где могут применяться селекторы по типу (type selectors), во всех последующих слоях в идеальном случае только селекторы по классам.
Максимально абстрактные, проектонезависимые, переносимые из проекта в проект (в идеале) блоки. Примером таких блоков являются OSCSS или первый базовый слой MCSS (http://operatino.github.io/MCSS/#1).
Основное правило — абстрактность как в названиях, так и в разметке. Для именования используются только классы.
Названия классов этого слоя не должны выглядеть чужеродно в любом месте на сайте. Блоки модуля должны иметь стиль по умолчанию, но должны оставаться простыми и легко модифицируемыми под задачи различных проектных модулей.
Этот слой включает в себя изолированные, проектные модули, из которых в дальнейшем собирается страница. Аналогично проектному слою MCSS (http://operatino.github.io/MCSS/#2). Для именования продолжают использоваться только классы.
Каждый модуль должен быть максимально изолирован и существовать как отдельная, независимая единица интерфейса. Изолированность модулей позволяет легко работать с их стилями, не боясь задеть отдельные части веб сайта.
Слой для косметических классов, хелперов. Аналогичен косметическому слою MCSS (http://operatino.github.io/MCSS/#3). Очень специфичные классы, затрагивающие крайне малое количество элементов за один раз. Возможно и поощряется использование !important
в свойствах данных классов.
Позволяет использовать простые классы, для редких, непроектных ситуаций, например нестандартный отступ или цвет ссылки.
Для описанных выше слоев, основной файл подпроекта website
может иметь следующую структуру:
@import "website/settings/*";
@import "_tools/*";
@import "website/generic/*";
@import "website/base/*";
@import "website/objects/*";
@import "website/components/*";
@import "website/trumps/*";
Слои подключаются в строго определенной последовательности, при этом порядок подключения файлов внутри слоя не имеет и не должен иметь значения.
В каждом конкретном подпроекте набор слоев может меняться. Новые слои могут быть добавлены при необходимости. Например, можно добавить слой pages
для блоков, специфичных для отдельных страниц вебсайта. При подключении новых слоев необходимо следовать правилу, что в каждом последующем слое правила имеют более высокую специфичность, но затрагивают меньшее количество DOM-элементов. В случае примера pages
, такой слой следовало бы подключать после слоя base
, перед слоем objects
.
Для именования любых сущностей: файлов scss, изображений всех форматов, шаблонов html, BEM-блоков и пр. используется kebab-case
. Это означает, что в именах используются только латинские буквы в нижнем регистре, для разделения слов используется символ минус (-).
Символ нижнего подчеркивания (_) может быть использован только как первый символ в имени scss-файла или директории блока. В этом случае scss-файл не компилируется в самостоятельный css и может быть использован только через @include
из других файлов, либо будет полностью проигнорирован. Папка со знаком подчеркивания вначале имени игнорируется при сборке стилей и используется, чтобы исключить какие-то блоки из css, не удаляя их исходные файлы.
Каждый блок в отдельном файле.
Стили каждого модуля должны храниться рядом друг с другом, можно их выделять как в закомментированные секции, так и в отдельные файлы:
.module { }
.module__list { }
.module-list--modificator { }
Селекторы, модифицируемые каскадом, должны так же храниться рядом с родительским классом:
.module { }
.module__list { }
.module__list .other-module { }
Исключением могут быть только классы из слоя контекста:
.module { }
.module__list { }
.touch .module__list { }
.lt-ie9 .module__list { }
Модификаторы стоит прописывать после основной группы модифицируемых элементов, чтобы не создавать путаницу, которая обязательно возникнет при появлении нескольких модификаций.
.block {}
.block__element {}
.block__sub-element {}
.block--modifier {
.block_element {}
.block__sub-element {}
}
Такая организация стилей упрощает дальнейший рефакторинг кода и позволяет непосвященному разработчику быстро разобраться во всех стилях модуля и быстро приступить к работе с ним.
При чистке кода просто избавляться от старых модулей и от всех его зависимостей.
Вся подготовка и сборка файлов фронтенда происходит при помощи gulp-тасков. При помощи gulp выполняются следующие процедуры:
- компиляция scss в css
- создание отдельных css-файлов для разных локализаций
- создание rtl-версии стилей
- оптимизация изображений
- сборка png и svg спрайтов
Консольная команда gulp
.
Данная задача выполняет:
- Компиляцию SCSS при помощи node-sass
- Группировку media-запросов
- Добавление вендорных префиксов при помощи autoprefixer.
- Создание отдельных css-файлов для разных локализаций
- Создание rtl-версии стилей
Консольная команда gulp images
.
Данная команда оптимизирует изображения форматов png
, jpg
, gif
, svg
. Оптимизированные копии исходных изображений сохранятся в отдельной директории. При этом задача обрабатывает изображение только в том случае, если изображение в целевой директории отсутствует или является более старым, чем изображение в исходной директории.
Данная задача игнорирует png и svg файлы, предназначенные для объединения в спрайты.
При удалении изображения в иходной директории, копия в целевой директории сохраняется и требует ручного удаления.
Конольная команда gulp sprites
.
Данная команда создает спрайты из png-изображений и соответвущие им scss-файлы. Для генерации спрайта необходимо соблюсти ряд правил в именовании директории.
Обработаны будут только директории, имя которых начинается с префикса, указанного в файле конфигурации (по умолчанию sprite-
). Каждая такая директория должна содержать только файлы в формате png. Для каждой директории будет создан свой спрайт объединенных изображений и scss-файл стилей.
Если название директори заканчивается на @2x
, то для нее будет создано два спрайта: для обычных экранов и для экранов с двойной плотностью пикселов. В этом случае папка должна содержать изображения вдвое большего разрешения. Спрайт с уменьшенными изображениями будет создан автоматически на основе удвоенных.
Пример правильных имен директорий: sprite-logos
, sprite-promotion@2x
.
Конольная команда gulp svg
.
Данная команда создает html-файл со спрайтом из svg-файлов. Подробнее о технике https://css-tricks.com/svg-sprites-use-better-icon-fonts/
При этом каждый подпроект имеет свой отдельный svg-спрайт. Для того, чтобы данное svg-изображение было объединено в спрайт, имя файла должно начинаться с префикса, указанного в файле конфигурации (по умолчанию sprite-
). При этом префикс в имени в итоговом файле будет удален и ссылаться на изображения в шаблонах нужно по их исходному имени.
Пример правильного имени файла: sprite-facebook.svg
. Ссылка на это изображение будет #facebook
.
Svg-изображения являются векторным форматом, поэтому их использование весьма предпочтительно, т.к. это упрощает поддержку для устройств с экранами повышенной плотности (ретина). Хорошим сферой применения svg являются иконки.
Существует ряд методов и инструментов, которые позволяют значительно упростить применение svg в проекте.
Подробнее о технике https://css-tricks.com/svg-sprites-use-better-icon-fonts/
(ссылку на документацию django template tags)
В папке templatetags
создать файл svg_icon.py
:
from django.template import Library
register = Library()
@register.simple_tag
def svg_icon( id ):
return '<svg class="ico _%s" role="img"><use xlink:href="#%s"></use></svg>' % (id, id)
Разместить svg-файлы в одной директории с блоком, который их использует. Если блок использует те же самые svg-файлы, которые использует уже существующий в проекте блок, эти файлы должны быть скопированы в директорию нового блока и существовать независимо.
Такая система позволяет изолировать блоки друг от друга и в случае, когда блок перестает использоваться, удалить все используемые им файлы вместе с блоком, не опасаясь удалить файлы, которые могут быть использованы другими блоками.
Добавить префикс sprite-
(может быть изменен в конфигурации gulp) в имени каждого из svg-файлов. Имя файла без префикса и расширения svg является его идентификатором, который используется при вставке изображения в код шаблона.
Выполнить консольную команду gulp svg
.
Для каждого подпроекта генерируется отдельный файл спрайтов. Файл спрайта сохраняется в папке шаблонов, указанной в конфигурации gulp. Файл имеет расширение html и содержит в имени название подпроекта.
Пример имени: svg-sprite-website.html
.
Подключить сгенерированный спрайт в шаблоне
<div style="display:none">{% include "svg-sprite-website.html" %}</div>
Загрузить templatetag в шаблоне {% load svg_icon %}
. Затем использовать подлключенный templatetag {% svg_icon "facebook" %}
, где facebook
это идентификаторор изображения.
Масштабирование наоборот: БЭМ-методология Яндекса на небольших проектах by Максим Ширшин