Властивість background-clip та як його застосовувати

17

Від автора: властивість background-clip – одне з тих властивостей, які я вже знаю на протязі декількох років, але так толком і не використав. Може пару раз воно мені придалося, як частину рішення, в питанні на сайті Stack Overflow. Я його не використовував аж до цього року, поки не почав створювати свою величезну колекцію слайдерів.

Деякі з вибраних мною дизайнів були настільки складні, що на весь слайдер у мене був лише один доступний елемент input. А це значить, що я навіть не міг використовувати псевдокласи на ньому. Навіть якщо в окремих браузерах псевдокласи на елементі инпут працюють, то вони працюють виключно як баг. А я не хочу покладатися на це. Все це означало, що мені доведеться використовувати дуже багато властивостей, пов’язаних з фоном, кордонами і тінями. З цієї роботи я багато чого для себе дізнався і в цій статті поділюся деякими уроками.

Спочатку давайте розберемося, що ж таке це властивість background-clip та що вона робить. На рисунку зображено квадрат.

Властивість background-clip та як його застосовувати

Значення padding дорівнює 0, а padding-box збігається з властивістю content-box. Content limit і padding limit також рівні.

Властивість background-clip та як його застосовувати

Якщо властивість border-width: 0, то border-box збігається з padding-box, а padding limit збігається з border limit.

Властивість background-clip та як його застосовувати

Якщо і padding та border-width дорівнюють 0, то всі три content-box, padding-box і border-box однакові, також будуть рівні content limit, padding limit і border limit.

Властивість background-clip та як його застосовувати

За замовчуванням фон повністю перекриває властивість 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.

Властивість background-clip та як його застосовувати

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

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

Властивість background-clip та як його застосовувати

Використовуючи одне значення для background-size не забезпечить кросбраузерності (тест). Зліва: Firefox (по специфікації приймає друге значення за 100%). Праворуч: Chrome/ Mozilla, Safari, IE/ Edge (неправильно задають друге значення першого).

За допомогою властивості background-clip можна зробити так, щоб фон закривав тільки padding-box або content-box. Властивість відсікає і не показує ту частину, яка виходить за область кадрування, де область кадрування це область в пунктирній рамці на малюнку нижче.

Властивість background-clip та як його застосовувати

За замовчуванням background-clip: border-box область кадрування це border-box. Тобто ми бачимо фон під рамкою.

Властивість background-clip та як його застосовувати

Якщо задати background-clip: padding-box, областю кадрування стане padding-box. Це означає, що фон буде показуватися тільки в padding-box (не буде видно під рамкою).

Властивість background-clip та як його застосовувати

І якщо поставити background-clip: content-box, область кадрування буде content-box. Фон буде видно тільки всередині області content-box.

Властивість background-clip та як його застосовувати

У демо нижче проілюстровані три ситуації:

Також є ще одна властивість background-origin, яке задає до якої з трьох областей буде ставитися background-position (і background-size %).

Розглянемо точно таку ж конструкцію з переривчастою рамкою border, але в цей раз з видимим padding’ом. На фон ми поставимо зображення і градієнт. І зображенню і градієнту задано background-size: 50% без повторень. Також зображення не задано background-position: 100% 100% (а градієнт за замовчуванням 0 0):

