Остановите войну в Украине!

Как я на­учил­ся лю­бить скуч­ные ме­лочи CSS

Перевод «Learning to Love the Boring Bits of CSS»

Перевод Влад Андерсен

Редактура Вадим Макеев

В будущем CSS есть много того, чего нам стоит ожидать с нетерпением: с одной стороны, там будет целый спектр новых методов, которые произведут революцию в вёрстке; с другой стороны — новый набор графических эффектов, которые позволят создавать на лету фильтры и шейдеры. Людям всё это страшно нравится, в журналах и блогах пишется огромное количество статей об этих нововведениях.

Но если эти инструменты можно назвать выставочными лошадками CSS, тогда, думается мне, стоит обратить немного внимания и на рабочих лошадей: на те компоненты языка, которые являются его составными частями: селекторы, единицы измерения, функции. Я частенько называю их «скучными мелочами» — хотя на самом деле я это говорю с большой теплотой, и вам стоит, мне кажется, разделить моё доброе к ним отношение.

Почему? Давайте пробежимся по парочке лучших нововведений в CSS из разряда скучных мелочей — тех мелочей, над которыми работали в плохо освещенных лабораториях, вдали от блеска новых глянцевых штучек на витринах. Некоторые из этих мелочей уже какое-то время с нами, но заслуживают того, чтобы больше людей о них знали; другие, напротив, только начинают появляться в браузерах. Но тем не менее они произведут революцию в том, как мы работаем — причем тихо, скромно и без претензий.

Относительные единицы измеренияСкопировать ссылку

Скорее всего, вы, умный и предусмотрительный веб-разработчик, работаете с относительными единицами измерения — то есть с em или процентами — так что эта проблема вам должна быть знакома: вам наверняка приходится сидеть за калькулятором, чтобы вычислить размеры — из-за наследования. Например, сейчас вполне обычный прием — установить базовый размер шрифта для документа, а потом использовать относительные единицы, чтобы установить размер шрифта для всех остальных элементов на странице. В CSS это выглядит примерно так:

html { font-size: 10px; }
p { font-size: 1.4em; }

Здесь всё отлично, и никакой проблемы нет, пока у вас не появляется дочерний элемент, которому вы хотите установить какой-нибудь другой размер шрифта. Например, в такой разметке:

А и Б сидели на <span>трубе</span>.

Если вы хотите, чтобы этот <span> было меньшего размера шрифта, например, 1.2em, то что вам делать? Берите калькулятор и считайте, сколько будет 1,2 поделить на 1,4, и в итоге у вас получится:

p span { font-size: 0.85714em; }

И проблема не ограничивается использованием em. Если вы разрабатываете тянущийся сайт с использованием ширин в процентах, то знаете, что эти проценты соотносятся с размерами контейнера элемента, так что если у вас есть элемент, которому вы хотите поставить ширину в 40% от его родительского элемента, ширина которого — 75%, тогда придется устанавливать ширину этого элемента в 53,33333%.

Мягко говоря, не идеально.

Размеры относительно корневого элементаСкопировать ссылку

Чтобы побороть эту проблему с размерами шрифтов, теперь нам доступна новая единица измерения — rem (корневой em). Это всё ещё относительная единица измерения, но она всегда соотносится с фиксированным базовым значением, а именно — с размером шрифта корневого элемента документа (в случае HTML это всегда элемент <html>). Если предположить, что мы используем тот же самый размер шрифта для корневого элемента, что и в прошлый раз (10px), то нам потребуются для этого случая следующие CSS-правила:

p { font-size: 1.4rem; }
p span { font-size: 1.2rem; }

Теперь оба правила соотносятся с размером шрифта корневого элемента — это куда более элегантно, да и работать с этим удобнее, особенно если у вас простое базовое значение, например, 10px или 12px. Это как если вернуться назад и использовать значения в пикселях, только с возможностью их масштабировать.

Это одна из функций, перечисленных в этой статье, которая поддерживается браузерами очень хорошо: все современные браузеры, включая IE9, поддерживают эту единицу, нет её пока только в Opera Mobile.

Размеры относительно вьюпортаСкопировать ссылку

