Свойство background-clip и его применения

Свойство background-clip и его применения

background-clip — одно из тех свойств, о которых я давно знала, но почти не использовала. Может, лишь пару раз в ответах на Stack Overflow. До прошлого года, когда я начала создавать эту огромную коллекцию ползунковых регуляторов (слайдеров). Некоторые дизайны, что я решила воспроизвести, были довольно-таки сложны, а мне на каждый ползунок отводился один-единственный элемент, и им как назло оказался input , к которому даже псевдоэлементов толком не добавить. Хоть в некоторых браузерах такое и работает, то, что оно работает — на самом деле баг, и мне не хотелось на это полагаться. Так что в итоге мне пришлось использовать фоны, рамки и тени в изобилии. И я многому научилась при этом, и в этой статье делюсь некоторыми их тех уроков.

Первым делом давайте посмотрим, что такое background-clip и что оно делает.

На следующей картинке у нас блочная (или всё-таки боксовая? — прим. перев.) модель элемента.

Если padding равен 0 , то padding-box по размеру точно равен content-box , и край контента совпадает с краем внутреннего отступа.

Если border-width равен 0 , то border-box по размеру точно равен padding-box , и край рамки совпадает с краем внутреннего отступа.

при border-width: 0

Если и padding , и border-width оба равны 0 , то все три области ( content-box , padding-box и border-box ) равны по размеру, и края контента, внутреннего отступа и рамки все совпадают друг с другом.

при padding: 0 и border-width: 0

По умолчанию фоны покрывают всю border-box (под рамкой они тоже накладываются), но их background-position (а также проценты для background-size ) отсчитываются от padding-box .

Чтобы лучше это понять, рассмотрим пример. Возьмем блок с наугад выбранными размерами, зададим ему простой градиентный фон с background-size: 50% 50% и заштрихованный border (с помощью border-image ), так что сквозь штрихи нам будет видно, что там под рамкой:

В этом примере видно, что градиентный фон целиком покрывает border-box (его видно под штрихованной рамкой). Мы не указывали ему background-position , так что у него значение по умолчанию — 0 0 . Видно, что оно отсчитывается от padding-box , потому что начинается от левого верхнего угла (точки 0 0 ) этой области. Видно также, что background-size , указанный в процентах, отсчитывается от padding-box .

по умолчанию фон полностью покрывает border-box , но начинается в левой верхней точке padding-box

Задавая background-size для градиентов (но не для настоящих картинок), мы обычно вынуждены указывать два значения ради единообразия результата в браузерах. Если указать лишь одно значение, то второе в Firefox станет равным 100% (по спецификации), тогда как все остальные браузеры ошибочно делают второе значение равным первому. Пропущенное значение background-size принимается за auto , и так как у градиентов нет внутренних размеров и пропорций, значение auto никак из них не высчитать, так что оно должно восприниматься как 100% . Поэтому, если только мы не собираемся растягивать background-size на 100% по обоим измерениям, нам понадобятся два значения.

Задание background-size с одним значением не дает кроссбраузерного результата (можно проверить); слева: Firefox (по спецификации, второе значение считается равным 100% ); справа: Chrome/ Opera, Safari, IE/ Edge (ошибочно делают второе значение равным первому)

Можно заставить фон покрывать только padding-box или только content-box с помощью background-clip . Обрезка (clipping) означает, что всё, что выступает за обрезаемую область, отсекается и не показывается. Обрезаемой областью на иллюстрации ниже будет то, что лежит внутри пунктирной линии.

иллюстрация, что такое обрезка

При background-clip: border-box (по умолчанию) обрезаемой областью будет border-box , так что у нас будет фон и под рамкой.

Если задать background-clip: padding-box , обрезаемой областью будет padding-box , так что фон будет отображаться только в пределах padding-box (под рамкой его уже не будет).

И наконец, при background-clip: content-box обрезаемой областью будет content-box , так что фон будет показан только в пределах content-box .

