Pull to refresh

12 малоизвестных фактов о CSS (продолжение)

Reading time12 min
Views58K
Original author: Louis Lazaris
Больше года назад я опубликовал 12 малоизвестных фактов о CSS (перевод на хабре), и по сей день это была одна из самых популярных статей на SitePoint. С тех пор я собирал больше интересностей и маленьких советов по CSS для новой публикации. Мы же все знаем, что каждый успешный фильм должен способствовать выходу новомодного сиквела, верно?


Автор иллюстрации SitePoint/Natalia Balska.

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

Примечание переводчика
0. Да, я видел опубликованный пару часов назад перевод этой же статьи. Но мне совесть не позволит удалить многодневный труд из-за опоздания на пару часов :) в общем, на ваш суд.

1. Оригинальная статья изобилует рабочими демонстрациями с CodePen. Хабр, к сожалению, не поддерживает вставки с подобных ресурсов, а заменять рабочие динамические примеры статичными картинками, думаю, не имеет смысла. Поэтому статья получилась немного «лысой» простыней, но, я надеюсь, заинтересованные читатели будут открывать недостающие примеры по ссылкам в новом окне.

2. Хотя я имею непосредственное отношение к верстке, перевод получился большим и не таким простым, как показался поначалу. Замечания по ошибкам, опечаткам, терминологии и т.п. просьба присылать личным сообщением в хабрапочту.


1. В свойстве border-radius можно использовать «слэш» синтаксис


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

Верьте или нет, но следующий код валидный:

.box {
  border-radius: 35px 25px 30px 20px / 35px 25px 15px 30px;
}

Если вы никогда не видели подобного синтаксиса, он может показаться немного запутанным, поэтому приведу объяснение из спецификации:

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

В спецификации также приведена следующая схема:



В пояснении к картинке сказано: «Два значения border-top-left-radius: 55pt 25pt определяют кривизну угла».

Таким образом, использование слэша в значениях свойства border-radius позволяет создавать несимметрично изогнутые углы (прим. переводчика: скруглы). Если вы хотите более подробно разобраться в этом, прочтите мою статью (ссылка на которую выше приводилась), или лучше посмотрите удобную интерактивную демонстрацию от MDN.

Большинство border-radius генераторов не позволяют использовать эти необязательные значения. Из тех, что я нашел, генератор от MDN единственный поддерживает «слэш» синтаксис.

2. Свойство font-weight поддерживает ключевые слова относительности


Обычно, когда вы видите описание свойства font-weight, значение в нем либо normal, либо bold. Вы также могли видеть целые значения с инкрементом в сотню: 100, 200, и т.д. вплоть до 900.

Однако, есть два значения, о которых часто забывают: bolder и lighter.

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

В синтаксисе, основанном на целых значениях, 700 означает bold и 400 значит normal. Итак, если ваш шрифт поддерживает толщину 300, но не меньше, значение lighter будет использовать толщину 300, если наследуемая толщина шрифта 400. Если же шрифт не поддерживает более тонкого начертания (т.е. значение по умолчанию 400 является самым тонким), то толщина так и останется 400, и значение lighter не произведет никакого эффекта на текст.

Посмотрите на следующий пример.

В этом примере я использую шрифт Exo 2, который поддерживает 18 разных начертаний. В моем демо не используются курсивные начертания, но шрифт их поддерживает для каждого значения от 100 до 900.

Пример включает в себя 12 вложенных элементов «box» с различными значениями font-weight, включая bolder и lighter, чтобы вы могли видеть как это влияет на «жирность» текста в разных случаях наследования. Ниже под спойлером приведен CSS из примера. Обратите внимание на комментарии в коде, и помните, что каждый следующий элемент «box» вложен в предыдущий:

CSS из примера
.box {
  font-weight: 100;
}
 
.box-2 {
  font-weight: bolder; /* переключает на 400 */
}
 
.box-3 {
  font-weight: bolder; /* переключает на 700 */
}
 
.box-4 {
  font-weight: 400;
}
 
.box-5 {
  font-weight: bolder; /* переключает на 700 */
}
 
.box-6 {
  font-weight: bolder; /* переключает на 900 */
}
 
.box-7 {
  font-weight: 700;
}
 
.box-8 {
  font-weight: bolder; /* переключает на 900 */
}
 
.box-9 {
  font-weight: bolder; /* переключает на 900 */
}
 
.box-10 {
  font-weight: lighter; /* переключает на 700 */
}
 
.box-11 {
  font-weight: lighter; /* переключает на 400 */
}
 
.box-12 {
  font-weight: lighter; /* переключает на 100 */
}


