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