Эти три случая показаны в следующем живом примере:

У нас есть и еще одно свойство под названием background-origin , которое указывает, от какой из трех областей отсчитывается background-position (и background-size , если выражен в процентах).

Допустим, у нас опять есть элемент с заштрихованным border , но, на этот раз, с видимым padding . Положим на фон обычную картинку и градиент. У обоих background-size: 50% 50% , и оба они не повторяются. В добавок к этому, у картинки будет background-position: 100% 100% (а для градиента оставим 0 0 по умолчанию):

Следующий пример показывает, что происходит при каждом из трех возможных значений background-origin — border-box , padding-box и content-box :

Значение 100% 100% , указанное для background-position настоящей картинки — это 100% 100% от области, указанной в background-origin . В то же время, 50% 50% , указанные для background-size , означают половину ширины и половину высоты области, указанной в background-origin .

В сокращенной записи background свойства background-origin и background-clip можно указывать именно в таком порядке в конце слоя. Поскольку они оба принимают значение вида *- box , то если указать лишь одно значение такого типа, оно будет присвоено сразу им обоим. Если указать два значения, background-origin получит первое, а background-clip — второе. Если не указывать значений вида *- box , оба свойства останутся со своими значениями по умолчанию ( padding-box для background-origin и border-box для background-clip ).

Чудесно! Посмотрим теперь, как использовать это в своих интересах!

Прозрачный зазор между рамкой и фоном

Кто-то наверняка вспомнит, что с background-clip можно делать полупрозрачные рамки. Но можно еще и добавить зазор между рамкой и областью, покрытой фоном, без дополнительного элемента. Простейший способ этого добиться — задать padding в дополнение к border и указать для background-clip значение content-box . Если сделать это сокращенной записью с единственным значением content-box , мы заодно установим значение content-box и для background-origin , но в данном случае это нормально, никаких побочных эффектов от него нет.

Обрезка фона по content-box значит, что он не распространится за края контента. Дальше их фона не будет, так что будет видно всё, что находится под нашим элементов. Добавка border значит, что мы увидим этот border в между краем внутреннего отступа и краем рамки. Но, поскольку padding ненулевой, у нас остается прозрачная область между краями контента и внутреннего отступа.

подсветка рамки, внутреннего отступа и области контента в отладчике

Можно проверить это вживую в этом примере на CodePen:

Можно сделать результат еще интереснее, добавив фильтр drop-shadow() , который добавит всей конструкции желтоватое свечение:

Напоминание насчет префиксов: я видела множество ресурсов, добавляющих префиксы -moz- и -ms- для CSS-фильтров. Пожалуйста, не делайте этого! CSS-фильтры работают в Firefox без префикса с тех самых пор, как их там реализовали (Firefox 34, осенью 2014-го), а сейчас они появились в Edge за флагом — и тоже без префикса! Так что CSS-фильтрам никогда не были нужны префиксы -moz- и -ms- , добавлять их абсолютно бессмысленно, они будут лишь загромождать стили мертвым грузом.

Можно также получить клёвый визуальный эффект, применив градиент и для background-image , и для border-image . Сделаем градиент, начинающийся со сплошного оранжево-красного цвета вверху и переходящий в полную прозрачность внизу. Поскольку различаются лишь оттенки, а в остальном наши градиенты идентичны, создадим функцию Sass.

Можно проверить это в действии в этом примере на CodePen:

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

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

box-shadow может принимать 2 , 3 либо 4 значения длины. Первое — смещение по x (определяющее, насколько сдвинуть тень вправо), второе — смещение по y (насколько сдвинуть тень вниз), третье — радиус размытия (определяет, насколько будет размыт край тени), и четвертое — радиус распространения (определяет, как далеко тень будет простираться во все стороны).

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

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

Еще одна важная вещь, которую здесь надо отметить — поскольку она удобна для нашей задачи — то, что box-shadow никогда не отображается под border-box , даже когда эта область (полу)прозрачна.

