Работа с тенями

Мы попробуем разобраться в том, как работают тени в новых модулях CSS3. С практической точки зрения, мы рассмотрим два правила: box-shadow и text-shadow, определенные соответственно в модулях CSS3 Backgrounds and Borders и CSS3 Text.

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

Первая часть посвещена работе с box-shadow, во второй мы пройдемся по теням для текста.

box-shadow

Сдвиги и цвет

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

box-shadow: 3px 3px;

Положительные значения сдвига смещают тень вправо и вниз, отрицательные — влево и вверх.

По умолчанию, если цвет тени не задан, в большинстве браузеров (все, кроме webkit-based) он берется из цвета текста (color) в текущем контексте (1.2), впрочем, похоже, этот момент спецификацией не обговаривается:

box-shadow: 3px 3px; color:blue;

Чтобы задать цвет тени, достаточно указать его дополнительным параметром (1.3):

box-shadow: 3px 3px darkgreen;

Очевидно, цвет можно указывать любым из доступных способов: от прямого указания названия и шестнадцатеричного кода, до rgb или rgba и hsla с прозрачностью (мы рассмотрим такие примеры чуть позже).

Размытие


Третий «линейный» параметр, который можно задать при описании тени — это радиус размытия (blur), положительная величина, указывающая насколько сильно нужно размывать тень по пространству (2.1–2.3):

box-shadow:3px 3px 3px darkgrey;

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

box-shadow:0 0 9px black;

Сам алгоритм размытия спецификацией не описывается, кроме указания того, что это должен быть эффект, аналогичный размытию по Гауссу (Gaussian blur) с половинным радиусом в обе стороны от границы тени (2.4):

Растяжение


Еще один интересный параметр — это растяжение или распространие тени (spray), позволяющее увеличить или уменьшить ее размеры (по умолчанию размеры тени соответствуют размеру исходного объекта). Надо отметить, что этот параметр не сразу появился в спецификации, поэтому во множестве примеров в интернете он просто не рассматривается.
Для увеличения тени нужно указать положительный spray-параметр (3.1, 3.2):

box-shadow:6px 6px 0px 4px darkred;

Для уменьшения — отрицательный (3.3):

box-shadow:12px 12px 8px -4px darkred;

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

На примере выше (3.4) тень смещена на 6px вниз и влево и увеличена на 8px с каждой стороны:

box-shadow:6px 6px 0 8px grey;

Если в вашем блоке используются скругленные уголки, будьте готовы к тому, что в расширенной тени радиус скругления также будет пропорционально смаштабирован (3.5):

Внутренняя тень


Наконец, еще один хитрый параметр — это возможность применения тени внутри блока. Для этого используется специальное ключевое слово inset (4.1-4.4):

box-shadow:inset 4px 4px rgba(66,66,66,0.5); /* (4.1) */
box-shadow:inset 4px 4px 0 8px rgba(198,198,198,1); /* (4.2) */
box-shadow:inset -2px -2px 8px 0px black; /* (4.3) */
box-shadow:inset 0 0 4px 0px black; /* (4.4) */

Обратите внимание, что внутренняя тень отрисовывается только внутри блока, к которому применено соответствующее правило, причем применение spray-параметра для внутренней тени (4.2) в отличие от внешней приводит к уменьшению внутреннего перимерта тени.

Множественные тени

Ну и теперь еще один нюанс: на самом деле, к блокам можно применять любое количество теней одновременно, для этого достаточно их перечислить через запятую при описании box-shadow.

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

box-shadow: 0 0 2px 1px red,
            0 0 2px 2px orange,
            0 0 2px 3px yellow,
            0 0 2px 4px green,
            0 0 2px 5px lightblue,
            0 0 2px 6px blue,
            0 0 2px 7px violet;

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

Так как тени независимы, вы легко можете сочетать тени, выстроенные в разных направлениях (5.2):

box-shadow: -6px -6px 8px -4px rgba(255,0,0,0.75),
            6px -6px 8px -4px rgba(0,255,0,0.75),
            6px 6px 8px -4px rgba(255,255,0,0.75),
            -6px 6px 8px -4px rgba(0,0,255,0.75);


Аналогично, можно сразу задавать внутренние и внешние тени (5.3):