background: linear-gradient(right to bottom,
#e18728, #be4c39, #9351a6, #4472b9),
url(tiger_lily.jpg) 100% 100%;
background-repeat: no-repeat;
background-size: 50% 50%;

У демо нижче показані випадки для кожного із трьох можливих значень властивості background-origin — border-box, padding-box і content-box:

Значення 100% 100% властивості background-position для зображення відповідають значенням 100% 100% для блоку властивість background-origin. Але в той же час значення background-size: 50% 50% це половина ширини та висоти блоку властивість background-origin.

У скороченому властивості background можна задати значення властивості background-origin і background-clip саме в такому порядку у кінці шару. Так як дані властивості приймають значення від блоку, то якщо у блоку зазначено лише одне значення, то обидві властивості також будуть встановлені на це значення. Якщо блоку задані обидва значення, то перше це background-origin, а друге background-clip. Якщо значення у блоку не задані, то будуть взяті значення за замовчуванням (padding-box для background-origin і border-box для background-clip). Чудово, тепер розберемося, як це можна використовувати!

Прозорий проміжок між рамкою і фоном

Хтось, може бути, ще пам’ятає, що з допомогою властивості background-clip можна отримати напівпрозору рамку. Але ми також можемо додати додаткове місце між рамкою і областю з фоном без введення додаткових елементів. Найпростіший спосіб це в додатку до border додати ще й padding і встановити background-clip: content-box. Якщо ставити все це через скорочене властивість, то необхідно також задати background-origin: content-box. В нашому випадку так робити можна, і не буде ніяких небажаних ефектів.

border: solid .5em #be4c39;
padding: .5em;
background: #e18728 content-box;

Кадрування фону в області content-box означає, що фон не буде вилазити за межі даної області. За межами даної області фон не буде видно, і нам відкриється вид на те, що знаходиться під нашим блоком. Якщо додати border, то здасться рамка між padding limit і border limit. Але якщо padding не дорівнює 0, то нам все ще буде видно прозора область між content limit і padding limit.

Властивість background-clip та як його застосовувати

Підсвічування border’а, padding’а й області контенту через панель розробника. Можна потестить наживо:

Можна зробити трохи цікавіше, додавши фільтр drop-shadow(), який додасть жовтуватого світіння:

Префікси: Я зустрічав безліч ресурсів, які додають вендорные префікси –moz — і –ms — до даного CSS фільтру. Будь ласка, не робіть цього! У Firefox у CSS фільтрів прибрали префікси з першим релізом (Firefox 34, восени 2014 р.). Сьогодні в Edge фільтри також вживаються без префіксів. Тобто CSS фільтрів ніколи не були потрібні префікси –moz — і –ms-, додавати їх марно. Вони тільки засмічують стилі.

Якщо задати градієнт для background-image і border-image, то вийде крутий ефект. Ми зробимо градієнт, який починається з яскраво помаранчевого/червоного зверху і поступово згасає до повної прозорості. Так як у нас різні тіні, а градієнти однакові, можна створити Sass функцію.

@function fade($c) {
return linear-gradient($c, rgba($c, 0) 80%);
}
div {
border: solid 0.125 em;
border-image: fade(#be4c39) 1;
padding: 0.125 em;
background: fade(#e18728) content-box;
}

Якщо у нас в блоці короткий текст, то даний підхід більш менш працює. Але він не підходить для створення прозорої рамки навколо блоку, якщо текст великий. Виглядає це жахливо.

Проблема в тому, що текст починається прямо від краю блоку з жовтогарячим і ми не можемо задати padding, так як він вже у нас є, і він прозорий. Можна додати ще один елемент, або ж використовувати box-shadow!

Властивість box-shadow приймає 2, 3 або 4 значення. Перше це зсув по осі Х (задає наскільки зсувається тінь вправо), друге – зсув по осі y (зсув вниз), третє – радіус розмиття (як сильно буде розмитий край тіні) і четверте значення – радіус розкиду (розкид тіні у всіх напрямках). У демо нижче можна погратися зі значеннями – клікніть на будь-який з них, і з’явиться слайдер.

Радіус розмиття може приймати значення рівні або вище нуля, а зрушення та радіус поширення можуть приймати від’ємні значення. Від’ємні значення зрушують тінь в протилежну сторону по осі (вліво або вверх). Негативне значення для радіуса розкиду означає, що тінь не розширюється, а стискається.

Ще одна важлива річ – дуже зручна для нашого випадку – тінь box-shadow не видно під border-box. Навіть якщо ця область напівпрозора. Якщо залишити значення зрушень і радіусу розмиття на нулі, а радіус розкиду задати, то ми отримаємо другу суцільну рамку однакової ширини по всіх напрямах, яка буде починатися з кордону першої рамки.

Властивість background-clip та як його застосовувати

Важливо пам’ятати, що ця рамка може бути різної ширини в усіх напрямках. Задати іншу ширину можна, змінивши значення зсуву і радіусу розкиду. Є тільки одне обмеження – сума ширини горизонтальних меж завжди дорівнює сумі ширини вертикальних кордонів. Обійти це обмеження можна, використовуючи кілька тіней. Однак даний підхід ускладнює те, що по суті має бути легким.

Повертаючись до нашого демо, що ми використовуємо box-shadow для імітації рамки, а справжню рамку використовуємо для створення прозорої області. Для цього необхідно задати background-clip: padding-box, а padding зробить все інше:

border: solid 1em transparent;
padding: 1em;
box-shadow: 0 0 0 1em #be4c39;
background: #e18728 padding-box;

Властивість background-clip та як його застосовувати

Також імітувати рамку можна з допомогою внутрішньої тіні inset. В такому випадку тінь починається від padding limit (область між padding та border) і спрямована всередину:

Властивість background-clip та як його застосовувати

Так як у нас кілька тіней, ми можемо емулювати кілька рамок. Давайте розберемо приклад з двома тінями, одна inset, інша звичайна. Якщо справжній border не дорівнює нулю і прозорий, а background-clip: padding-box, то ми отримуємо імітацію подвійний рамки з прозорою областю (справжня рамка) між зовнішньої і внутрішньої рамками. Зверніть увагу, що необхідно збільшити padding для компенсації відстані, займаного внутрішнім box-shadow.

border: solid 1em transparent;
padding: 2em; // збільшили з 1em до 2em для компенсації внутрішнього «border»
box-shadow:
0 0 0 0.25 em #be4c39 /* зовнішній «border» */,
inset 0 0 0 1em #be4c39 /* внутрішній «border» */;
background: #e18728 padding-box;

Можна протестувати наживо. Ми додали drop-shadow() фільтр для світіння:

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

Скажімо, ми хочемо створити мішень, як на картинці нижче. Одне обмеження – ми можемо використовувати тільки один елемент без псевдокласів.

Властивість background-clip та як його застосовувати

Першим приходить в голову використовувати repeating-radial-gradient. Структура мішені приблизно така:

Властивість background-clip та як його застосовувати

Половина мішені займає 9 одиниць, тоді горизонтальний і вертикальний розміри будуть 18 одиниць. Перший кругової градієнт чорний і займає першу одиницю, потім йде прозора область аж до третьої одиниці, потім знову чорна окружність і прозора область… начебто значення повторюються. Все окрім першого від 0 до 1. Спочатку у нас чорна область, потім знову чорна, але вже займає відстань з 3 до 5 одиниці – дві одиниці! Т. е… ми не можемо почати з 0, необхідно почати з -1, так адже? Ну по специфікації це повинно працювати.

$unit: 1em;
background: repeating-radial-gradient(
#000 (-$unit), #000 $unit,
transparent $unit, transparent 3*$unit
);

Перша проблема в тому, що IE думає, що це має відображатися по-іншому.

Властивість background-clip та як його застосовувати

На наше щастя, в Edge це пофиксили. Але якщо вам потрібно підтримувати IE, то проблема залишається. Дану проблему можна вирішити простим радіальним градієнтом, тим більше нам не потрібно так багато кіл. Коду буде більше, але це не так погано…

background: radial-gradient(
#000 $unit, transparent 0,
transparent 3*$unit, #000 0,
#000 5*$unit, transparent 0,
transparent 7*$unit, #000 0,
#9 000*$unit, transparent 0
);

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

Властивість background-clip та як його застосовувати

Оригінал (зліва зверху), IE/Edge (праворуч зверху), Firefox (знизу зліва) і Chrome (знизу справа).

Можна скористатися non-sharp transition trick:

background: radial-gradient(
#000 calc(#{$unit} — 1px),
transparent $unit,
transparent calc(#{3*$unit} — 1px),
#3 000*$unit,
#000 calc(#{5*$unit} — 1px),
transparent 5*$unit,
transparent calc(#{7*$unit} — 1px),
#7 000*$unit,
#000 calc(#{9*$unit} — 1px),
transparent 9*$unit
);

Даний спосіб покращує результат в IE (де вже все добре) і Firefox, але в Chrome краю все ще жахливі.

Властивість background-clip та як його застосовувати

Оригінал (зліва зверху), IE/Edge (праворуч зверху), Firefox (знизу зліва) і Chrome (знизу справа)

Може бути, радіальні градієнти не найкраще рішення. А що якщо б нам довелося адаптувати спосіб з background-clip та box-shadow з попереднього прикладу під цю проблему? Можна використовувати зовнішню тінь box-shadow для зовнішнього кола, а inset тінь для внутрішнього кола. Між колами прозора рамка. Також необхідно задати background-clip: content-box і задати відповідний padding, щоб між центральним колом і першим чорним кругом була прозора область.

border: solid 2*$unit transparent;
padding: 4*$unit;
width: 2*$unit; height: 2*$unit;
border-radius: 50%;
box-shadow:
0 0 0 2*$unit #000,
inset 0 0 0 2*$unit #000;
background: #000 content-box;

Повзунки як в реальних прикладах

Вперше ця ідея мені прийшла, коли мені довелося стилізувати кілька повзунків, доріжки, прогрессбары не webkit браузерах. Для таких компонентів в браузерах є псевдокласи.

Для доріжок є webkit-slider-runnable-track, -moz-range-track і -ms-track. Для повзунків є -webkit-slider-thumb, -moz-range-thumb і -ms-thumb. А для прогрессбаров і тега fill є -moz-range-progress, -ms-fill-lower (для лівої частини) і -ms-fill-upper (і для правої частини). Webkit браузерах немає спеціальних псевдокласів для стилізації частин повзунка окремо.

Це виглядає некрасиво, але ще більш неприємно те, що ми не можемо перерахувати всі версії браузерів через кому для одного компоненту. Код нижче не спрацює:

input[type=’range’]::-webkit-slider-thumb,
input[type=’range’]::-moz-range-thumb,
input[type=’range’]::-ms-thumb { /* стилі */ }

Необхідно писати так:

input[type=’range’]::-webkit-slider-thumb { /* стилі */ }
input[type=’range’]::-moz-range-thumb { /* стилі */ }
input[type=’range’]::-ms-thumb { /* стилі */ }

Дані підхід схожий на стиль написання коду WET. У багатьох випадках це він і є – хоча, враховуючи численні невідповідності браузерів з повзунками, краще вирівнювати код за окремими рядками. Я вирішив використовувати міксин thumb() та передавати йому аргументи. Щось на зразок цього:

@mixin thumb($flag: false) {
/* стилі */
@if $flag { /* ще стилі */ }
}
input[type=’range’] {
&::-webkit-slider-thumb { @include thumb(true); }
&::-moz-range-thumb { @include thumb(); }
&::-ms-thumb { @include thumb(); }
}

Давайте повернемося до стилізації. Ми можемо додати псевдокласи до цих компонентів тільки у Chrome/Mozilla, а значить, що при відтворенні їх зовнішнього вигляду ми не повинні покладатися на псевдокласи. Т. е. фон, рамка, тіні і фільтри будуть виставлятися на самому рядку. Розглянемо кілька прикладів!

М’який пластиковий повзунок

Візуально приклад буде виглядати приблизно так:

Властивість background-clip та як його застосовувати

Перше приходить в голову градієнт властивості background і градієнт для border-image, потім поставити тінь і все.

border: solid 0.375 em;
border-image: linear-gradient(#fdfdfd, #c4c4c4) 1;
box-shadow: 0 0.375 em 0.5 em -0.125 em #808080;
background: linear-gradient(#c5c5c5, #efefef);

І цей метод працює (замість повзунка для простоти можна використовувати button):

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

І що ж робити? Використовувати background-clip! Спочатку задамо елементу нульовою padding, приберемо рамку і зробимо повзунок круглим border-radius: 50%. Потім накладаємо два градієнта, перший для content-box (задається через скорочене властивість). І в кінці додаємо дві тіні: одна темна під повзунком, а друга розташована всередині і затемнює трохи нижню і зовнішні кордони.

border: none; /* робить border-box ≡ padding-box */
padding: .375em;
border-radius: 50%;
box-shadow: 0 .375em .5em -.125em #808080,
inset 0 -.25em .5em -.125em #bbb;
background:
linear-gradient(#c5c5c5, #efefef) content-box,
linear-gradient(#fdfdfd, #c4c4c4);

Матовий повзунок

Щось схоже на картинку нижче:

Властивість background-clip та як його застосовувати

Виглядає дуже схоже на попередній приклад, тільки тепер згори у нас світла лінія, а всередині тінь. Повзунок не буде круглим, якщо ми встановимо зовнішню світлу лінію. Для цього необхідно компенсувати висоту елемента, зменшити її. А значить, нам потрібно провести більше обчислень, щоб визначити позицію внутрішньої частини. Якщо використовувати inset для світлої лінії, то потім ми не зможемо його використовувати для додавання внутрішньої тіні. Але це можна імітувати за допомогою radial-gradient, який добре підігнаний за розміром, позиціонуванню до content-box. Тобто стратегія у нас точно така ж, як і в попередньому прикладі, тільки тут є додатковий шар radial-gradient зверху інших фонів. Справжній background-size радіального градієнта більше ніж content-box, тобто ми можемо зрушити його вниз, не доводячи його верхню межу до межі контенту.

border: none; /* робимо border-box ≡ padding-box */
padding: .625em;
width: 1.75 em; height: 1.75 em;
border-radius: 50%;
box-shadow:
0 1px .125em #444 /* dark lower shadow */,
inset 0 1px .125em #fff /* top light hint */;
background:
/* внутрішня тінь */
radial-gradient(transparent 35%, #444)
50% calc(50% + .125em) content-box,
/* внутрішній фон */
linear-gradient(#bbb, #bbb) content-box,
/* зовнішній фон */
linear-gradient(#d0d3d5, #d2d5d7);
background-size:
175% 175% /* робимо radial-gradient bg більше*/,
100% 100%, 100% 100%;

3D повзунок

Наприклад, щось схоже на це:

Властивість background-clip та як його застосовувати

Такий повзунок вже складніше попередніх. Необхідно буде задати різні content-box, padding-box і border-box таким чином, щоб ми змогли накласти фон і використовувати background-clip.

Основної частини слайдера задається фон градієнт, який кадрируется по області content-box, а під ним ще один фон кадрируется по області padding-box. Обидва градієнта зверху третього linear-gradient, який обрізається по border-box. Також використовується внутрішня тінь box-shadow для підсвічування області padding limit (між padding та border):

border: solid .25em transparent;
padding: .25em;
border-radius: 1.375 em;
box-shadow:
inset 0 1px 1px rgba(#f7f7f7, .875) /* верх */,
inset 0 -1px 1px rgba(#bbb, .75) /* низ */;
background:
linear-gradient(#9ea1a6, #fdfdfe) content-box,
linear-gradient(#fff, #9c9fa4) padding-box,
linear-gradient(#eee, #a4a7ab) border-box;

Тепер необхідно додати невелике коло. Це той випадок, коли б нам дійсно згодилися псевдокласи. Я зміг реалізувати даний спосіб для Blink браузерів, а для інших браузерів мені вдалося зробити пристойний фолбэк з допомогою накладання двох радіальних градієнтів поверх лінійного.

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

Домогтися такого ефекту можна за допомогою псевдокласів – спершу необхідно встановити і налаштувати розмір властивості, зробити елемент круглим border-radius: 50%. Потім задаємо padding, прибираємо рамку і додаємо два градієнта для властивості background. Верхній радіальний градієнт і обрізається по області content-box:

padding: .125em;
background:
radial-gradient(circle at 50% 10%,
#f7f8fa, #9a9b9f) content-box,
linear-gradient(#ddd, #bbb);

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

Ілюзія глибини

Ідея показано на картинці нижче:

Властивість background-clip та як його застосовувати

Так само, як і раніше ми задаємо елементу ненульовий прозорий border, padding і накладаємо фони з різними background-clip значеннями (пам’ятайте, що content-box повинен бути зверху border-box). У цьому прикладі нас linear-gradient закриває border. Темний градієнт і пара радіальних меншого розміру без повторень для затемнення країв і області padding’а, і останній повністю чорний для content області. Потім задається border-radius, рівний як мінімум половині висоти тематичній області плюс два паддінга і дві рамки. Також додається внутрішня тінь box-shadow для підкреслення області padding limit.

border: solid .375em transparent;
padding: 1em 3em;
width: 15.75 em; height: 1.75 em;
border-radius: 2.25 em;
background:
linear-gradient(#090909, #090909) content-box,
radial-gradient(at 5% 40%, #0b0b0b, transparent 70%)
no-repeat 0 35% padding-box /* left */,
radial-gradient(at 95% 40%, #111, transparent 70%)
no-repeat 100% 35% padding-box /* right */,
linear-gradient(90deg, #3a3a3a, #161616) padding-box,
linear-gradient(90deg, #2b2d2c, #2a2c2b) border-box;
background-size: 100%, 9em 4.5 em, 4.5 em 4.5 em, 100%, 100%;

Але є одна проблема, яку можна побачити нижче:

З-за принципу роботи border-radius – для області вмісту ми відняли border-width і padding, що призвело до того, що значення радіуса стало негативним. Тобто нам нічого скругляющі в області вмісту. Але це можна виправити! Можна імітувати потрібну нам форму з допомогою лінійного і радіального градієнту в області вмісту.

Спершу необхідно переконатися, що ширина області вмісту кратна висоті (в нашому прикладі 15.75 em = 9*1.75 em). Перший шар це our case, 15.75 em = 9*1.75 em). We first layer a non-repeating linear-gradient всередині елемента. Градієнт займає всю висоту, а по краях залишає область, рівну половині висоти тематичній області. Зверху цього градієнта ми додаємо radial-gradient властивість background-size, значення дорівнюють висоті тематичній області.

Металевий повзунок

Щось на зразок кнопки нижче:

Властивість background-clip та як його застосовувати

Даний приклад трохи складніше, так що розіб’ємо його на частини. Спершу ми робимо круглу кнопку з однаковими width і height і border-radius: 50%. Потім задаємо box-sizing: border-box так, щоб border-width і padding були спрямовані всередину. Наступним кроком буде задати прозору рамку і паддінґ. На даний момент ми маємо наступне:

$d-btn: 27em; /* діаметр повзунка */
$bw: 1.5 em; /* border-width */
button {
box-sizing: border-box;
border: solid $bw transparent;
padding: 1.5 em;
width: $d-btn; height: $d-btn;
border-radius: 50%;
}

Поки що це ще ні на що не схоже, так як ми використовували всього дві властивості: box-shadow і background.

Перш ніж продовжити давайте розберемо повзунок на складові:

Властивість background-clip та як його застосовувати

Починаючи з зовнішньої частини і йдучи всередину:

Широке зовнішнє кільце з лампочками

Тонке внутрішнє кільце

Перфорована область (вона включає в себе також блакитне світіння від внутрішнього диска)

Велика центральна частина

Широке зовнішнє кільце це рамка, центральна частина – область контенту, а все інше між ними (тонке кільце і область з точками) це паддінґ. Тонке внутрішнє кільце можна створити з допомогою внутрішньої тіні:

box-shadow:
/* темна тінь як роздільник для зовнішнього кільця */
inset 0 0 1px #666,
/* темна верхня область*/
inset 0 1px .125em #8b8b8b,
inset 0 2px .25em #a4a2a3,
/* темна нижня область */
inset 0 -1px .125em #8b8b8b,
inset 0 -2px .25em #a4a2a3,
/* кругла смуга для внутрішнього кільця */
inset 0 0 0 .375em #cdcdcd;

Також необхідно додати дві зовнішніх тіні: одна для легкої підсвічування зверху зовнішнього кільця, а друга для тіні під повзунком.

box-shadow:
0 -1px 1px #eee,
0 2px 2px #1d1d1d,
inset 0 0 1px #666,
inset 0 1px .125em #8b8b8b,
inset 0 2px .25em #a4a2a3,
inset 0 -1px .125em #8b8b8b,
inset 0 -2px .25em #a4a2a3,
inset 0 0 0 .375em #cdcdcd;

Все ще не схоже, але вже щось:

Тепер необхідно накласти три типи фонів зверху вниз: тільки для content-box (центральна область), тільки для padding-box (область з точками і блакитним світінням) і тільки для зони border-box (широке зовнішнє кільце і лампочки).

Почнемо з центральної області. Тут у нас кілька переривчастих круглих ліній, створених з допомогою трьох радіальних градієнтів, розташованих один над іншим за принципом cicada і з допомогою конічний відбиттів від конічних градієнтів. Конічні градієнти поки що не підтримуються ні в одному браузері, так що нам потрібно polyfill.

background:
/ * ======= content-box ======= */
/* circular lines — 13, 19, 23 being prime numbers */
repeating-radial-gradient(
rgba(#e4e4e4, 0) 0,
rgba(#e4e4e4, 0) 23px,
rgba(#e4e4e4, .05) 25px,
rgba(#e4e4e4, 0) 27px) content-box,
repeating-radial-gradient(
rgba(#a6a6a6, 0) 0,
rgba(#a6a6a6, 0) 13px,
rgba(#a6a6a6, .05) 15px,
rgba(#a6a6a6, 0) 17px) content-box,
repeating-radial-gradient(
rgba(#8b8b8b, 0) 0,
rgba(#8b8b8b, 0) 19px,
rgba(#8b8b8b, .05) 21px,
rgba(#8b8b8b, 0) 23px) content-box,
/* конічних reflections */
conic-gradient(/* random variations of some shades of grey */
#cdcdcd, #9d9d9d, #808080,
#bcbcbc, #c4c4c4, #e6e6e6,
#dddddd, #a1a1a1, #7f7f7f,
#8b8b8b, #bfbfbf, #e3e3e3,
#d2d2d2, #a6a6a6, #858585,
#8d8d8d, #c0c0c0, #e5e5e5,
#d6d6d6, #9e9e9e, #828282,
#8f8f8f, #bdbdbd, #e3e3e3, #cdcdcd)
content-box;

Ось тепер трохи схоже на результат!

Переходимо до області з точками. Блакитне світіння це простий радіальний градієнт з прозорою зовнішньою частиною. Точки засновані на шаблоні Carbon fibre з галереї Lea Verou, яку вона зібрала близько 5 років тому. Досі корисна річ, особливо для артистичних людей, як я.

$d-hole: 1.25 em; /* perforation diameter*/
$r-hole: .5*$d-hole; /* perforation radius */
background:
/ * ======= padding-box ======= */
/* cyan glow */
radial-gradient(
#00d7ff 53%, transparent 65%) padding-box,
/* holes */
radial-gradient(
#272727 20%, transparent 25%)
0 0 / #{$d-hole} #{$d-hole}
padding-box,
radial-gradient(
#272727 20%, transparent 25%)
$r-hole $r-hole / #{$d-hole} #{$d-hole}
padding-box,
radial-gradient(#444 20%, transparent 28%)
0 .125em / #{$d-hole} #{$d-hole}
padding-box,
radial-gradient(#444 20%, #3d3d3d 28%)
#{$r-hole} #{$r-hole + .125em} / #{$d-hole} #{$d-hole}
padding-box

Ми вже ближче до оригіналу:

Широке зовнішнє кільце без лампочок створюється за допомогою конічного градієнта:

conic-gradient(
#b5b5b5, #8d8d8d, #838383,
#ababab, #d7d7d7, #e3e3e3,
#aeaeae, #8f8f8f, #878787,
#acacac, #d7d7d7, #dddddd,
#b8b8b8, #8e8e8e, #848484,
#a6a6a6, #d8d8d8, #e3e3e3,
#8e8e8e, #868686, #a8a8a8,
#d5d5d5, #dedede, #b5b5b5) border-box;

І у нас майже готовий металевий повзунок!

Поки що на ньому ще немає лампочок. Давайте додамо їх! Кожна лампа складається з двох неповторюваних радіальних градієнтів, розташованих один над іншим. Верхній шар-це і є сама лампочка, а нижній дещо зміщений по вертикалі створює легке світіння в нижній частині лампочки. Ефект той же, що і для створення дірок в області з точками. Нижній градієнт однаковий, а верхній відрізняється в залежності від того включена лампочка.

Задамо $k-е кількість лампочок. Для верхнього градієнта ми використовуємо блакитний колір, а після – сірий. У нас 24 лампочки, розміщених на колі посередині області рамки. Тобто радіус лампочки дорівнює радіус повзунка мінус половина ширини рамки.

Всі градієнти ми будемо генерувати через Sass. Спершу створимо порожній список градієнтів, потім у циклі до списку буде додаватися по два градієнта. Розташування градієнтів обчислюється так, що на наступній ітерації ми знаходимося на попередньому колі. Перший градієнт залежить від індексу в циклі, а другий завжди такою ж (змінюється тільки позиція на окружності).

$d-btn: 27em;
$bw: 1.5 em;
$r-pos: .5*($d-btn — $bw);
$n-leds: 24;
$ba-led: 360deg/$n-leds;
$d-led: 1em;
$r-led: .5*$d-led;
$k: 7;
$leds: ();
@for $i from 0 to $n-leds {
$a: $i*$ba-led — 90deg;
$x: .5*$d-btn + $r-pos*cos($a) — $r-led;
$y: .5*$d-btn + $r-pos*sin($a) — $r-led;
$leds: $leds,
if($i < $k,
(radial-gradient(circle, #01d6ff,
#178b98 .5*$r-led,
rgba(#01d6ff, .35) .7*$r-led,
rgba(#01d6ff, 0) 1.3*$r-led) no-repeat
#{$x $r-led} #{$y $r-led} /
#{2*$d-led} #{2*$d-led} border-box),
(radial-gradient(circle, #898989,
#4d4d4d .5*$r-led, #999 .65*$r-led,
rgba(#999, 0) .7*$r-led) no-repeat
$x $y / #{$d-led} #{$d-led} border-box)
),
radial-gradient(circle,
rgba(#e8e8e8, .5) .5*$r-led,
rgba(#e8e8e8, 0) .7*$r-led) no-repeat
$(x$y + .125em) / #{$d-led} #{$d-led}
border-box;
}

Тіні в перпендикулярних площинах

Розглянемо приклад повзунка, коли він розміщений вертикально, а його тінь лежить під ним в горизонтальній площині. Щось схоже на:

Властивість background-clip та як його застосовувати

Нам необхідно відтворити цей ефект, використовуючи тільки один елемент без псевдокласів. В цьому випадку добре працює розміщення різних фонів з різними значеннями background-clip та background-origin. Створюємо кнопку з двома фонами: один зверху і обрізається по області content-box, другий під ним і обрізається по області padding-box. Для тіні використовуються radial-gradient(), background-clip та background-origin: border-box. Базові стилі схожы з попереднім прикладом:

$l: 6.25 em;
$bw: .1*$l;
border: solid $bw transparent;
padding: 3px;
width: $l; height: $l;
border-radius: 1.75*$bw;

Задаємо товсту прозору рамку так, щоб нам вистачило місця для тіні знизу. Робимо це для всіх тіней, не тільки нижній, так як для області padding-box нам потрібно однакову скруглення кутів (якщо забули, як це працює, дивіться відео за посиланням border-radius).

Перший background зверху це конічних-gradient(), і він створює конічний металевий блиск. Він обрізається по області content-box. Слідом під ним йде звичайний лінійний градієнт, який обрізається по padding-box. Ми використовуємо три внутрішніх тіні, щоб зробити другий фон менш плоским – додаємо ще одну тінь по колу з нульовим розмиттям, позитивним значенням різниці, робимо її яскравіше зверху за допомогою напівпрозорої білої тіні, а знизу – темніше за допомогою напівпрозорої чорної тіні.

box-shadow:
inset 0 0 0 1px #eedc00,
inset 0 1px 2px rgba(#fff, .5),
inset 0 -1px 2px rgba(#000, .5);
background:
conic-gradient(
#edc800, #e3b600, #f3cf00, #ffe800,
#ffe900, #ffeb00, #ffe000, #ebc500,
#e0b100, #f1cc00, #fcdc00, #ffe500,
#fad900, #eec200, #e7b900, #f7d300,
#ffe800, #ffe300, #f5d100, #e6b900,
#e3b600, #f4d000, #ffe400, #ebc600,
#e3b600, #f6d500, #ffe900, #ffe90a,
#edc800) content-box,
linear-gradient(#f6d600, #f6d600) padding-box

Для тіні додаємо третій фон, якому виставляємо background-clip та background-origin в border-box. Цей фон не повторює radial-gradient(), який ми прикріпили до низу (а по горизонталі-по центру) і стиснули по вертикалі так, щоб він вписався в нижню рамку і навіть залишав трохи місця. Радіальна тінь у нас займає близько .75 від border-width.

radial-gradient(rgba(#787878, .9), rgba(#787878, 0) 70%)
50% bottom / 80% .75*$bw no-repeat border-box

Ну ось і все! Можете погратися з готовими кнопками:

У властивості background-clip абсолютно точно є свої способи застосування! Зокрема, коли необхідно використовувати кілька шарів по краях елемента.