Если оставить смещения и размытие нулевыми, но задать положительное значение радиусу распространения, то с виду получится вторая сплошная рамка одинаковой со всех сторон толщины, наружу от края настоящей рамки.

имитация рамки с помощью box-shadow

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

Возвращаясь к нашему примеру, можно имитировать рамку с помощью box-shadow с распространением, реальный border превратить в прозрачный зазор, задать для background-clip значение padding-box , и применить padding по назначению:

подсветка областей рамки, внутреннего отступа и контента в отладчике

Это можно посмотреть в действии в следующем примере на CodePen:

Можно было бы имитировать рамку и внутренней тенью (с параметром inset ). В этом случае она начинается от края внутреннего отступа (границы между областями padding и border ), и распространяется на указанный радиус внутрь.

эмуляция второй рамки с помощью внутренней box-shadow

Поскольку теней может быть много, так можно имитировать несколько рамок. Пусть, к примеру, их у нас две, причем одна из них — inset . Если реальный border элемента ненулевой толщины и при этом transparent , а у background-clip стоит значение padding-box , то получится имитация двойной рамки с прозрачной областью (на месте реальной рамки) между внешней и внутренней частями. Обратите внимание, что ради этого понадобилось увеличить padding для компенсации места, занятого внутренней box-shadow .

Можно испытать это в действии в этом примере на CodePen, где добавлен еще фильтр drop-shadow() для «светящегося» эффекта.

Мишень из одного элемента (без псевдоэлементов) с гладкими краями

Допустим, нам нужно получить вот такую мишень, и при этом нам позволено использовать только один элемент без псевдоэлементов.

мишень, которую нам надо построить силами CSS

Первая мысль — воспользоваться repeating-radial-gradient . Структура нашей мишени примерно такая:

иллюстрация структуры мишени

Итак, половина мишени составит 9 единиц, т.е. размеры мишени по вертикали и горизонтали будут по 18 единиц. Наш повторяющийся радиальный градиент всю первую единицу черный, потом до третьей единицы (включительно — прим. перев.) прозрачный, потом опять черный и опять прозрачный… звучит как повторение. Вот только первый раз у нас черная область от 0 до 1, т.е. одна единица, а следующая черная область уже от 3 до 5 — две единицы! Так что… нам тут надо начать отсчет не от 0 , а от -1 , верно? Что ж, это должно работать, согласно спецификации.

Этот пример на CodePen иллюстрирует принцип:

Первая загвоздка здесь в том, что у IE другое мнение, как это должно работать.

repeating-radial-gradient с отрицательной первой контрольной точкой: сравните ожидаемый результат (слева) и IE (справа)

К счастью, это исправили в Edge, но если нужна поддержка IE, проблема остается. Ее можно решить, используя обычный радиальный градиент, нам ведь всё равно не так уж много кругов и нужно. Кода больше, но всё не так уж плохо…

Можно увидеть его в действии в этом примере на CodePen:

Теперь окружности распределены во всех браузерах одинаково, но у нас осталась другая проблема: пусть края не слишком отличаются по гладкости от оригинальной картинки в IE/Edge, но в Firefox и Chrome они выглядят просто ужасно!

сравните оригинал (вверху слева), IE (вверху справа), Firefox (внизу слева) и Chrome (внизу справа)

Что ж, в IE (где и так было неплохо) и Firefox стало лучше, но в Chrome края по-прежнему безобразные.

сравните оригинал (вверху слева), IE (вверху справа), Firefox (внизу слева) и Chrome (внизу справа)

Может быть, радиальные градиенты вообще не лучшее решение, в конце концов. А почему бы нам не приспособить сюда решение на background-clip и box-shadow из предыдущего раздела? С помощью внешней box-shadow мы можем сделать внешнее кольцо, а тенью с inset — внутреннее. Пространство между ними займет прозрачная рамка. Мы также установим для background-clip значение content-box и дадим элементу достаточный внутренний отступ, так что у нас получится прозрачная область между центральным кружком и внутренним кольцом.

