Ситуация получилась следующая. Компания переделывала сайт и сказала, что необходимо сделать его максимально гибким, чтобы, во-первых, можно было дизайн обновлять просто, во-вторых, сделать ещё несколько подразделов на базе готового функционала (вроде как SaaS фишка для заказчиков).
(Часть 1)

Прилетает первый дизайн (пришлось сильно изменить, т.к. nda, все дела, но что-то похожее).
Что тут у нас интересное.
Есть секции, которые разбиты по принципу:
— Заголовок
— Контент
Заголовок всегда одинаковый.
Значит выносим заголовок и текст контента в типографию (Components/Typography, либо любое другое название, абы вам нравилось и семантически объясняло, что это).
Дальше куча всяких секций. На таком мелком рисунке не видно, но многие секции имеют одинаковые отступы, а у 50% ещё и высота одинаковая.
Получается нужна ещё сущность, которая задаёт одинаковые правила для секций, например, единые отступы и единую минимальную высоту. Я назвал этот тип сущностей containers. В частности, для секции BasicSection.
Получается, у нас уже есть Typography, в которой есть заголовок и Containers, где есть BasicSection.
Замечательно, создаём такие секции.


Наверное, немного сумбурно в стилях, да? Сейчас поясню.
Из предыдущей статьи мы помним, что у нас есть ритм в дизайне. Поэтому мы взяли 10px, как основу для всех-всех отступов на сайте.
Почему 10px? Потому что все отступы кратны 10ке. Отступы сбоку страницы – 90px, отступы между кнопочками, лейблами, формой – 10px либо 20px и тд.
Далее именно от 10px ($offset-basic) будут считаться все отступы. Сам же $offset-basic использоваться нигде не должен, т. к. является исключительно точкой отсчёта и не относится к семантике сайта.
Двигаемся дальше. Высота секции, по нашему мнению, равна высоте экрана (100vh). Вроде бы всё логично, но, когда я добавляю на сайт хедер, появляется полоса прокрутки. Связанно это с тем, что у нас блок 100vh + 150px (например, столько) высота header. Для того, чтобы не терять контекст, который мы только что обнаружили, предлагаю записать эту логику в переменную высоты секции.
Итого:

Почему это важно. Я часто видел записи в компоненте header, height: 150px, а в высоте секции height: 95vh. Вроде бы точно, но куда делись 5vh? Почему 150px = 5vh? И прочие вопросы.
Где? Ну вот например откройте сайт Forbes. Похожая логика магических чисел: 3%, 31.8%


Ну да, я понимаю, что, скорее всего, оставшийся блок занимает 97% и 68.2%, или не оставшийся блок, а два оставшихся блока. Хмм. Ладно, да, я не понимаю.
Думаю, на этом с формулой высоты мы закончили, едем дальше.

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

Надеюсь, вас не сильно удивят переменные ширины экранов…
Получилось, по-моему, читаемо.
Глобальные переменные из html подставляются в компонент. В будущем их можно переиспользовать в других компонентах, секциях (с другой высотой).
Смотрим на секции дальше.



Чётко выделяется отступ между элементами и отступ от заголовка. Следовательно, для label нам необходимо добавить отступ справа, равный одному $offset-basic
, а заголовку снизу – равный двум $offset-basic
.
Единственное, по поводу отступов прошу ознакомиться с данной статьёй, чтобы понимать, почему не стоит добавлять отступы слева и сверху.
Результат:

Я добавлю media queries, потому как у нас всё-таки вёрстка под разные устройства …

Если честно, уже слабо читаемо.
Да, если разнести по разным файлам, будет лучше.
Например, в main.scss объявлять глобальные переменные и использовать их в media queries внутри компонентов.

Media queries вынести в mixins.scss как что-то типа @include mobile
, @include tablet
.
Вот так
Получаем следующее:

В принципе неплохо. Ладно. Не неплохо. Терпимо. Но это будет для каждого элемента, каждого тега. Выглядит как очень странное дублирование кода…
Здесь мне очень хочется побеседовать о читаемости.
Давайте сравним 3 варианта кода, которые я часто встречаю, и тот, что был выше.



Ииии, «silver bullet», который я предлагаю. Вариант записи, к которому мы так долго шли! Выносим переменные, которые мы меняем из каждого компонента в один глобальный – html тег.
Как? Добавляем css-variables.

