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

Питер Гастон  21 августа 2012

В будущем 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-.

Ну как, уже полюбили рабочих лошадок?

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

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

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

Перевод оригинальной записи «Learning to Love the Boring Bits of CSS» Питера Гастона (Peter Gasston), опубликованной на сайте A List Apart. Переведено и опубликовано с разрешения автора.

Перевод выполнил Влад Андерсен.

Теги: ,

Комментарии +

  1. Владислав 4 сентября 2012 в 12:15

    вы, умный и предусмотрительный веб-разработчик, работаете с относительными единицами измерения — то есть с em

    По-моему, верстка в em это уже анахронизм.
    А статья к сожалению аналогична большинству статей по css. Этот селектор делает то-то, этот то-то. Для этого существуют справочники.

  2. Дима 4 сентября 2012 в 12:16

    К сожалению до использования этих всех вещей на реальных крупных проектах как минимум два года. Минимум.

  3. Seva 4 сентября 2012 в 14:50

    Хорошая статья, спасибо!

  4. WinterSun 4 сентября 2012 в 15:56

    Спасибо за статью, очень будут полезными эти нововведения. Нужно быть наготове)

  5. psywalker 4 сентября 2012 в 20:53

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

    Во-первых, нужно уточнить, что calc() пашет в Safari только с версии 6.0, а во-вторых, разве в Opera calc() пашет?

  6. Вадим Макеев 4 сентября 2012 в 22:05

    Максим, суть процитированного фрагмента в том, что в нём перечисляются стабильные версии браузеров, в которых поддерживается calc(). Не поддерживается — не упомянут, текущая стабильная версия Safari 6. Мне кажется, что не стоит делать энциклопедическую справку из авторского текста.

  7. psywalker 4 сентября 2012 в 22:32

    Согласен. Вадим, а когда ожидается поддержка в Opera? Просто очень полезная вещь, хотелось бы использовать её во всех современных браузерах.

  8. Вадим Макеев 4 сентября 2012 в 22:34

    Максим, вероятно в ближайшее время вместе с vh/vw, над задачей уже работают.

  9. psywalker 4 сентября 2012 в 22:35

    Спасибо! Будем ждать :)

  10. Владислав 5 сентября 2012 в 0:10

    Вадим, кстати, а опера, реализовывая поддержку vh/vw, будет учитывать ширину скроллбара при расчете ширины вьюпорта. А то вот хром на данный момент не учитывает и по-моему это большой косяк.

  11. Вадим Макеев 5 сентября 2012 в 0:20

    Владислав, уточню у наших разработчиков, спасибо.

  12. Андрей Ситник 5 сентября 2012 в 5:23

    Каким образом указание unicode-range позволяет снизить скорость загрузки шрифтов из Интернета? Браузер же не знает в каком месте файла шрифтов находиться нужный диапазон?

    У меня два варианта:
    1. Браузер начинает грузить файл сначала и останавливается загрузки все указанные символы. Но тогда преимщество не такое серьёзное (снижает размер только в двое, но в реальности думаю ещё хуже).
    2. Речь идёт не о загрузке через Интернет, а о загрузке в память. Тогда свойство всё равно не очень полезное — так как мощность устройств растёт как на дрожах.

  13. SelenIT 5 сентября 2012 в 17:39

    Андрей Ситник, кстати, дельное замечание! К оригинальной статье тоже есть коммент на эту тему, автор на него пока не ответил...

    Насколько я сам понимаю спецификацию:

    The unicode-range descriptor serves as a hint for user agents when deciding whether or not to download a font resource.

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

  14. Seva 16 сентября 2012 в 18:18

    rem штука нужная но

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

    зачем мне нужно уменьшая шрифт в блоке, отталкиваться от базового размера шрифта, он визуально должен вписываться в окружение то есть именно отсчёт должен идти от блока в котором он находиться, зачем мне нужно ему ставить именно 1.2em от базового? Если я мог бы поставить ему 0.8 , 0.85 или 0,9 и не париться. Да может быть исключение и нам нужен будет именно точный размер, тогда да нужен будет rem, но пример описанный выше не очень подходит, разве что как учебный...

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

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

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

  15. Михаил 20 сентября 2012 в 20:07

    статья прекрасна, но перевод, конечно..... мда)

  16. Вадим Макеев 21 сентября 2012 в 15:44

    Михаил, если вы находите, что какие-то фразы переведены неудачно, мы будем очень благодарны, если вы расскажете об этом в письме в редакцию: wst@web-standards.ru. Но уверяю вас, над этим текстом работало три человека: профессиональный переводчик, литред и редактор.

  17. Артем 21 декабря 2012 в 16:36

    Это конечно все хорошо и удобно, но, учитывая, например, что мой основной заказчик - предприятия с заточенным корпоративным софтом под ie6-7, лично для меня это, мягко говоря, бесполезно.
    Все сейчас кричат направо и налево, дескать HTML5! CSS3! Наконец-то! Ура! Отказывайтесь от XHTML, CSS2! Ага...
    Только вот полноценно ими сейчас пользоваться не получится, если хочешь зарабатывать деньги, конечно :)
    Особенно смешит, когда спрашиваешь на форуме верстальщиков, простой пример, как лучше сделать скругленные углы и все перебивая друг-друга назидательно советуют "border-radius, конечно же!!!1 Вот глупый, такую ерунду спрашивает".
    И начинаешь задумываться, кто же на самом деле эти "специалисты" с десятками тысяч сообщений. Диванные теоретики?

    Для своего личного сайта - да, пожалуйста, хоть заглушку на ie ставь, по типу - "пройдите скачайте нормальный браузер". А вот повседневного заказчика такое, к сожалению, не устроит.

  18. rassadin 10 января 2013 в 22:48

    Единственное, что поддержка vw/vh, как и calc() реализована iOS 6.0, а на большинство iPad и iPad2, а их миллионы, работают на 4 и 5 версиях и врядли когда обновятся.
    Так что облизнулись, спасибо, привет Apple.

  19. Даниил Пронин 6 августа 2014 в 16:25

    Вот что-что, а `:matches()` действительно может сильно сократить размер CSS за счёт исключения повторений длинных селекторов (не люблю сокращать слова) в сложных нагромождениях. Сейчас за меня это делает SASS, но когда The IE8 Countdown достигнет 1% (или хотя бы 5% в России), я переложу эту работу с препроцессора на CSS.

Перейти к началу