В этом случае ключевые сова «bolder» и «lighter» устанавливают font-weight на значения 100, 400, 700 и 900. С девятью разными стилями эти ключевые слова никогда не приведут к значениям 200, 300, 500, 600 и 800.

Так получается потому что вы говорите браузеру выбрать следующий шрифт в серии, который «жирнее» или «тоньше». Но браузер выбирает не просто следующий вариант жирнее или тоньше, а вариант начертания жирнее или толще относительно текущего. Однако, если самое тонкое начертание шрифта начинается со значения 300 (как у шрифта Open Sans), а значение по умолчанию 400, то использование ключевого слова «lighter» установит font-weight на значение 300.

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

3. Свойство outline-offset


Свойство outline хорошо известно благодаря своему удобству при отладке верстки (контур элемента никак не влияет на окружающие его элементы). В спецификации, однако, также добавлено свойство outline-offset, которое осуществляет именно то, что следует из его названия — оно позволяет задать отступ контура от элемента.

Пример работы outline-offset.

В этом примере подвигайте слайдер вправо/влево, чтобы увидеть как это влияет на отступ контура от элемента. В данном случае отступ меняется от 0px до 30px, но в CSS вы можете задать любой отступ. Следует помнить, что хоть outline и универсальное свойство (одновременно устанавливающее цвет, стиль и толщину внешней границы), оно не включает в себя outline-offset. Поэтому при необходимости outline-offset каждый раз нужно прописывать отдельно.

Единственный минус outline-offset — он поддерживается всеми браузерами, кроме Internet Explorer (не поддерживает даже IE 11).

4. В CSS есть свойство table-layout


Вы, наверное, скажете: «Ха, старо как мир. Я знаю о display:table. Простейший способ вертикального центрирования». Но это не то, о чем я хочу рассказать. Заметьте, я сказал свойство table-layout, а не display.

Свойство table-layout не самая простая CSS фича, чтобы объяснить как оно работает. Поэтому давайте заглянем в спецификацию, а потом рассмотрим пример. Спецификация гласит:

При этом (быстром) алгоритме горизонтальная разбивка таблицы не зависит от контента в ячейках; зависит только от ширины таблицы, ширины колонок и бордюров или расстояния между ячейками.

Это, похоже, первый раз, когда в W3C спецификации что-то трудно понять… Хах, прост шутканул (прим. переводчика: в оригинале «LOL JK»).

А если серьезно, как всегда нам поможет живой пример. В этом демо таблице в CSS добавлено table-layout: fixed. Кликайте по кнопке, чтобы выключать/включать это свойство.

На этом примере вы можете видеть преимущество использования table-layout: fixed над table-layout: auto. Такой подход не всегда будет лучшим выбором и не всегда необходим, но не плохо бы о нем знать, когда речь идет о таблицах с клетками непостоянной ширины.

В прошлом году Крис Коер (Chris Coyier) написал отличную статью о table-layout, поэтому если вы хотите больше узнать об этом свойстве — советую почитать.

5. Свойство vertical-align в ячейках таблицы работает по-другому, нежели в других элементах


Если вы занимались созданием сайтов в середине 2000-х и раньше или часто верстаете HTML письма для email рассылок, вы, наверное, предполагаете, что CSS свойство vertical-align является аналогом старого HTML4 атрибута valign, который в HTML5 уже не поддерживается.

Но CSS свойство vertical-align работает не так же, особенно с таблицами. Это, на мой взгляд, странновато, но все же лучше, чем если бы свойство вообще не работало в таблицах.

Так в чем же разница между применением свойства к обычным элементам и к ячейкам таблиц?

Если vertical-align применяется не к ячейкам таблиц, то следует этим простым правилам:

  • работает только с inline и inline-block элементами;
  • не влияет на соседние элементы в контенте, но влияет на выравнивание элемента относительно соседних inline и inline-block элементов;
  • на свойство влияют настройки шрифта, такие как line-height, и размер соседних inline и inline-block элементов.

В этом примере свойство vertical-align присвоено элементу input. Нажимая на кнопки вы можете устанавливать свойству значения, написанные на кнопках. Обратите внимание, как каждое значение меняет позицию элемента input.

В целом, это довольно простенькая демонстрация работы свойства. Для более глубокого анализа прочтите прошлогоднюю статью Кристофера Оуе (Christopher Aue).

Когда дело доходит до таблиц, vertical-align работает совсем по-другому. В этом случае вы применяете свойство к одной и более ячейкам, и выравнивание контента ячеек зависит от выбранного значения. Пример на CodePen.

Как показано в примере, только 4 значения свойства vertical-align применимы к ячейкам таблиц. Кроме того, значение baseline влияет не только на ячейку, к которой проставляется, но и на соседние ячейки в строке таблицы.