Посмотрите, как это работает, в следующем примере на CodePen, никаких «рваных» краев и прочих неприятностей:

Реалистичные элементы управления

Я впервые это придумала, когда пыталась оформлять ручку ползунка от <input type=»range»>, дорожку ползунка и, для «невебкитных» браузеров, заполненную часть дорожки. Для этих деталей в браузерах предусмотрены псевдоэлементы.

Для полосы у нас есть -webkit-slider-runnable-track , -moz-range-track и -ms-track . Для ручки — -webkit-slider-thumb , -moz-range-thumb и -ms-thumb . И для заполненной части есть -moz-range-progress , -ms-fill-lower (оба для части слева от ручки) и -ms-fill-upper (справа от ручки). У WebKit-браузеров не предусмотрено псевдоэлемента, которым можно было бы оформить части ползунка до и после ручки по-разному.

Разнобой между этими псевдоэлементами выглядит ужасно, но еще ужаснее то, что мы не можем перечислить все разнобраузерные версии для одной детали, чтобы оформить их все сразу. Что-то вроде такого не заработает:

Нам приходится постоянно писать их так:

Что выглядит как механическое «переливание» в коде одной и той же «воды», и часто это так и есть — хотя при таком обилии разночтений между браузерами, как с ползунками, такие дубликаты полезны для сглаживания этих различий. Моим решением было использовать миксин thumb() и при необходимости передавать ему аргументы для обработки несоответствий. Например, как-то так:

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

Давайте взглянем на несколько примеров!

Мягкий пластик

В качестве визуального примера представим себе что-то вроде ручки ползунка ниже:

ползунок с ручкой из мягкого пластика

На первый взгляд всё решение сводится к градиентному background и и градиенту для border-image , потом еще box-shadow для красоты — и такой элемент готов:

И это даже работает (на примере элемента button вместо ручки ползунка ради простоты):

За исключением того, что наша ручка круглая, а не квадратная. Нужно просто добавить ей border-radius: 50% , так? Так вот… это не сработает, потому что мы используем border-image , с которым border-radius самого элемента игнорируется, хотя, как это ни забавно, он по-прежнему действует на box-shadow , если она есть.

Что же делать? Точно, использовать background-clip ! Сначала мы зададим элементу ненулевой padding , уберем рамку и скруглим его с помощью border-radius: 50% . Затем наложим два градиентных фона, обрезав верхний по content-box (обратите внимание, что обрезка входит в сокращенную запись background) . Наконец, добавим две тени, одну темную, создающую собственно тень под ручкой, а вторую с inset , которая будет слегка затемнять низ и бока внешней части ручки.

Полюбоваться конечным результатом можно в этом примере на CodePen:

Матовая ручка

Что-то вроде ручки ползунка со следующей картинки:

ползунок с матовой ручкой

Этот случай похож на предыдущий, только теперь у нас светлая линия вверху и внутренняя тень для средней части. Если использовать внешнюю box-shadow для создания светлой линии, вся конструкция потеряет круглую форму, если только заодно не уменьшить высоту для компенсации тени. А тогда потребуется больше расчетов для определения положения внутренней части. Если же сделать ее тенью с inset , то мы уже не сможем использовать ее для затемнения внутренней части. Однако, мы можем эмулировать ее с помощью radial-gradient , которому заданы подходящий размер, положение и обрезка по content-box . То есть делаем всё аналогично предыдущему примеру, но теперь у нас поверх остальных фонов будет наложен дополнительный radial-gradient . Фактический background-size этого радиального градиента больше, чем наш content-box , так что мы можем сдвинуть его вниз так, что его верхний край останется за пределами области контента.

Можно посмотреть на это вживую в этом примере на Code Pen:

Объемный элемент

Например, ручка следующего ползунка:

ползунок с объемной ручкой