box-shadow: inset 0 0 8px lightgray,
            1px 1px 3px darkgray;


Или «продвинутое подчеркивание» (5.4):

box-shadow: 0 1px red,
            0 3px 3px -2px black


Или, если проявить еще немного фантазии и дополнительных спецэффектов, сделать slick-box, описанный, например, у Matt Hamm (5.5):

.slick-box {
    position: relative;
    height: 50px;
    border: 1px solid #efefef;
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.06) inset;
}

.slick-box:before, .slick-box:after {
    content: '';
    z-index: -1;
    position: absolute;
    left: 10px;
    bottom: 10px;
    width: 70%;
    max-width: 300px; /* avoid rotation causing ugly appearance at large container widths */
    height: 55%;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
    transform: skew(-15deg) rotate(-6deg);                 
}
            
.slick-box:after {
    left: auto;
    right: 10px;
    transform: skew(15deg) rotate(6deg);                 
}

(Для упрощения, я убрал код с вендорными префиксами, но вам нужно будет добавить -ms-transform, -webkit-transform и т.д.)

Общий синтаксис

Резюмируя, синтаксис для описания теней выглядит следующим образом:

box-shadow: <shadow> [ , <shadow> ]*;
<shadow> = inset? && [ <length>{2,4} && <color>? ]

Последнее в полном виде разворачивается в следующую схему:

box-shadow: inset? h-offset v-offset blur-radius spread-distance color;

Радиус размытия и растяжение не являются обязательными. inset переключает режим отображения тени с внешней на внутреннюю.

Интерактив


Если вы хотите просто поиграться с тенями в интерактивном режиме, наши коллеги к прошедшей в сентябре конференции Build подготовили демонстрационную страницу: "Hands-on: box-shadow".

Internet Exlorer

Насущный для многих вопрос: box-shadow поддерживается в IE9 и выше.

И еще одна важная деталь: стандартные css-правила, начиная с 9й версии Internet Explorer, работают с использованием аппаратного ускорения — в отличие от нестандартных старых фильтров вроде filter:DXImageTransform.Microsoft.Shadow. То есть, использовать стандарты не только правильнее, но и эффективнее.

Мой совет: старайтесь использовать стандартные возможности, исходя из идей progressive enhacenment.

Часть 2

Продолжаем погружаться в искусство владения тенями в новых модулях CSS3. Прошлый раз мы рассматривали работу с box-shadow, сегодня мы перейдем к text-shadow.

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

text-shadow vs box-shadow

Если вы вернетесь к разделу про синтаксис box-shadow в первой части, вы найдете такое описание:

box-shadow: <shadow> [ , <shadow> ]*;
<shadow> = inset? && [ <length>{2,4} && <color>? ]

Где последнее разворачивается в полном виде в такую конструкцию:

box-shadow: inset? h-offset v-offset blur-radius spread-distance color;

Спецификация CSS3 Text, описывая text-shadow, говорит буквально следующее:

<shadow> is the same as defined for the ‘box-shadow’ property except that the
‘inset’ keyword is not allowed

В переводе на русский это означает, что для текста невозможны внутренние тени (inset) и синтаксис для text-shadow выглядит следующим образом:

text-shadow: <shadow> [ , <shadow> ]*;
<shadow> = [ <length>{2,4} && <color>? ]

Аналогично box-shadow возможны множественные тени накладываемые поверх друг друга так, что первая тень оказывается наверху. Теперь давайте смотреть, как это все работает.

text-shadow

Сдвиги и цвет

Начнем со сдвигов и работы с цветом. За сдвиги тени отвечают первые два линейных параметра, указывающих длинну (1.1–1.4):

Если вы указываете положительные значения, тень сдвигается влево и вниз (1.1):

text-shadow:10px 10px; width:300px;

Отрицательные значения сдвигают вправо и вверх (1.2):

text-shadow:-5px -5px; width:300px; color:blue;

Аналогично box-shadow, все браузеры, кроме основанных на webkit, если цвет тени явно не задан, берут его из цвета текста (1.1–1.2). Это может быть полезным, например, если вы хотите автоматически делать тень цвета текста, но, например, размытой (см. примеры 2.3 и 2.4). Для явного задания цвета тени достаточно прописать нужное значение в правиле:

