Сложность, которую мы добавляем в CSS
Можно найти класс в разметке и не найти его в исходниках стилей. Имя собрано из нескольких частей, препроцессор соединил их при сборке, а поиск в редакторе ничего об этом не знает. Автору кода всё понятно. Человеку, который пришёл в проект следом, предстоит сначала восстановить чужой способ думать.
В первом выпуске «Веб-стандартов», в феврале 2016 года, мы с Вадимом Макеевым и Ольгой Алексашенко обсуждали, как инструменты меняют повседневную работу с CSS. Препроцессоры уже стали привычными, PostCSS позволял пробовать будущие возможности языка, а ради загрузки шрифтов приходилось писать отдельную программу. Меня занимало, сколько сложности действительно нужно конкретному проекту и кому потом с ней разбираться.
Найти класс в собственном проекте
В препроцессоре можно вложить один селектор в другой, а можно пойти дальше и сконструировать само имя класса. Взять имя родителя через амперсанд, добавить суффикс и получить новый селектор. Запись короче, повторять имя блока не приходится. Но полного имени, которое видит разработчик в браузере, в исходном файле уже нет.
Я встречал такой код в работах наставников HTML Academy. Файлы оставались большими, модулей было много, и обычный поиск переставал помогать. Разработчик, который знает устройство проекта, ещё помнит, где собирается нужный селектор. Следующему человеку приходится это выяснять. Ради чего мы добавили ему работу — чтобы меньше печатать?
Вадим возразил: если стили разделены на небольшие файлы по блокам, место поиска и так понятно. Кроме того, имя блока можно поменять один раз, и вложенные селекторы изменятся вместе с ним. Это полезное свойство, когда блок действительно нужно переименовать или перенести.
Но небольшие модули ещё нужно сделать, а сокращённая запись появляется уже сейчас. В большом файле возможность однажды переименовать блок мало утешает человека, которому сегодня нужно найти и исправить стиль.
Переменная рядом со своей задачей
Препроцессоры дали нам разбиение стилей на файлы, вычисления, переменные и вложенность. Часть этих возможностей постепенно приходила в сам CSS. Вместе с ними в вёрстке появились подходы из программирования, которые не всегда удачно переносили в стили.
Например, в статье о препроцессорах предлагали собрать все переменные в одном глобальном файле. Мне это казалось сомнительным. Есть общая настройка проекта, а есть значение, которое нужно одному модулю. Если ширина слайдера рассчитывается из количества слайдов, переменная с этим количеством относится к слайдеру. Уносить её в общую конфигурацию только потому, что это переменная, незачем.
С цветами и шрифтами тоже хотелось понимать задачу. В библиотеке компонентов они могут быть частью публичной настройки: другой разработчик возьмёт библиотеку и изменит её под свой продукт. У сайта с определённым дизайном такой задачи может не быть. Если каждое значение объявлено настройкой, код выглядит гибким, но читать его приходится через дополнительный слой имён и файлов.
Оля привела пример, где эта гибкость была нужна: дневная и ночная темы. Цвета должны согласованно меняться, поэтому переменные там решают конкретную задачу. То же касается набора шрифтов или связанных типографических значений. Спор был не о том, разрешать ли переменные, а о том, какое устройство проекта они описывают.
Bootstrap или Foundation удобно брать за образец: там много кода и за ним стоят опытные разработчики. Но вместе с приёмом легко перенести и ограничения, ради которых он появился. Авторы фреймворка не знают, в каких проектах его будут использовать, и оставляют много настроек. У себя мы знаем задачу и можем решить её проще.
Писать ближе к будущему CSS
Новые страницы Академии мы в тот момент делали на PostCSS, без отдельного препроцессора. Нужные нам возможности давал набор плагинов. Мне нравилось, что часть из них позволяла писать синтаксис, который обсуждался для будущего CSS: знакомиться с языком до того, как его одинаково начнут понимать браузеры. Примерно так же Babel позволял раньше пользоваться новыми возможностями JavaScript.
В сборке оставались и другие плагины, в том числе для вложенности. Мы выбирали то, что требовалось в работе. Оля, например, использовала Sass вместе с PostCSS: один инструмент не обязательно должен был заменить другой целиком.
Настройка нашей сборки заняла время. Плагины выполнялись последовательно, и результат зависел от их порядка. Некоторые возможности из общего набора cssnext пришлось отключить, а соответствующие преобразования подключить отдельно, на нужном месте. Пока разбирались, встречались ошибки и неочевидные зависимости.
К моменту разговора эта конфигурация работала уже около полугода, и возвращаться к ней не приходилось. С настройкой пришлось повозиться, зато потом можно было просто писать стили.
Что происходит, пока загружается шрифт
Со шрифтами у нас получилась другая история. Раньше Академия асинхронно загружала CSS с закодированными в base64 шрифтами и сохраняла его в localStorage. При следующем открытии страницы можно было взять готовую строку оттуда. На первый взгляд удобный способ избежать повторной загрузки.
На практике чтение большой строки из localStorage могло подвесить браузер. Мы встречали проблемы и в актуальном на тот момент Firefox. Приходилось думать ещё и о том, сколько работы мы поручаем устройству: прочитать строку, разобрать её, декодировать шрифт. Особенно на телефоне. Точных замеров расхода батареи у меня не было, но само количество дополнительных действий вызывало вопросы.
За неделю до записи мы поменяли схему. Стали загружать обычные файлы WOFF и WOFF2, а их хранение оставили браузерному кешу. Пока шрифт не готов, текст отображается системным шрифтом. Когда загрузились все четыре нужных начертания, на странице появляется класс, который включает наш шрифт.
Загрузкой управлял JavaScript. Мы использовали FontFaceOnload и ждали завершения всех загрузок; в sessionStorage отмечали, что в этой сессии шрифты уже получены. Хотелось пользоваться нативным API загрузки шрифтов, но поддержки в нужных браузерах пока не хватало.
Оля тоже рассказывала о неприятностях с хранением шрифтов в localStorage. В её случае в кеше оказывался повреждённый шрифт, и приходилось разбираться уже с тем, как его оттуда убрать. Две похожие реализации дали разные проблемы в месте, где хотелось всего лишь показать текст.
Вернуть браузеру его работу
Вадим заметил, что вся эта программа вокруг шрифта в идеале вообще не нужна. Браузер умеет загружать и кешировать ресурсы; разработчику нужна возможность указать, как вести себя тексту до завершения загрузки. Я с этим соглашался. Мы ждали font-display, чтобы не воспроизводить такое поведение своими средствами.
С иконочными шрифтами мы уже расстались. Если шрифт не загрузился или браузер обработал его не так, вместо понятной иконки пользователь получал что-то другое. В Opera Mini это было особенно заметно. SVG казался более подходящим форматом для самих изображений.
Я пробовал разные способы подключать SVG и склонялся к внешнему файлу со спрайтом, откуда можно брать отдельные изображения через use. Для браузеров без нужной поддержки собирался попробовать SVG for Everybody.
Во всех этих случаях хотелось убрать работу, которая появилась только из-за недостающей возможности браузера. Пока её нет, мы настраиваем плагины, следим за загрузкой и подключаем полифилы. Когда она появляется, можно пересмотреть собственный код и оставить браузеру то, с чем он уже справляется.