Это уже посложнее, для этого все три области ( content-box , padding-box и border-box ) должны быть разными, чтобы мы могли накладывать фоны друг на друга и добиваться нужного эффекта с помощью background-clip .

Итак, для основной части ползунка у нас есть градиентный background , обрезанный по content-box , наложенный поверх другого, обрезанного по padding-box , и оба они поверх третьего linear-gradient , обрезанного по border-box . Мы таже задействовали внутреннюю box-shadow , чтобы подчеркнуть край внутреннего отступа (между padding и border ):

Результат можно посмотреть на CodePen здесь:

See the Pen 3D button by Ana Tudor (@thebabydino) on CodePen.

Теперь остается маленькая круглая деталька. Это тот случай, когда я по-настоящему почувствовала потребность в псевдоэлементе. В итоге я и пошла по этому пути для Blink, но мне удалось сделать так, что запасной вариант для других браузеров — из наложенных друг на друга двух радиальных градиентов поверх линейных — стал выглядеть прилично:

Можно было бы еще приблизиться к нашей цели, лучше подобрав оттенки, добавив еще радиальных градиентов или даже воспользовавшись background-blend-mode , но на подобное мне не хватает художественного чутья.

С псевдэлементом добиться нужного результата намного проще — сначала нужно задать ему правильные размер и положение, закруглить его с помощью border-radius: 50% . Затем задать ему padding , никакой рамки, и использовать два градиента в background , верхний из которых радиальный и обрезан по content-box :

Посмотреть, что получилось, можно в следующем примере:

Для ручки реального ползунка я использовала тот же самый background с радиальными градиентами сверху для всех браузеров, а для Blink добавила псевдоэлемент прямо поверх этих радиальных градиентов. Это потому, что стили ручки ползунка в Safari применяются к ::-webkit-slider-thumb , но Safari не поддерживает псевдоэлементов для ручки (или дорожки). Так что если бы я убрала запасной вариант из стилей для ::-webkit-slider-thumb , то Safari не отобразил бы круглую детальку вообще.

Иллюзия глубины

Дорожка следующего ползунка иллюстрирует основную мысль:

ползунок с дорожкой, уходящей в глубину

Как и раньше, для этого мы задаем элементу ненулевой прозрачный border , padding , и накладываем друг на друга фоны с разными значениями background-clip (помните, что слои со значением content-box должны лежать поверх слоев со значением padding-box , а те — поверх слоев со значением border-box ). В данном случае у нас есть более светлый linear-gradient для заливки области border , более темный — плюс пара уменьшенных неповторяющихся радиальных, чтобы добавить еще темноты по краям — для области padding , и совсем тёмный для области контента. Затем мы задаем этой конструкции border-radius , равный как минимум половине от суммы height области контента, удвоенного padding и удвоенного border . И еще добавим внутреннюю box-shadow , чтоб слегка подчеркнуть край внутреннего отступа.

Но тут есть проблема, и ее видно в следующем примере на CodePen:

Из-за того, как работает border-radius — радиус области контента получается из указанного вычитанием border-width и padding , что дает в итоге отрицательное число — углы области контента не закругляются. Что ж, мы можем это исправить! Можно эмулировать нужную нам форму, сочетая линейные, и радиальные градиенты для области контента.

Делается это так: сначала убедимся, что width нашей области контента кратна ее height (в нашем случае, 15.75em = 9*1.75em ). Нижним слоем положим linear-gradient по центру так, чтобы по вертикали он покрывал всю height области контента, но оставляет зазоры по половине ее height от обоих боковых краев. Поверх него накладываем radial-gradient , background-size , равным height области контента и по горизонтали, и по вертикали.

Металлические элементы управления

Например, что-то вроде кнопки, изображенной ниже:

металлический элемент управления

Это уже чуть сложнее, так что давайте разберем его по частям. Прежде всего, мы делаем кнопку круглой, задав ей равные width и height и установив border-radius: 50% . Затем убеждаемся, что ей задан box-sizing: border-box , чтобы border-width и padding отсчитывались внутрь (вычитались из размеров, что мы указали). Теперь зададим ей прозрачную рамку и внутренний отступ. И вот что у нас получилось на данный момент:

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