6. Псевдоэлемент ::first-letter умнее, чем вы думали


Псевдоэлемент ::first-letter позволяет стилизовать первую букву любого элемента и дает возможность реализовать эффект буквицы (прим. переводчика: позволил себе сослаться на русскоязычную страницу wiki), который был распространен в печати в течение многих лет.

Хорошая новость в том, что, похоже, браузеры имеют одинаковое представление о «первой букве» элемента. Впервые я прочел об этом в твите Мэта Эндрюса (Matt Andrews), хотя, такое поведение браузеров ему явно не понравилось. Вы можете посмотреть его пример на CodePen.


Мне кажется, что большая четверка браузеров работает с этим свойством одинаково, и это круто, так как я считаю, что это правильное поведение. Было бы немного странно, если бы открывающая скобка считалась первой буквой. В таком случае это было бы похоже на «first character» (первый символ), который мог бы стать самостоятельным псевдоклассом.

7. В HTML вы можете использовать недопустимые символы в качестве разделителя в списке классов элемента


Этот подход обсуждался в 2013 году Бэном Эвардом (Ben Everard), и, я думаю, стоит подробнее рассмотреть эту тему.

В публикации Бэна говорилось об использовании символа слэша ("/") для разделения в HTML списков классов на группы, чтобы сделать код легче для прочтения и понимания. Автор указывает на то, что хотя слеш является недопустимым символом, браузеры не споткнутся на нем, а просто его проигнорируют.

Предположим, у вас такой HTML:

<div class="col col-4 col-8 c-list bx bx--rounded bx--transparent">

Со слешами он будет выглядеть так:

<div class="col col-4 col-8 / c-list / bx bx--rounded bx--transparent">


В качестве разделителей вы можете использовать любые символы (и допустимые, и недопустимые):

<div class="col col-4 col-8 ** c-list ** bx bx--rounded bx--transparent">
 
<div class="col col-4 col-8 || c-list || bx bx--rounded bx--transparent">
 
<div class="col col-4 col-8 && c-list && bx bx--rounded bx--transparent">

Все подобные конструкции отлично работают, можете протестировать на этом демо.

Конечно, эти разделители не могут быть использованы в качестве классов, поэтому я называю их «недопустимыми». Следующий код будет нерабочим и не применит стили к элементу:

./ {
  color: blue;
}

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

.\/ {
  color: blue;
}

Углубляясь в подробности скажу, что символы юникода не нужно экранировать, поэтому вы можете использовать подобные безумные конструкции:

<div class=" ★"></div>


С таким CSS:

. {
  color: hotpink;
}
 
.★ {
  color: yellow;
}

Есть так же возможность экранировать и символы данного типа, вместо их непосредственной вставки. Следующий код эквивалентен предыдущему:

.\2665 {
  color: hotpink;
}
 
.\2605 {
  color: yellow;
}

8. Количество повторов анимации может принимать дробные значения


Вероятно вы в курсе, что при использовании keyframe-анимаций вы можете использовать свойство animation-iteration-count, чтобы установить количество повторов анимации:

.example {
  animation-iteration-count: 3;
}

Целое значение в данном случае говорит о том, что анимацию нужно повторить 3 раза. Но, возможно, вы не в курсе, что можно устанавливать и дробные значения:

.example {
  animation-iteration-count: .5;
}

В данном случае будет запущена только половина анимации (она остановится на половине первой итерации). Давайте посмотрим на пример, в котором анимируются два шарика. У верхнего шарика счетчик повторов установлен на «1», у нижнего на «0.5».

Что интересно, счетчик повторов не зависит от свойства/значения, которые анимируются. Другими словами, если вы анимируете какие-то 100 пикселей, это не значит, что на половине повтора анимация остановится на 50 пикселях. Предыдущий пример использует тайминг-функцию linear, что гарантирует остановку второго мячика на половине пути.

Вот еще две анимации, но тут уже используется тайминг-функция ease: пример. Обратите внимание, что сейчас второй мячик также прошел до остановки половину анимации. Повторю, так получается из-за различных тайминг-функций.

Если вы понимаете тайминг-функции, вы поймете, почему при ease-in-out мячик остановится в той же позиции, что и при linear. Поиграйтесь с различными дробными значениями и тайминг-функциями чтобы увидеть, как это влияет на результат.

9. Animation может не работать из-за имени анимации


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

@keyframes reverse {
  from {
    left: 0;
  }
 
  to {
    left: 300px;
  }
}
 
.example {
  animation: reverse 2s 1s;
}