Если вам кажется, что единица rem — крутая (а я так думаю!), то вы будете в восторге от того, что узнаете, что существует ещё и новый набор единиц измерения, который поможет победить проблему с процентами. Эти единицы работают примерно так же, как rem, за исключением того, что они соотносятся не с установленным пользователем значением на корневом элементе документа, а с самим вьюпортом, или областью просмотра устройства.

Две базовых единицы здесь — vh и vw; они соотносятся (соответственно) с высотой и шириной вьюпорта. Каждая единица является числом, и это число равняется соответствующему проценту от указанного измерения (ширины или высоты). Пока я ещё помню уроки в школе сценаристов, давайте я лучше не буду объяснять, а покажу:

div { height: 50vh; }

В этом примере высота блока будет равна точно половине высоты вьюпорта; 1vh — это 1% от высоты вьюпорта, так что равенство 50vh = 50% от этой высоты выглядит вполне логичным.

Если размер окна просмотра изменяется, то изменяется и значение соответствующих единиц, и при этом вам не нужно беспокоиться о вложенных элементах: элемент с шириной 10vw всегда будет этой ширины, вне зависимости от ширины его родительского элемента.

Также есть единица vmin, которая соответствует меньшему значению — vh или vw; кроме того, недавно стало известно, что в спецификацию добавится и соответствующая единица vmax (хотя на момент написания статьи этого пока не случилось).

Сейчас эти единицы поддерживаются в IE9+, Chrome и Safari 6.

Рассчитываемые значенияСкопировать ссылку

Если вы будете работать с тянущимся или адаптивным дизайном, вы, без сомнения, столкнетесь с проблемой смешивания единиц измерения — когда вы хотите, чтобы у вас на странице была сетка, ширина у которой задается в процентах, но при этом с фиксированными полями. Например:

div {
    margin: 0 20px;
    width: 33%;
}

Если в вашей верстке указаны только padding и border, тогда, в принципе, можно решить проблему с помощью box-sizing, но с margin это не поможет. Есть лучший и более гибкий подход — использовать для значения функцию calc(), которая позволяет вам производить математические действия с разными единицами, скажем:

div {
    margin: 0 20px;
    width: calc(33% - 40px);
}

Вы можете применяться её где угодно, не только для ширин, но и везде, где вам нужны единицы изменения. А если вы хотите зайти совсем далеко, то вы тоже можете даже использовать calc() внутри calc().

В IE9+ эта функция поддерживается без префикса(!), в Firefox — с префиксом -moz- (в релизе 16 или 17 он должен быть отброшен), а в Chrome и Safari — с префиксом -webkit-. В мобильный WebKit он, увы, пока не включен.

Загрузка подмножества символовСкопировать ссылку

Быстрая загрузка веб-страниц всегда была важна — но сейчас, с появлением на рынке широкого спектра мобильных устройств (с каждым из которых понятие «скорость соединения» становится всё более изменчивым и неопределенным) это, пожалуй, особенно важно. Один из способов ускорить загрузку страницы — сократить размер внешних подгружаемых файлов, и поэтому новое свойство внутри @font-face, которое позволяет делать именно это — весьма полезное добавление.

Свойство, о котором идет речь — unicode-range, и в качестве значения оно принимает набор ссылок на юникод-символы. При загрузке внешних ресурсов из файла шрифта будут загружаться только эти символы, а не все символы, присутствующие в шрифте. Приведенный код показывает, как загрузить только три символа из файла foo.ttf:

@font-face {
    font-family: foo;
    src: url('foo.ttf');
    unicode-range: U+31-33;
}

Особенно это полезно в том случае, если вы используете иконки внутри шрифта и хотите показывать на конкретной странице не все из них, а только конкретные. В одном тесте, который я провел, использование unicode-range сократило общее время загрузки файла шрифта в среднем на 0,85 секунды — а это вполне существенно. Конечно, у вас могут получиться и другие цифры.

Это свойство на данный момент поддерживается в IE9+ и браузерах на движке WebKit: Chrome и Safari.

Новые псевдоклассыСкопировать ссылку

Единицы измерения и значения — это всё прекрасно, но особенно меня радуют селекторы и псевдоклассы. Когда у меня получается выдумать замысловатый селектор, даже если он в итоге будет запрятан там, где только немногие избранные смогут его найти, я всегда чувствую себя мастером своего дела. Перефразируя отца Стива Джобса: ты должен сделать так, чтобы забор со стороны твоего дома выглядел так же хорошо, как со стороны улицы, даже если больше никто не будет знать, что это так — ведь ты будешь.