Теперь наши классы выглядят вот так. Все классы. Все секции, все заголовки, кнопочи, лейбочки – всё в проекте. Потому как они будут создаваться по подобию.
Как же выглядят media queries?

А вот так. Код стал разделён по файлам. Теперь в компонентах содержится только информация о типе отступа (его семантическое значение), а в файле variables.scss – информация о том, как отличаются отступы в зависимости от устройства.
Для продолжения статьи я сделал шаблон на базе create-react-app, чтобы вы могли самостоятельно следить за развитием событий, тыкая в код и пробуя своими руками, удобно вам или нет (ссылка).
Я добавил некоторые переменные, например, для типографии. Для понимания размера шрифта предлагаю ознакомиться с данной статьёй.
Дальше задачка посложнее прилетела – правки по дизайну. Ну, как сказать правки. Дизайн полностью изменился.
Некоторые секции возможно только переписать с нуля, некоторые похожи, но их нужно полностью модифицировать.

Было

Стало
Главная практически не изменилась – только отступ между секциями (от меню до заголовка) и цвета.
Зато сервисы полностью другие. Единственное, что похоже – структура карточек. И то не сильно.

Было

Стало
И думаете, это проблема? На изменение ушло минут 20 максимум.

Было

(зачёркнутое вынес в отдельный компонент)
Стало
Меняем две колонки на сложную систему колонок, как в grid template areas.
Т.к. у нас теперь два типа карточек – картинки и текст. Вынес их в отдельный компонент
Для отступов между карточками добавляем grid-gap, равный отступу между элементами.
Всё, можно кофе заварить. Даже структуру в jsx не пришлось переписывать (я вынес для красоты карточки в отдельный компонент, но там всё равно меньше 20 строк было…)
Выше я обещал, что дизайна будет три.


Подлетела белая версия…
Как мы видим – логика совершенно разная. Слева (на чёрном) у нас есть, назовём это, overlay. Справа (на белом) у нас сразу идёт текст и фото. Сами фото идут в разном порядке, разной структуре. По сути это одна и та же страница, но для разной темы.
Как расставлять фото – думаю нет особого смысла показывать – гридом это делать.
Основные вопросы – как добавить разную логику в зависимости от темы? И как организовать цвета?
В данном проекте я разделил цвета на базовые и связанные с темой. Базовые – это выделение (розовое, фиолетовое), цвет текста в выделенных элементах (всегда белый) и disabled – для элементов формы. Все остальные цвета вошли в темы.
Дальше, чтобы понять, какая тема в данный момент включена, я добавил css переменную –theme с указанием темы (‘dark’, ‘light’ соответственно), которую ловил в компонентах, где оно было необходимо, с помощью следующей логики.

В отличие от подхода с data-attribute, данный способ позволяет переопределять тему не только в root компоненте, но и во вложенных. Собственно переопределение происходит с помощью ThemeProvider.
Получив доступ к текущей теме уже непосредственно в jsx компоненте, я добавлял логику overlay (подложки под текстом в тёмной теме) и расстановки карточек (2 или 3 ряда).
Как резюме, хочу обрисовать плюсы и минусы данного подхода.
По сути, всё так же, как с комментариями. Если вы пишете развёрнутые и понятные комментарии там, где они действительно необходимы, если вы не забываете обновлять их каждый раз после того, как изменилась логика - тогда это даст вам огромный плюс в поддержке кода. Я бы сказал, чем больше становится проект, тем проще его будет писать.
То же самое и здесь. Если, узнав новую информацию от дизайнера вы не ленитесь обновлять все переменные, которые с этим связанны, если вы стараетесь сделать логику в коде похожую на ту, что в голове заказчика, то со временем проект не будет нуждаться в дизайнере – быстрее станет сделать, чем нарисовать.
Если же вы забываете обновлять комментарии, или услышав про новые требования от заказчика лениво создаёте очередную переменную, а не обновляете существующую, то данный подход сделает ваш код слишком запутанным.
Ещё раз хочется напомнить, что разработчик – это человек, который переводит мысли заказчика на язык машины.
Пишите понятный код, надеюсь, поддержка теперь станет для вас удовольствием!