Не усложняйте масштабируемый CSS
Перевод статьи «Keeping it simple with CSS that scales» Энди Белла
Это письменное изложение моего доклада «Не усложняйте масштабируемый CSS», который я впервые прочитал на State of the Browser 2019.
Это очень длинная статья, поэтому я записал аудиоверсию:
Ссылка на Яндекс.Диск
У CSS сегодня очень странное положение в вебе. Явно видна поляризация сообщества с мнениями от «CSS отстой» до «CSS рулит, учите матчасть, глупцы».
Я больше склоняюсь к лагерю «CSS рулит» и сейчас я объясню почему: у меня есть теория относительно людей с позицией «CSS отстой». Я предполагаю, что этот лагерь состоит из тех, кто излишне перегружает свой CSS, из тех, кто не понимает силы CSS, и, наконец, из тех, кто смотрит на CSS как на JavaScript и ожидает, что эти две технологии будут работать примерно одинаково.
Цель этой статьи в том, чтобы поговорить с вами, как мы можем упростить CSS, получив на выходе невероятную мощь, и, в то же время, оставить его максимально технологичным. Секрет в том, что большая часть написанного не относится к CSS напрямую, но позже вы всё поймёте.
Поговорим о масштабе
Я очень не люблю термин «Масштаб». Я думаю, что мы зациклились на этом термине. Ровно как и на других подобных ему обозначениях: «JAMstack», «Serverless» и «performant».
Все эти слова одинаково ужасны, но с одной вещью они справляются — создают общий, узнаваемый конструкт для коммуникации. И как бы мне не было больно, но я вынужден был использовать термин «Масштаб» в названии этой статьи. И ещё до того, как открыть эту статью, вы знали, что я буду говорить в ней о работе с большими кодовыми базами.
Одна из вещей, которая раздражает меня в термине «Масштаб» заключается в том, что люди часто используют его как довольно мутное оправдание для создания чего-то слишком усложнённого.
Взглянем на простой пример. Обычно эти слова произносит какой-нибудь Бро технарь:
Мы используем библиотеку CSS-in-JS потому что наш продукт нуждается в масштабировании
Народ, давайте хоть минуту будем честны: большая часть этой болтовни масштабируется максимум до размеров мусорного ведра. Так что это не достаточное оправдание, не так ли?
У меня хватит смелость сказать и то, что использование масштабирования в качестве оправдания для намеренного усложнения чего угодно — особенно CSS—не прокатит даже для огромных команд, работающих на гигантском проекте. Держите это в уме и, надеюсь, к концу чтения вы перейдёте на мою сторону.
Четыре ключевые вещи
Думаю, что сегодня мы сосредоточимся на четырёх ключевых вещах. В оставшейся части статьи я проведу вас по каждой из них. Теперь вы знаете, сколько вам предстоит меня терпеть.
Можно сократить эти четыре правила до аббревиатуры DCCS.
Приступить к погружению!
Без паники
Одна из моих любимых книг — «Автостопом по Галактике». В этой книге рассказывается, как Землю взрывают в ходе строительства гиперпространственной магистрали и главный герой, Артур Дент отправляется в путешествие автостопом по просторам космоса вместе со своим приятелем-пришельцем Фордом Префектом.
Я знаю, что звучит очень увлекательно, но вы пришли сюда не книжки обсуждать.
Настоящий «Справочник Автостопщика по Галактике »— это, своего рода, интерактивная энциклопедия — ну, интерактивная по меркам 1979 года…
То, что всегда находило отклик в моей душе — обложка, на которой написано «Без паники». Эта фраза встречается в книге бесчисленное количество раз, даже того, когда контекст ситуации очень располагает к панике.
Я ассоциирую себя с Фордом Префектом, который руководит нелепым британцем Артуром Дентом в их совместных ошеломляющих приключениях в разных точках космоса. Форд всегда воспринимает испытания спокойно, прагматично, ровно так, как советует справочник — «Без паники». И я думаю, что мы должны принять этот совет всерьёз.
Фактически, когда Артур впервые берёт книгу в руки, то произносит:
«Мне нравится обложка… “Без паники”. Это первая полезная и вразумительная вещь из всего, что я услышал за день.»
Мда, я немного отклонился от сути…«Без паники». Это действительно полезный совет, поскольку когда мы находимся в состоянии паники, то допускаем нелепые ошибки. Подумайте об этом: каково количество вселяющих ужас хаков вы использовали, когда вам нужно было что-то быстро поправить в проекте.
Множество людей делают так находясь в состоянии паники. Дедлайн всё ближе и у нас нет времени разбираться в причинах поломки CSS. Мы просто добавляем !important
в качестве хотфикса.
Всё в порядке: все так делают. Но вот что не в порядке, так это технический долг. Иногда паника перерастает из маленьких заплаток тут и там в что-то более серьёзное, например, в использование CSS-in-JS фреймворка. Что, как мне кажется, равнозначно взятию кредита для выплаты ипотеки.
Обычно большие технические долги закрываются лишь частично, потому что разработчик, накопивший этот долг, скорее всего уже уволился и нашёл новую работу. Наибольший урон с точки зрения производительности в этой ситуации наносится пользователю. Что совершенно ошеломляет меня при учёте всех современных возможностей CSS.
Текущее положение дел в CSS
Ну, серьёзно, у нас никогда не было CSS лучше чем сегодня. Поддержка браузерами гридов — 93%, а флексов — 98,8%. Если использовать их в рамках подхода прогрессивного улучшения, то ваша раскладка всегда будет в полном порядке. Дело сделано. Без проблем.
Мы так же имеем в распоряжении кастомные свойства CSS, которые представляют собой нативные переменные. Они невероятно удобны для токенизации нашего CSS. Поскольку они так же подвластны каскаду, то мы можем переопределять их контекстуально. Это очень удобно при создании тем, режимов отображения (тёмный режим), алгоритмов.
:root {
--primary: #8e8e8e;
}.box {
background: var(--primary);
}.badge {
color: var(--primary);
}
Я перечислил лишь малую часть новых крутых и полезных фич. Вы можете самостоятельно убедиться в том, на сколько мощным инструментом для написания стилей является современный CSS. Но и этого может быть не достаточно, особенно если вы пишите действительно много кода. Давайте взглянем на возможное решение.
Sass для победы!
Весь этот нативный функционал клёвый, правда? Не забывайте и о нативной вложенности. Но кто делает всю работу, выполняет все эти функции? Правильный ответ: браузер!
В большинстве случаев всё в порядке. Хотя мысль о нативном CSS пугает меня. Особенно когда мы уже нагрузили браузер тяжёлым JS-фреймворком.
Обычно я говорю, что использование SASS не обязательно, поскольку нативные фичи языка уже на подходе. Но на самом деле лучше аккуратно предварительно скомпилировать ваш CSS и не заставлять браузер пыхтеть над этим. Он и так уже усердно трудится, перерисовывая DOM каждый раз при изменении малейшей мелочи (кхе-кхе, реактивные фреймворки, кхе-кхе). Так зачем же заставлять браузер работать ещё усерднее? Только ради того чтобы вы имели нативную вложенность? Попахивает пренебрежением пользователями в угоду удобства разработчика.
Мы с лёгкостью выбрасываем вещи в мусор, не задумываясь о последствиях. Да, что ж, наследование скоро появится, но как будут справляться с 5 уровнями наследования слабенькие девайсы? Как эти немощные аппараты будут справляться с вычислением цвета, заданного при помощи кастомного свойства, значение которого переписывалось раз пять? Не важно на сколько хороши кастомные инструменты, если мы продолжим бездумно выкидывать в браузер замусоренный код. От этого пострадают пользователи.
Sass прекрасен тем, что он вобрал лучшее от обоих миров. У вас в распоряжении куча крутого, например, наследование. Но если вы делаете это правильно, то в результате можете получить хорошие плоские селекторы. У вас так же есть компоненты. И если вы захотите, то можете собирать проект из нескольких бандлов. Вы так же можете линтить CSS во время сборки, чтобы контролировать, что всё идёт по плану.
Самая важная вещь в том, что с Sass — точнее с SCSS— вы всё ещё пишите CSS. SCSS достаточно умён, чтобы делать только то, что ему положено и оставить остальной ваш CSS-код в покое. По мне так это звучит идеально.
Разговаривайте
Похоже, что мы живём в век, когда все инструменты и методологии придуманы лишь затем, чтобы не общаться друг с другом.
Сгенерированные классы в CSS это классический пример. Типичная проблема. У некоторых может в этом месте случиться приступ экзистенциального кризиса или кто-то, может быть, мечтает о сгенерированных машинами классах. Но зацените: дальше мы попробуем штуку под названием разговор:
Разраб #1 — Лукреция
«Компонент, который мне нужен, уже существует. Загляну-ка я в git и проверю, кто его создал. Ага, это была Изабелла»
Лукреция разрабу #2, Изабелла
«Привет, разработчик Изабелла, есть ли причины, по которым этот компонент называется “block”?»
Изабелла Лукреции
«Ах да, есть очень важная причина. Может ты назовёшь этот компонент “box”?»
Теперь вы самостоятельно можете додумать эту очень важную причину:
- Общее решение заинтересованных сторон
- Проблема устаревшей кодовой базы
- Дизайнерское решение
Важно отметить, что Изабелла — профессионал, которая умеет эффективно общаться. Поэтому она предлагает альтернативу.
Лукреция
«Это отличная идея. Спасибо!»
Что это было?? Подобное полезное волшебство случается когда мы ПО-НАСТОЯЩЕМУ РАЗГОВАРИВАЕМ ДРУГ С ДРУГОМ.
Так называемые «софт скиллс» я предпочитаю называть базовыми навыками. Ими часто пренебрегают в пользу ведения собственного списка ссылок или писать записочки на доске. Это сильно меня раздражает, потому что нельзя быть эффективным членом команды без коммуникации.
Документация — ваше всё
Другой невероятно полезный способ коммуникации, не предполагающий разговора, это письмо. Я люблю писать, и вы наверняка заметили, что я пишу много…На самом деле я записываю практически всё. Потому что не знаю, когда мне что-нибудь из этого может понадобиться. А ещё записи помогают мне всё запомнить.
Существует реальная ценность в том, чтобы всё записывать. Особенно в большой команде фронтенд-разработчиков. Тем более при написании CSS-кода или любого другого. Вы можете документировать процесс размышлений и пояснять, почему вы сделали это именно так. Пишите документацию, и тогда новый разработчик сможет быстро разобраться что к чему.
Под документацией я не имею в виду тома структурированных документов. Я подразумеваю чёртовы комментарии в коде!
Вот вам пример:
.card {
background-color: #ffffff !important;
}
Если бы я увидел такое, то я бы воскликнул «Что, чёрт возьми, тут происходит?»
Но взгляните на такой вариант:
.card {
/* Когда карточка используется в устаревшем приложении, то случаются коллизии стилей. Поэтому, к сожалению, пришлось прибегнуть к этому ядрёному решению. Эта часть однозначно нуждается в рефакторинге */ background-color: #ffffff !important;
}
Теперь, когда я смотрю на этот кусок кода, я скорее испытываю подобные чувства: «Что ж, вполне уместно. У меня есть немного времени и, возможно, я могу взглянуть на источник проблемы.»
Хорошо налаженная письменная коммуникация может исключить ненужные коллизии между людьми и тем самым предотвратить появление большого технического долга
Беспроигрышный вариант!
Верно, хватит разглагольствований о том, как быть взрослым и перейдём наконец к части, где я расскажу о том, как писать CSS. Приступим.
Консистентность
Я думаю, что консистентность это ключ к масштабированию CSS (простите, что я снова использую термин «Масштаб»). На самом деле это именно то, что пытаются дать вам все эти чрезмерные абстракции. Но, мне кажется, они зашли слишком далеко.
Я пользуюсь методологией, которая является своего рода производной от БЭМ — упрощённая его версия. Я называют её C-BEUT, от слова Cascade (Каскад), Block (Блок), Element (Элемент), Utility (Утилита) и Token (Токен). Это похоже на чушь, я знаю. Но оно вполне вписывается в список всех тех бессмысленных имён, что мы уже придумали, правда? Я поясню как работает C-BEUT.
Каскад
Буква «С» означает «Каскад» (Cascade). Каскад моя любимая вещь в CSS, если использовать её правильно. Вы можете написать совсем немного CSS-кода и получить поистине прекрасный результат. В этой методологии каскад стоит на первом месте. Я прописываю дефолтные значения, темы и типографику как можно выше. Это очень мощно.
Компоненты в свою очередь могут быть более схематичными и отличаться только тем, что расходится с глобальными темами, а не содержать в себе вообще все стили.
Блок
Вот есть ваш компонент, карточка или кнопка. Это строительный блок интерфейса. И это правда так — часть конструкции и ничего больше.
Элемент
Это нечто, что зависит от блока — дочерний элемент. Он всегда живёт внутри родительского блока. Хороший пример с карточкой. Внутри неё может быть card__image
.
Я стараюсь оставлять имена классов элементов максимально лёгкими и использовать CSS-алгоритмы для описания контента вместо углубления.
Это сохраняет наши файлы компонентов лёгкими и схематичными.
Утилита
Под утилитой я понимаю класс, который делает что-то одно и делает это хорошо. Как водопроводчик или открывашка. Пример утилитарного класса — класс, центрирующий текст или с неким верхним отступом. Просто. Низкоуровневый профильный инструмент.
Важно то, что вы можете писать общий CSS и применять его там, где это необходимо. Вместо того, чтобы повторяться снова и снова.
Токен
На самом деле это тот же утилитарный класс, но имплементирующий дизайнерский токен. Частно генерируется отдельным инструментом.
Что такое дизайнерский токен? Позволю объяснить это понятие своей приятельнице Джине, которая и придумала их.
Под дизайнерским токеном подразумевается визуальный атом дизайн-системы — в частности это именованные элементы, содержащие в себе атрибуты визуального дизайна. Мы используем их вместо захардкоженых значений. Это облегчает процесс поддержания и масштабирования визуальной системы.
— @jina
Эти маленькие классы отлично подходят для разделения основы интерфейса от дизайн-системы. Это позволяет дизайн-системе жить отдельно от остального кода, что даёт больше возможностей и свободы.
Токен .text-600
нужен для изменения размера текста, ровно так же, как это сделано на моём сайте. Размер меняется на треть. Таким образом все классы образуют плавную кривую изменения размера.
Я использую токены для всего:
- Размера текста
- Отступов между элементами
- Внутренних отступов
Вот и всё. Я не буду тратить время чтобы убедить вас, что это лучший способ написания CSS, потому что это, скорее всего, не так. Но это хотя бы последовательно. Ровно как БЭМ или ITCSS. В конце дня выберите то, что сработает для вашей команды. И задокументируйте это, чёрт возьми. Дайте возможность членам вашей команды принимать решения в рамках принятой политики вместо того, чтобы замедлять их.
Одна вещь, которую я скажу про эту методологию перед тем, как вы выберите именно её: потратьте время на эксперименты. Всё меняется, просто примите и смиритесь с этим. Именно поэтому я использую C-BEUT вместо БЭМ. Я думаю, что БЭМ прекрасен, но я столкнулся с проблемами, когда мои модификаторы выходили из-под контроля. Поэтому я поразмыслил, упростил всё и пришёл к использованию утилит и токенов.
Всегда будьте готовы к изменениям. Мы живём в постоянно меняющемся мире и работает в невероятно изменчивой индустрии. Если вы слишком привяжитесь к своим инструментам, то вы, скорее всего, создадите много проблем не только себе в будущем, но и другим.
Упрощение
Было бы странно, если бы я писал статью с названием «Не усложняйте масштабируемый CSS» и не сказал ни слова об упрощении, правда?
Давайте начнём с разговора о фреймворках. Они вам точно нужны? Я так не думаю.
Bootstrap невероятно мощный фреймворк. Он появился тогда, когда мы только учились создавать адаптивные сайты и боролись с проблемами кроссбраузерности. Это были адские деньки и Bootstrap облегчил все эти страдания. На самом деле я пойду дальше и назову Bootstrap одной из самых крутых когда-либо созданных дизайн-систем.
Использование Bootstrap сегодня — когда у нас есть нативные инструменты для раскладки, например, гриды и флексы — похоже на использование кувалды для разбивания яиц. Это может привести к осложнениям. Из-за особенностей строения фреймворк использует глобальные селекторы для очень агрессивного насаждения стилей. Это сново приводит нас к подобным строчкам:
.card {
background-color: #ffffff !important;
}
Будем честными, подобными проблемами страдает большинство CSS-фреймворков. Пример, который доводит меня до сердечного приступа — Tailwind CSS, который генерирует 40 тысяч строк CSS-кода по дефолту (исходники). Чёкнуться можно.
Если вы используете какую-нибудь методологию типа BEM, ITSCSS, SMACSS или даже C-BEUT, то вы можете построить сетку на гридах, добавить щепотку флексов и всё. Вы великолепны.
Это подход, который мы разбираем в книге «Every Layout», которую я написал в соавторстве с моим приятелем Хейдоном Пикерингом.
Для наших макетов мы находим наиболее надежное решение, упрощая и решая поставленную проблему. Затем мы даём браузеру общее понимание, используя аксиомы и рудименты, вместо того, чтобы заниматься микроменеджментом и постоянно перерисовывать кадры. Это даёт нам очень согласованную систему, в первую очередь ориентированную на прогрессивное улучшение.
Этот проект получился действительно крутым и отозвался в сердцах многих людей, потому не тяните и кликайте сюда.
Пара слов о прогрессивном улучшении. Встаньте в круг и преклоните колени на мгновение.
НЕ ПРЕНЕБРЕГАЙТЕ СОВРЕМЕННЫМ CSS ИЗ-ЗА НЕОБХОДИМОСТИ ПОДДЕРЖИВАТЬ IE11.
Это нелепо. Я каждый раз бешусь, когда кто-нибудь делится в Твиттере классным трюком и в комментариях обязательно появляется этот парень «ЧтО нА сЧёТ пОдДеРжКи IE11?»
Хватит передвигать пиксели в ваших проектах и вместо этого используйте прогрессивное улучшение, задавая значения по-умолчанию, которые автоматически улучшаются там, где доступна поддержка.
Посмотрите на пример: у нас есть список из трёх колонок. В прошлом мы бы применили несколько хаков не только чтобы расположить блоки именно в таком порядке, но и чтобы добиться одинакового отображения во всех браузерах.
А я предлагаю отступить на шаг назад и упростить проблему, найти разумное решение. И вот оно: одна колонка с отступами. Мы можем достичь 100% покрытия, написав минимум CSS.
Вот вам маленькая удобная грид-система, использующая minmax
для расстановки колонок. Адаптивная система раскладки без единого медиавыражения.
.auto-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
grid-gap: 1rem;
}
Но гриды до сих пор поддерживаются не везде. Скажем спасибо истиной природе CSS, браузер просто проигнорирует те свойства, которые не понимает и продолжит чтение кода. Благодаря этому мы можем написать ниже ещё один блок:
.auto-grid > * {
max-width: 25rem;
margin-left: auto;
margin-right: auto;
}.auto-grid > * + * {
margin-top: 1rem;
}
Сегодня благодаря @supports
мы можем группировать стили, которые поддерживаются. Если браузер поддерживает гриды, то он поддерживает и @supports
, дело сделано.
Всего 22 строки CSS без каких-либо хаков. Работает везде вплоть до IE9 (и, возможно, даже в браузерах старше).
Притормозите
Последняя остановка в процессе упрощения и, на самом-то деле, последний пункт перед тем, как я закончу — «притормозите».
Нет, правда, притормозите. Я знаю, что это сложно, когда вы работает от спринта к спринту или над большим проектом. Но поверьте мне, когда дерьмо попадает на вентилятор, нужно сбросить скорость.
Я пришёл к этому в начале года. Я работал над большой библиотекой паттернов и мы столкнулись с некоторыми осложнениями. Я должен был остановиться, посмотреть на проблему со стороны и включить критическое мышление, находясь в покое. Но вместо этого я продолжал отрабатывать каждый спринт, ретроспективу, сессию планирования. В итоге отсутствие замедления привело к массивным негативным последствиям. Я обналичивал технический долг так, будто зарабатывал на этом мили.
У нас было две-три системы раскладки, несколько текучих типов и несколько утилитарных типов, которые конфликтовали между собой. А так же компонент карточки, который сам по себе был практически вебсайтом. Если бы я притормозил и отступил на шаг назад, то я мог бы предсказать эти проблемы. Но я этого не сделал. Так что серьёзно, притормозите и в итоге вы сэкономите время, которое потом можете потратить на исправления.
Дам вам заключительный совет под занавес: вместо того, чтобы двигаться быстро и разрушать вещи, двигайтесь медленно и осознанно.
Вы можете посмотреть этот доклад в записи здесь, State of the Browser 2019.
Презентацию вы найдёте по ссылке тут.