Заметьте, для анимации я использую имя reverse. На первый взгляд код выглядит рабочим, но обратите внимание что произойдет, если мы используем его на живом примере.

Анимация не работает, так как «reverse» — это зарезервированное ключевое слово для свойства animation-direction. Это произойдет с любой анимацией, имя которой совпадает с зарезервированными ключевыми словами, используемыми в универсальном (кратком) способе записи. При подробной форме записи, с раздельным указанием animation-name и других параметров, анимация будет работать.

Имена анимации, которые могут сломать краткую форму записи, включают в себя имена тайминг-функций, таких как infinite, alternate, running, paused и т.д.

10. Вы можете выбирать диапазон элементов


Не знаю кто первый использовал эту возможность, но я впервые узнал о ней из этого примера от Гуннара Битерсманна (Gunnar Bittersmann). Скажем, у вас есть нумерованный список из 20 элементов и вы хотите выделить элементы с 7 по 14 включительно. Вот как это можно сделать одним селектором:

ol li:nth-child(n+7):nth-child(-n+14) {
  background: lightpink;
}

Посмотрите демо работы селектора.

Дополнение: Как отметили в комментариях, в Safari есть баг, из-за которого эта техника не работает. К счастью, решение, предложенное Мэтом Помаски (Matt Pomaski), похоже работает: просто разверните цепочку псевдоклассов, чтобы получился такой селектор ol li:nth-child(-n+14):nth-child(n+7). В ночных сборках WebKit такого бага нет, поэтому со временем и в Safari подобная конструкция будет работать нормально.

Этот код использует связку псевдоклассов с выражениями. Хотя выражения могут показаться запутанными, вы можете видеть выделяемый диапазон элементов в числах, использованных в этих выражениях.

Как это работает: В первой части связки выражение говорит «выбрать 7-й элемент и каждый после него». Во второй части говорится «выбрать 14-й элемент и каждый до него». Но так как селекторы в связке — каждый ограничивает область выборки другого. Так вторая часть связки не дает первой выбраться за пределы 14-го элемента, а первая часть не дает второй опуститься в выборке ниже 7-го элемента.

Для более детального изучения этих типов селекторов и выражений вы можете прочитать мой старый пост на эту тему.

11. Псевдоэлементы могут быть применены к некоторым одиночным тегам


Возможно вы, как и я, в какой-то момент пытались применить псевдоэлементы к изображениям или элементам форм. Этого сделать не получится, потому что псевдоэлементы не работают с заменяемыми элементами. Я думаю, многие разработчики предполагают, что это касается всех одиночных тегов (не имеющих закрывающего тега). Но это не так.

Вы можете применить псевдоэлементы к некоторым одиночным тегам, которые не являются заменяемыми элементами. Это касается и элемента hr, как видно из этого примера.

Закрашенная область в этом примере — это горизонтальный очерк (элемент hr), и к нему применены оба псевдоэлемента ::before и ::after. Интересно, но мне не удалось получить тот же результат с br, хотя он и является незаменяемым однотеговым элементом.

Вы также можете добавить псевдоэлементы к элементам meta и link, если вы достаточно ненормальны, чтобы сделать их блоковыми display: block, как показано в следующем примере.

12. Некоторые значения в селекторах атрибутов регистронезависимы


В завершение рассмотрим одну небольшую неясность. Предположим, у вас следующий HTML:

<div class="box"></div>
<input type="email">

Вы можете применить стили к каждому из этих элементов, используя селекторы атрибутов, как тут:

div[class="box"] {
  color: blue;
}
 
input[type="email"] {
  border: solid 1px red;
}

Такие стили сработают нормально. А что если сделать так?

div[class="BOX"] {
  color: blue;
}

input[type="EMAIL"] {
  border: solid 1px red;
}

Обратите внимание, что значения атрибутов указаны теперь в верхнем регистре. В этом случае к элементу .box стили не будут применены, так как атрибут class является регистрозависимым. С другой стороны, к полю email стили применятся, потому что значения атрибута type не чувствительны к регистру. Тут нет никакого открытия, но, возможно, вы не знали об этом.

Народ, пора закругляться


В этот момент опускается занавес, и надеюсь-не-слишком-дрянной сиквел заканчивается.

Чувствуется, что каждую неделю я натыкаюсь на небольшие CSS интересности и изучаю их, и, я надеюсь, что что-то из описанного выше оказалось познавательным для большинства из вас. Какие у вас любимые малоизвестные CSS трюки или техника? Знаете ли вы свойства или возможности CSS, которые вы считаете малоизвестными, но хорошо поддерживаемыми браузерами? Поделитесь ими в комментариях.
Tags:
Hubs:
+46
Comments13

Articles