text-shadow:-1px -1px white; color:blue; background:#333; /* 1.3 */

text-shadow:1px 1px rgba(255,255,255, 0.5); color:blue; background:#eee; /* 1.4 */

Обратите внимание, что при указании цвета полностью применимы возможности CSS3 Color, включая указание уровня прозрачности цвета (alpha) через rgba или hsla.

Размытие

Третий линейный параметр описывает радиус размытия тени (2.1–2.4):

В полном согласии с определением box-shadow, размытие задается некоторым положительным числом — радиусом размытия. Само размытие может осуществляться UA по любому алгоритму с большой точностью аналогичному размытию по Гауссу с половинным радиусом относительно границы тени.

В первых двух примерах (2.1 и 2.2) задан разный радиус размытия:

text-shadow:5px 5px 3px darkred; color:red; /* 2.1 */

text-shadow:4px -4px 10px red; color:azure; background:#333; /* 2.2 */

Во второй паре примеров (2.3 и 2.4) различается только цвет текста и фона, а сами правила задания тени описаны через применяемый к блокам класс blured-shadow:

.blured-shadow {
    text-shadow:0px 0px 4px ; /* не зависит от цвета текста */
}

color:red; /* 2.3 */

color:lightgray; background:#333; /* 2.4 */

(Этот пример некорректно работает в Chrome из-за описанных выше нюансов, впрочем спецификация говорит, что пропущенный цвет остается на усмотрение UA.)

Растяжение и сжатие

Четвертый линейный параметр, если он присутствует, отвечает за растяжение или сжатие тени.

Для увеличения тени spray-distance должен быть положительным (3.1):

text-shadow:5px 5px 0px 3px lightgreen; color:green;

Для уменьшения — отрицательным (3.2):

text-shadow:8px 8px 2px -3px darkgreen; color:green; font-weight:900;

Если отступ тени нулевой, ее можно использовать для обводки текста (3.3):

text-shadow:0 0 0 3px rgba(128, 255, 0, 0.75); color:green; background:#333;

Важная деталь. Насколько мне известно, в настоящий момент (сюрприз!) параметр spray-distance для text-shadow поддерживается только в Internet Explorer 10, а остальные браузеры его не поддерживают (см. например, bug 655590 "[css3-text] Support the spread radius in text-shadow" в Mozilla Bug tracker). Это же верно и в отношении большинства учебников и статей в интернете, которые своевременно не были обновлены вслед за изменениями в спецификации. Поэтому в большинстве примеров, которые вы найдете в сети, вы даже не увидите упоминания возможности растяжения или сжатия тени текста ;)

И еще одна важная деталь. Наличие четвертого параметра сегодня трактуется неподдерживающими его браузерами как неправильное задание тени — в результате эти правила просто игнорируются. Поэтому для обеспечения хоть какого-то уровня совместимости, если вы используете spread-distance, необходимо дублировать правила, например, так:

text-shadow: 0px 0px 10px lightgreen; /* 3.4 */
text-shadow: 0px 0px 10px 10px lightgreen; /* 3.5 */

Если вы все же хотите смоделировать увеличение тени, в определенных пределах это можно сделать через множественные тени, рассматриваемые в следующем разделе (см. примеры 4.6 и 4.7).

Множественные тени

Наконец, аналогично теням для блоков, к тексту также можно применять несколько теней одновременно, добиваясь при этом различных эффектов (4.1–4.5):

Начиная с простейшей дублированной обводки (4.1):

text-shadow: 0 0 0 3px white, 0 0 0 4px gray; color:magenta;

И возможности смещения теней в разные стороны (4.2):

text-shadow: 3px 3px 4px 2px rgba(255,255,255,0.35), 
             6px -6px 4px 2px rgba(255,255,255,0.25), 
             -3px -3px 4px 6px rgba(255,0,255,0.15);

Продолжая эффектами типа неона (4.3):

text-shadow: 0 0 0 3px white, 
             0 0 2px 6px magenta,
             0 0 1px 9px white,
             0 0 6px 12px magenta;

И немного более изощренным вариантом (4.4)

