Ситуация получилась следующая.
Компания переделывала сайт и сказала, что необходимо сделать его максимально гибким, чтобы, во-первых, можно было дизайн обновлять просто, во-вторых, сделать ещё несколько подразделов на базе готового функционала (вроде как 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 (https://www.forbes.com).
Похожая логика магических чисел: 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 варианта кода, которые я часто встречаю, и тот, что был выше.



Смотрим, вникаем.
Первый вариант (#1) – классическое копирование из фигмы или ещё откуда.
Второй вариант (#2) – классическое унифицирование переменных. Когда у нас цвета идут как $red, $black либо $alpha, $beta, а отступы просто умножаются (в лучшем случае, бывает просто новые переменные создаются и существует по 1000 переменных в проекте одновременно).
Третий способ (#3) вроде как читаем, но, честно говоря, тоже не сильно хороший. Есть какая-то идея в имени переменной ($[property]-[element]-[media-query]), но выглядит уж больно избыточно.
Ииии, «silver bullet», который я предлагаю.
Вариант записи, к которому мы так долго шли!
Выносим переменные, которые мы меняем из каждого компонента в один глобальный – html тег.
Как?
Добавляем css-variables.

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

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

Было

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

Было

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

Было

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


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

В отличие от подхода с data-attribute, данный способ позволяет переопределять тему не только в root компоненте, но и во вложенных.
Собственно переопределение происходит с помощью ThemeProvider.
Получив доступ к текущей теме уже непосредственно в jsx компоненте, я добавлял логику overlay (подложки под текстом в тёмной теме) и расстановки карточек (2 или 3 ряда).
Как резюме, хочу обрисовать плюсы и минусы данного подхода.
По сути, всё так же, как с комментариями. Если вы пишете развёрнутые и понятные комментарии там, где они действительно необходимы, если вы не забываете обновлять их каждый раз после того, как изменилась логика - тогда это даст вам огромный плюс в поддержке кода.
Я бы сказал, чем больше становится проект, тем проще его будет писать.
То же самое и здесь. Если, узнав новую информацию от дизайнера вы не ленитесь обновлять все переменные, которые с этим связанны, если вы стараетесь сделать логику в коде похожую на ту, что в голове заказчика, то со временем проект не будет нуждаться в дизайнере – быстрее станет сделать, чем нарисовать.
Если же вы забываете обновлять комментарии, или услышав про новые требования от заказчика лениво создаёте очередную переменную, а не обновляете существующую, то данный подход сделает ваш код слишком запутанным.
Ещё раз хочется напомнить, что разработчик – это человек, который переводит мысли заказчика на язык машины.
Пишите понятный код, надеюсь, поддержка теперь станет для вас удовольствием!
Комментарии (9)
IgosYee
18.12.2021 14:59+1Ох, все отступы кратные 10ке. Тут недавно клиент пришел со своим дизайном, вроде ок. Но все отступы плавают 17рх, 9рх и тд, логики в них никакой нет. "Дизайнер" ставил их на глаз. На просьбу подправить или что мы будем использовать одинаковые в похожих элементах, клиент сказал "нетЪ, хочу перфект пиксель"...
mikita_du Автор
18.12.2021 15:13Тут дело такое.
У меня в одном дизайне были разные отступы в дизайне вокруг одного элемента.
Сверху 9px, справа 7px, снизу 8px.
Я на своё усмотрение сделал единое. На дизайн было похоже - все были рады)
stainlouder
19.12.2021 00:20Согласен, довольно небольшой процент дизайнеров, которые относятся к своей работе с пониманием. Как раз используют ритмику, палитры, иногда даже золотое сечение, причем со знанием дела. Многие, к сожалению, считают, что достаточно просто "природного" чутья и понимания "что с чем слепить чтобы было круто". А еще находятся заказчики, которые до абсурда отставают каждый пиксель подобных дизайн кодов.
Здорово, что можно настроить все в переменных и тогда и собственный перфекционизм спокоен и изменения не требуют большого количества времени.
skeevy
20.12.2021 02:02В первой части переизобрели бутстрап 4, во второй части в css уже видны проблемы отсутствия следования методологии какой либо
Для новичков материал суперский, но автору посоветовал бы пересмотреть подход к организации css, начиная с нейминга и раз вы используете scss - использовать миксины чаще и placeholder-классы. Даже "улучшенный" код можно ещё сократить, используя все выше, а спагетти из card-* упаковать в 3-5 строчек, используя map
notiv-nt
Конечно прекрасно, но такой контент лучше в видео формате воспринимать
Xeldos
Нет.
FFF
Такой контент лучше воспринимать в виде статьи с хорошим оформлением, чего здесь, увы, нет.