Прежде чем двигаться куда-то дальше, давайте немного разберем, из чего состоит элемент:

составные части металлического элемента управления

В порядке от самой внешней детали к центру, у нас есть:

  • широкое внешнее кольцо со светодиодами
  • тонкое внутреннее кольцо
  • решетчатая область (она же включает в себя голубое свечение вокруг следующей внутренней части)
  • большая центральная деталь

Широкое внешнее кольцо — это область рамки, центральная часть — область контента, а всё, что между ними (тонкое внутреннее кольцо и решетчатая часть) — область внутреннего отступа. Тонкое внутреннее кольцо мы сделаем внутренней тенью:

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

До цели еще далековато, но уже хоть что-то.

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

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

И наконец это становится на что-то похоже!

Переходим к решетчатой области. Голубое свечение — просто радиальный градиент, сходящий к краям в прозрачность, а вот в основе отверстий лежит паттерн «Карбоновое волокно» из галереи, которую Лиа Веру сделала целых пять лет назад — но всё еще чертовски полезной для людей с ограниченными художественными способностями вроде меня.

Кажется, у нас начинает вырисовываться что-то приличное:

Основа широкого внешнего кольца (без светодиодов) делается одним коническим градиентом:

И вот у нас получился металлический элемент!

Но на нем пока нет светодиодов, исправим же это поскорее!

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

Включенными у нас будут светодиоды вплоть до $k -того. Так что до этой отметки используем голубой вариант верхнего градиента, а после нее — серый.

У нас 24 светодиода, размещенных по окружности, проходящей по середине области рамки. Так что ее радиус — это радиус элемента минус половина толщины рамки.

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

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

Тени на перпендикулярной плоскости

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

элементы с тенью на горизонтальной плоскости под ними

Нам нужно воспроизвести этот эффект с помощью всего одного элемента, без псевдоэлементов.

Наложение фонов с разными значениями background-clip и background-origin выручает и в этом случае. Саму кнопку мы строим двумя фонами, верхний из которых обрезан по content-box , а тот, что под ним — по padding-box , и используем фоновый radial-gradient() со значением border-box для background-clip и background-origin , чтобы сделать тень.

Базовое оформление очень похоже на оформление металлического элемента из прошлого раздела:

Мы задаем довольно широкую прозрачную рамку вокруг кнопки, чтобы нам хватило места для создания этой тени в нижней части рамки. Делаем это для всех сторон рамки, а не только для нижней, потому что нам нужно, чтобы все углы у padding-box закруглялись одинаково и симметрично (если вам нужно освежить в памяти, как это работает, посмотрите потрясающий доклад о border-radius Лии Веру).

Первый background наверху — это conic-gradient() для создания металлического отлива. Он обрезан по content-box . Прямо под ним у нас linear-gradient() , обрезанный по padding-box . Мы используем три внутренние тени, чтобы сделать этот второй фон менее плоским — добавляем еще один оттенок вокруг него с нулевым размытием, осветляем его вверху полупрозрачной белой тенью и затемняем внизу полпрозрачной черной тенью.

Это дает нам металлическую кнопку (пока без тени):

Для тени добавим третий слой фона, для которого мы зададим обоим свойствам background-clip и background-origin значение border-box . Этим фоном будет неповторяющийся radial-gradient() , положение которого мы привяжем к bottom (а по горизонтали к середине) и который мы сожмем по вертикали так, что он поместится внутри нижней стороны рамки и даже еще немного места останется — пусть он будет размером, скажем, где-то .75 от значения border-width .

И всё! Можете поиграть с этими кнопками в следующем примере на CodePen:

Для свойства background-clip наверняка найдётся множество применений! Особенно при наложении разных эффектов по краям элементов.

📎📎📎📎📎📎📎📎📎📎