Для меня стало откровением, когда я впервые использовал :nth-of-type(), это было словно я вышиб ногой двери восприятия… Окей, я чуть-чуть преувеличиваю. Но есть несколько новых CSS-псевдоклассов, относительно которых действительно стоит испытывать энтузиазм.

Псевдокласс отрицанияСкопировать ссылку

Вы наверняка не поймете, насколько в действительности полезен новый (относительно) псевдокласс отрицания :not(), пока сами его не попробуете. В качестве аргумента :not() передается простой, а не составной селектор. Когда список элементов создается селектором, включающим :not(), все элементы, которые соответствуют аргументу, исключаются из этого списка. Знаю, звучит сложно, мне тоже так кажется. Но на самом деле всё довольно просто.

Представьте себе: у вас есть список, и вы хотите применить правило ко всем нечётным элементам в списке, но не к последнему. Вам придётся написать что-то вроде такого:

li { color: #00f; }
li:nth-child(odd) { color: #f00; }
li:last-child { color: #00f; }

С псевдоклассом отрицания вы можете исключить последний элемент из списка, используя в качестве аргумента :last-child. Таким образом количество правил уменьшится, и с кодом станет легче работать:

li { color: #00F; }
li:nth-child(odd):not(:last-child) { color: #F00; }

Ничего принципиально нового здесь нет, и, как я показал, вполне можно работать и без него, но — это довольно-таки полезно. У меня была возможность применить этот псевдокласс в проекте, где использовался встроенный WebKit, и я раз за разом убеждался в его пользе. Честно, это один из моих любимых псевдоклассов.

Всё верно, у меня есть любимые псевдоклассы.

Из всех функций, рассмотренных в этой статье, это самая широко поддерживаемая; её поддерживают IE9+ и все современные браузеры — без префикса. Если вы работаете с jQuery, вполне возможно, что вы уже привыкли использовать этот синтаксис — в jQuery он есть начиная с версии 1.0, как и метод not(), который делает то же самое.

Псевдокласс соответствияСкопировать ссылку

Псевдокласс соответствия :matches() принимает в качестве аргумента простой селектор, составной селектор, список, разделенный запятой, или любую комбинацию этих пунктов. Отлично! Но что же он делает?

Лучше всего он подходит для того, чтобы срезать лишнее с повторяющихся селекторов. В качестве сценария использования представьте себе, что у вас есть несколько элементов <p> в разных контейнерах, но вы хотите выбрать только некоторые из них; тогда правило в стилях будет выглядеть примерно так:

.home header p,
.home footer p,
.home aside p {
    color: #f00;
}

С селектором :matches() вы можете значительно сократить его, найдя сходство в селекторах; в нашем примере у нас везде в начале стоит .home, а конце — p, так что мы можем использовать :matches() для того чтобы собрать все элементы между ними. Непонятно? Это выглядит вот так:

.home :matches(header,footer,aside) p { color: #f00; }

В действительности это уже часть CSS4 (если быть совсем точными, спецификации CSS-селекторов уровня 4), и ещё в этой спецификации сказано, что вы сможете использовать такой же синтаксис — составные селекторы, разделенные запятой — в будущих версиях :not(). Здорово-то как!

На сегодняшний день :matches() есть в Chrome и Safari с префиксом -webkit-, а в Firefox он проходит под своим старым названием, :any(), с префиксом -moz-.

Ну как, уже полюбили рабочих лошадок?Скопировать ссылку

Самое лучшее во всех этих новых возможностях — они решают совершенно реальные проблемы, от маленького, но раздражающего повторения селекторов до новых и появляющихся в настоящее время сложных задач, связанных с разработкой высокопроизводительных адаптивных сайтов. На самом деле, я могу легко представить, что регулярно использую каждое из этих нововведений.

Может быть, про новые возможности вроде фильтров больше пишут, но вы с гораздо большей вероятностью обнаружите, что именно те мелочи, которые представлены в этой статье, будут вам полезны в разработке.

Каждая из них сделает вашу профессиональную жизнь немножко проще, и в то же время расширит пространство ваших возможностей — а в этом ничего скучного, поверьте, нет.