Как я организую архитектуру CSS в больших проектах с помощью UFOCSS – часть 1

Как я организую архитектуру CSS в больших проектах с помощью UFOCSS

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

Учитывая эти проблемы, я начал использовать самые распространенные системы: OOCSS, SMACSS, ITCSS, ACSS и БЭМ — с их помощью создается приемлемая архитектура CSS.

На самом деле, я ценю все CSS архитектуры по некоторым причинам и не хочу искать идеальную. Я пришел к выводу, что лучшее то решение, которое реально работает для всех людей, работающих над ним. Решению далеко до human-friendly, если в нем есть слабости. Иногда мы легко теряемся в техниках и забываем, что за каждой строкой кода стоит человек. То, как мы пишем и организуем код, должно быть важным средством передачи полезной информации другим разработчикам, а не только техническим решением проблемы.

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

Избегайте избыточной сложности

Объясняйте и делайте синтаксис класса легкочитаемым

Следуйте порядку и соблюдайте чистоту

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

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

Как расшифровывается UFOCSS?

Несмотря на схожесть названия, UFOCSS расшифровывается как User Friendly Oriented CSS. Это не методология пришельцев и не новый способ представления масштабируемой и модульной CSS архитектуры. Это попытка сосредоточиться на самой «человеческой» части того, что мы уже ценим. К чему это ведет? Давайте разбираться!

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

В общем, каталог стилей может быть таким:

Как видите, код разбит на 2 главные папки: abstracts и modules. Это помогает поддерживать структуру папок в чистоте и порядке, не создавая дополнительные папки, которые не так уж и нужны.

Названия папок можете выбрать какие угодно (т.е. tools для abstracts и patterns или layers для modules). Я использую соглашение о сортировке папок в алфавитном и числовом порядке. Такое соглашение действительно полезно при работе с языками, в основе которых лежит каскадирование и принципы наследования.

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

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

вы не можете делать верхние слои, если нет первого

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

если в нижнем слое использовать много шоколадной крошки, то все остальные слои не так ощутимы, шоколад будет перебивать остальные вкусы! (специфичность)

Папка abstract: здесь живут инструменты

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

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

Abstracts – это ингредиенты и инструменты, необходимые для старта и ускорения разработки, как переменные, функции и миксины. Они не влияют на внешний вид и вкус пирога, но определяют способ создания и поддержки. Никому они не нужны кроме вас и вашей команды!

Все файлы abstracts говорят сами за себя. Просто учтите, что я беру все, что нужно широко использовать из файла _variables – цвета, шрифты, grid и z-index. Я считаю, так всем легче понять, какие инструменты нам нужны.

Например, файл z-index управляет вертикальным порядком элементов. Хорошая практика управления z-index в сложных макетах – установка нескольких Sass списков, в которых описано, в каком порядке мы хотим выводить элементы, от низшего к высшему.

Учтите, что для создания нового контекста стека необходимо модальное окно. Можно просто создать новый Sass список в файле z-index:

$modal-elements: fields, form-controls, alerts, autocomplete-dropdown;

Этот Sass список – просто инструмент, помогающий безопасно управлять порядком стека элементов, он не генерирует CSS правила.

Использовать этот инструмент, получить значение z-index и присвоить его всем элементам можно через Sass функцию index() внутри корректного файла module, как показано ниже:

.modal-alerts { z-index: index($modal-elements, alerts); ....
}

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

Папка modules: здесь живут слои

Принцип действия в соответствии с уровнями возрастающей сложности и специфичности позволяет нам добавить второй кирпичик в стену CSS – это определение слоев CSS.

Слой abstracts, рассмотренный ранее, можно считать нулевым. После создания всех необходимых инструментов и «ингредиентов» можно начать писать стили нашего проекта через прогрессивные слои абстракции (о них ниже).

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

Далее мы будем использовать специальные префиксы в именах файлов, в зависимости от их CSS категории.

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

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

Все точно знают, для чего используется файл

Используемые префиксы отсортированы в алфавитном порядке. То есть они показаны в том порядке, в котором импортированы, что подчиняется принципу специфичности (сначала идет основа, затем макеты, объекты, утилиты и вендорные слои)

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

