Привет всем! Пришло время сообщить, что мы рассчитываем еще до конца февраля выпустить новую книгу по CSS, которая рекомендуется всем, кто уже освоил Макфарланда (пока в наличии, ближайшую допечатку рассчитываем сделать в январе).
Сегодня вам предлагается перевод статьи Кита Гранта (опубликована в июне), в которой автор излагает свою точку зрения на CSS и фактически объясняет, о чем хотел рассказать в своей книге. Читаем и комментируем!
Я потратил немало времени, размышляя, что такое «CSS-ориентированное мышление». Кажется, что некоторым удается его «усвоить», а другим – нет. Думаю, что, если бы я смог четко артикулировать, в чем заключается это мировоззрение, может быть, и сами CSS стали бы понятнее для тех, кто с ними мается. Отчасти поэтому я сел и написал книгу «CSS для профессионалов».
Сегодня я хочу еще раз подступиться к этой проблеме. Рассмотрим три ключевых характеристики CSS, отличающих этот язык от традиционных языков программирования. CSS устойчив, декларативен и зависит от контекста. Думаю, понимание этих аспектов языка – ключевая предпосылка для того, чтобы стать мастером CSS.
CSS устойчивы
Если вы случайно удалите фрагмент кода в файле JavaScript, то приложение или веб-страница, где он используется, практически наверняка обрушится или зависнет, а скрипт (а то и вся страница) выйдет из строя. Если проделать то же самое в CSS, то, возможно, вы ничего и не заметите. Практически весь остальной код кроме удаленного фрагмента будет и далее работать как надо.
Это свойство называется «устойчивостью» (resilience). HTML и CSS специально проектировались с расчетом на отказоустойчивость. Если возникает проблема, то браузер выбрасывает ошибку; в противном случае он просто игнорирует данную часть кода и идет дальше.
С точки зрения отладки это может показаться абсурдным: если программа не выбрасывает ошибок, как же нам узнать, что что-то пошло не так? Но в этом и заключается важнейший аспект устройства CSS. Он вплетен в ткань самого языка. Признаю, требуется время, чтобы к этому привыкнуть. Однако, как только вы это усвоите, можно смело переходить к использованию возможностей, поддерживаемых не во всех браузерах. Именно так и обеспечивается поступательное развитие сайта или приложения.
Рассмотрим такой пример с сетчатым макетом. Он работает как в браузерах, поддерживающих сетку, так и в браузерах, не поддерживающих сетку. В тех браузерах, где сетка не поддерживается, компоновка получится слегка неровной (точные размеры элементов могут варьироваться), но страница все равно будет уложена примерно так как надо:
.portfolio {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
}
.portfolio__item {
display: inline-block;
max-width: 600px;
}
Браузер, не понимающий двух объявлений сетки, проигнорирует их, а работу удастся довершить благодаря другим правилам. В свою очередь, браузер, понимающий сетку, использует сеточный макет и проигнорирует объявление inline-block (поскольку именно таким образом задумана работа сетки). Джен Симмонс полушутя называет это явление “Квантовыми CSS”. Берешь некоторую возможность CSS и «одновременно используешь и не используешь. А она одновременно и работает, и не работает».
Такой феномен «страховочного» поведения – неотъемлемая составляющая работы с CSS, но он чужд для большинства традиционных языков программирования.
CSS декларативны
В JavaScript мы пишем конкретные пошаговые инструкции, характеризующие, как программа должна прийти к некоторому результату. В CSS вы сообщаете браузеру, что именно хотите увидеть на экране, а браузер находит способ, как это сделать. Понять это очень важно. Если вы это усвоите, то CSS выполнит всю сложную работу за вас, а если не усвоите, то ополчитесь на самую суть языка CSS, и вас раз за разом будет ожидать разочарование.
Когда пишешь CSS, ты фактически задаешь систему ограничений. Мы не указываем браузеру, где должен находиться каждый конкретный элемент на странице, а сообщаем, какие отступы между ними должны быть, после чего браузер сам расставляет все по местам. Вы не указываете браузеру (как минимум, не должны указывать), какой высоты должен быть контейнер. Вы позволяете браузеру самому определить это во время отображения, когда ему будет известно содержимое контейнера, известно, какие еще стили применяются в данном контексте, и какова доступная ширина области просмотра.
Приходится учитывать слишком много переменных. Суть CSS – организовать процесс так, чтобы вы могли не беспокоиться обо всех этих переменных. Задать систему ограничений. О деталях позаботится сам язык.
Простой пример
Давайте рассмотрим такой образец CSS: font-size: 2em
. Что делает этот код? “Увеличивает кегль шрифта,” скажете вы. Но ведь это еще не все. Он также корректирует переносы текстовых строк внутри контейнера, так как теперь в каждую строку укладывается меньше слов. Соответственно, при этом зачастую увеличивается количество текстовых строк, а также увеличивается высота контейнера, чтобы эти новые строки в нем уместились. При изменении высоты контейнера соответствующим образом сдвинется весь текст, расположенный на странице под этим контейнером. Наконец, вышеприведенный код также задает локальное значение em
. Значения всех прочих свойств, при определении которых использовались единицы em
, придется вычислить заново.
Одно лишь это объявление порождает на странице целый шлейф изменений. И все они призваны достичь именно того, к чему вы, должно быть, стремитесь: содержимое всегда умещается на странице, элементы не накладываются друг на друга как попало, а также корректируются все свойства, зависящие от кегля шрифта (скажем, внутренние отступы). Вам не приходится задумываться обо всех этих деталях. Браузер сам выполняет все перечисленные расчеты, и делает это по умолчанию.
Если вы хотите, чтобы ничего из этого не происходило – это тоже можно сделать. Можно жестко ограничить высоту контейнера свойствами max-height
и overflow: auto
. Можно переопределить внутренние отступы, чтобы они измерялись в единицах rem
или px
, то есть, не пересчитывались вслед за кеглем шрифта. Здесь вскрывается интересный аспект работы с CSS: иногда вы не указываете браузеру, что он должен делать, а, фактически, запрещаете ему что-то делать.
Достоинства сетки
Однако, в CSS есть и новые возможности, которые еще круче. Наиболее характерные примеры — Flexbox и Grid. Всего несколько объявлений – и у вас готов исключительно гибкий сетчатый макет, который просто работает. Не приходится беспокоиться о многочисленных частных случаях. Фактически, вы приказываете браузеру: «уложи эти блоки в столбцы по 400 пикселов в ширину каждый», и браузер делает это за вас. На все про все требуется примерно три строки кода.
Если бы вы попытались сделать это императивно, то пришлось бы иметь дело с множеством странных сценариев. Что, если в одном из блоков попадется слишком длинное слово? Что, если область просмотра окажется слишком узкой? А если очень широкой? Что, если в одном элементе целая простыня контента, а в другом – всего несколько слов? Правда, вполне вероятно, что в CSS вам не придется ни о чем из этого задумываться. Все уже продумано за вас и заложено в спецификациях, а браузер выполняет правила за вас. В этом и заключается сила декларативного языка.
Конечно, здесь не обходится и без компромиссов. Если декларативный язык не поддерживает какой-то нужной вам возможности (скажем, «замостить»), остается полагаться на дедовские трюки или на JavaScript. Причем, борьбе именно с такими вещами долгие годы была в значительной мере посвящена разработка CSS. К счастью, с развитием Flexbox и Grid нам под силу гораздо больше, и без всяких хаков (да, незакрепленные элементы – это хак). Если в данной ситуации вам все равно чего-то не хватает, рекомендую почитать о CSS Houdini, которые только начинают приживаться в браузерах.
CSS зависит от контекста
В эру React мы взяли на вооружение исключительно практичный поход: модульную разработку на основе компонентов. Он вполне соответствует рекомендуемым практикам CSS, вместе с BEM, SMACSS и CSS-in-JS. Я не хочу преуменьшать значения всех этих возможностей, поскольку именно они играют ключевую роль при создании крупномасштабных приложений. Но, думаю, не менее важно признать, что CSS не является на 100% модульным, и не должен быть.
На то есть две причины. Первая и наиболее очевидная – приложение должно быть оформлено в глобальных стилях. Практически всегда нужно задать на уровне страницы гарнитуру и кегль шрифта, которые будут использоваться по умолчанию. После этого данные значения будут наследоваться всеми элементами-потомками, которые не станут их явно переопределять. Также вам нужно, чтобы некоторые аспекты вашего дизайна систематически применялись на всей странице: цветовая тема, радиусы скругления для блоков, оттенение блоков и общие размеры полей. Более локальные стили, действующие в отдельных частях страницы, применяются в контексте этих глобальных стилей.
Вторая, более тонкая причина, заключается в том, что и работа CSS, и ваши оформительские решения зависят от контекста страницы. Допустим, мы применим к элементу на странице следующие стили CSS:
.the-thing {
position: absolute;
top: 10px;
left: 10px;
}
Что будет делать этот код? Не зная, где именно этот элемент расположен в пределах DOM, и какие стили применяются на остальной странице, нам на этот вопрос не ответить. Абсолютное размещение делается относительно ближайшего элемента-предка, и такое позиционирование получится разным в зависимости от того, о каком предке идет речь, и какое размещение применялось к нему.
Более того, ваши возможности (или невозможность) накладывать элементы друг на друга будут в значительной степени зависеть от того, где два этих элемента расположены в DOM. Перемешивание элементов в DOM может радикально повлиять на компоновку элементов и на то, как они накладываются друг на друга. Именно поэтому поток документа и контексты наложения – принципиальные (хотя порой и сложные) темы.
Контекстуальная природа CSS сложилась такой отчасти и потому, что так устроен дизайн. Если инженер проектирует мост, то нельзя просто взглянуть на чертеж и сказать: «все хорошо, только вот эта балка мне не нравится; уберите-ка ее». Если убрать одну балку, это отразится на структурном единстве всего моста. Аналогично, достаточно изменить любой элемент дизайна – и все прочие элементы на экране станут восприниматься иначе. Зачастую приходится оформлять сразу несколько элементов в тесном единстве друг с другом.
Например, если увеличить заголовок на одной из плиток, он станет сильнее бросаться в глаза пользователю, и из-за этого другие элементы на экране покажутся менее важными. Здесь ограничения пролегают не в плоскости физики, как при строительстве моста; речь о более «гуманитарных» законах, влияющих на человеческое восприятие. Элементы страницы отражаются на экране, у которого есть собственные физические характеристики, а при работе необходимо учитывать реалии физического мира и то, как мы его воспринимаем.
Как известно, архитектура программного обеспечения подчиняется принципам модульности и инкапсуляции. Такой подход оправдан и на уровне кода, поскольку код сложен, а описанный метод позволяет разбить проблему на удобоваримые фрагменты. Однако, следует учитывать, что такой подход несовершенен. В CSS никогда нельзя полностью абстрагироваться от того, что происходит в конкретном модуле.
Итоги
Три описанных аспекта отличают CSS от традиционных языков программирования. Эти отличия могут не сразу укладываться в голове, но именно они являются наиболее сильными сторонами CSS. Рискну предположить, что разработчики, которым удастся воспринять и как следует усвоить эти принципы, достигнут в CSS настоящих высот.
Автор: ph_piter