text-shadow: 0 0 2px #fff,
             0 0 4px 2px rgba(255,255,255,0.5),
             0 0 6px 6px #f0f,
             0 0 4px 7px #fff,
             0 0 3px 15px #222,
             -4px 0 2px 9px #f0f,
             4px 0 2px 9px #f0f,
             0 -4px 2px 9px #f0f,
             0 4px 2px 9px #f0f;

Или же с ограниченным использованием предыдущего приема перекрытия смещенных теней — небольшое подчеркивание (4.5):

text-shadow: 0 -3px 3px 15px white, 0 1px 2px 9px;
color:magenta;

Эмуляция растяжения

Как было сказано в предыдущем разделе, технически множественные тени можно применять для эмулирования увеличения тени. Например, чтобы сделать что-то похожее на (4.6):

text-shadow: 0px 0px 0px 4px magenta;

Можно было бы применить одновременно несколько теней, сдвинутых в разных направлениях (4.7):

text-shadow: magenta 0px 2px, 
             magenta 2px 0px, 
             magenta -2px 0px, 
             magenta 0px-2px, 
             magenta -1.4px -1.4px, 
             magenta 1.4px 1.4px, 
             magenta 1.4px -1.4px, 
             magenta -1.4px 1.4px;

Однако, если приглядеться, между ними заметна разница. Также важно понимать, что такой прием ограничен в применимости: он не только менее точен, но и отрицательно сказывается на производительности отрисовки страницы.

Интересные примеры

Теперь давайте рассмотрим еще несколько примеров использования теней для реализации различных интересных эффектов.

Начнем с классической радуги (5.1):


text-shadow: 0 0 2px 3px yellow,
             0 0 2px 6px orange,
             0 0 2px 9px red,
             0 0 2px 12px lime,
             0 0 2px 15px blue,
             0 0 2px 18px violet;

Двойная тень для стрелки (5.2):

text-shadow: 0 0 2px 2px white,
             2px 0 2px 5px #222,
             3px 0 3px 6px #933,
             5px 0 2px 14px #222,
             6px 0 5px 16px #533;

Традиционная огненная тень (5.3):

text-shadow: 0 0 2px #eee,
             0 0 4px 2px #fff,
             0 -2px 4px 2px #ff3,
             2px -4px 6px 4px #fd3,
             -2px -6px 11px 6px #f80,
             2px -8px 18px 8px #f20;

Традиционный «letter-press», — здесь также важен контраст с фоном (5.4):

text-shadow: 0px 2px 3px #555;

Не менее традиционный 3d-text (5.5):

text-shadow: 0 0 1px #999,
             1px 1px 1px #888,
             2px 2px 1px #777,
             3px 3px 1px #666,
             4px 4px 1px #555,
             5px 5px 1px #444;

Двойная тень для винтажного эффекта (5.6)

text-shadow: 2px 2px #fff,
             3px 3px #666;

Проступающая надпись с прозрачным текстом и сжатой тенью, — также зависит от размера и гарнитуры шрифта (5.7)

text-shadow: 0 0 2px -3px rgba(196,255,0,0.3),
             0 0 6px -5px #9c6;
color:transparent;

Применение отдельной буквы для псевдо-класса ::first-letter (5.8)

.text {
    text-shadow:0 0 5px;
}
            
.text::first-letter {
    color:azure;
    text-shadow:0 0 5px, 0 0px 6px 3px blue, 0 -2px 6px 6px cyan, 0 -4px 9px 9px lightblue ;
}

Интерактив



Если вы хотите просто поиграться с тенями в интерактивном режиме, наши коллеги к прошедшей в сентябре конференции Build подготовили демонстрационную страницу: "Hands-on: text-shadow".

Internet Exlorer

text-shadow поддерживается в IE10+.

И повторю свой совет относительно использования фильтров: не используйте фильтры вообще, либо продумывайте верстку и стили так, чтобы для IE9+ они не применялись. Стандартные css-эффекты в IE в отличие от старых фильтров, начиная с 9й версии, работают с использованием аппаратного ускорения, к тому же фильтры часто оказываются несочетаемыми с новыми правилами свойствами CSS и их одновременное использование может приводить к неожиданным последствиям.

Смотрите также руководство для разработчиков по Internet Explorer 10.