Я пытаюсь упростить структуру папок, уменьшив и переименовав слои, представленные ITCSS: Settings, Tools, Generic, Elements, Objects, Components and Trumps.

Мне удобно сгруппировать переменные, функции и миксины в один слой Abstracts, а не разбивать их между Settings и Tools

Generic и Elements можно объединить в слой Base, так как оба включают самые базовые и низко специфичные классы

Я предпочитаю переименовывать слой Objects в Layout – самый понятный слой, так как он используется для некосметических классов

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

Trumps используется для утилит, поэтому я просто назову слой Utility

Если есть необходимость, вам никто не запрещает добавить дополнительный слой типа templates. Этот слой можно использовать для правил, которые уникально применяются в каких-то шаблонах. Я бы в таком случае использовал префикс _t_. Или же можно использовать следующие слои.

Слой bases

Это первый слой, который генерирует реальные CSS правила. Здесь располагаются стили reset или normalize, глобальные правила типа box-sizing и стили для текстовых HTML тегов. Я также думаю, что на этом уровне моно разместить некоторые хелпер-классы, строго относящиеся к HTML тегам типа .h1, .small, .mark – это пригодится, когда между стилем и семантикой нет соответствия.

Здесь можно разместить правила шрифтов, как показано ниже:

h1, .h1-like { font: { family: $font-primary; weight: bold; size: 2rem; } text-transform: uppercase; ......
}

Я бы включил эти правила в файл _b_typography.scss. Префикс _b_ расшифровывается как base.

Слой layouts

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

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

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

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

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

Представьте, что ваш клиент хочет разместить до 6 блоков в строке. Это никак не относится к дизайну всех элементов, поэтому можно создать файл _l_columns.scss, который работает только с размещением определенных блоковых элементов.

$min-cols: 2;
$max-cols: 6;
.l_columns-1 { display: grid; grid-row-gap: 30px; grid-column-gap: 30px;
}
@for $i from $min-cols through $max-cols { .l_columns-#{$i} { @extend .l_columns-1; grid-template-columns: repeat($i, 1fr); }
}

Так клиент может просто редактировать класс макета родительского контейнера, чтобы изменить макет. Если добавить класс l_columns-3, позиция блоков автоматически изменится. Можете поиграться с моим демо: Dynamic layout classes using CSS grid.

Как видите, эти классы макета никак не связаны с косметическими классами, которые мы будем обрабатывать в слое Object. Префикс _l_ расшифровывается как layout.

Слой objects

Здесь пройдет большая часть работы. Здесь можно собрать все UI объекты, которые можно принять, как атомы, молекулы и организмы (atomic дизайн) или элементы и блоки (БЭМ).

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

Вернемся к предыдущему примеру. Косметические стили для блоков типа объекта card-preview определяются в слое Object:

.card-preview { padding: 10px 25px; border: 1px solid $border-primary; text-align: center;
}

Эти CSS правила можно расположить в файле _o_cards.scss. Префикс _o_ расшифровывается как object.

Слой utilities

Здесь мы размещаем классы-утилиты типа .text-center, .font-small, .visually-hidden, bg-dark. Классы-утилиты применяют одно правило к элементу, поэтому мы должны гарантировать, что стили, заложенные в название класса, действительно применены. Это заставляет писать нас классы высокой специфичности. Именно поэтому здесь нужно применять !important.

Стили добавляются прямо в разметку, поэтому это не лучшее решение для первой стилизации объектов. Они могут помочь при редактировании. Здесь можно успешно применить принцип single responsibility – это дает редакторам шанс кастомизировть определенное свойство элемента. Префикс _u_ расшифровывается как utility.

Слой vendors

Здесь мы будем переписывать вендорные стили. Как и с классами-утилитами, эти классы имеют высокую специфичность. Поэтому мы помещаем их в конец стилей. Префикс _v_ расшифровывается как vendor.

В следующей статье я подробно расскажу о работе с:

Соглашением об именовании

Медиа запросами

Правилами состояний

А пока что, ждем комментариев от людей и зеленых человечков!

Автор: Francesca Giannino

Источник: https://medium.com/

Редакция: Команда webformyself.