Читайте начало статьи по ссылке.
Добавление радиуса границы
Если вы попытаетесь добавить border-radius к элементу при использовании непрозрачного решения, с которого мы начали, это будет довольно тривиальная задача. Все, что вам нужно сделать, это наследовать то же значение от основного элемента, и все готово.
Даже если у вас нет радиуса границы, рекомендуется определить border-radius: inherit. Это учитывает любой потенциальный радиус границы, который вы, возможно, захотите добавить позже, или радиус границы, полученный откуда-то еще.
Другое дело, когда имеешь дело с прозрачным решением. К сожалению, это означает поиск другого решения, потому что clip-path не может справиться с кривизной. Это означает, что мы не сможем вырезать область внутри основного элемента.
Мы введем в микс свойство mask.
Эта часть была очень утомительной, и я изо всех сил пытался найти общее решение, которое не полагалось бы на магические числа. В итоге я получил очень сложное решение, в котором используется только один псевдоэлемент, но код представлял собой груду спагетти, охватывающую лишь несколько частных случаев. Не думаю, что стоит исследовать этот путь.
Я решил вставить лишний элемент ради более простого кода. Вот разметка:
Я использую пользовательский элемент <sh>, чтобы избежать любого потенциального конфликта с внешним CSS. Я мог бы использовать div, но так как это общий элемент, он может быть легко затронут другим правилом CSS, пришедшим откуда-то еще, что может сломать наш код.
Первым шагом является позиционирование элемента <sh> и намеренное создание переполнения:
Код может показаться немного странным, но мы доберемся до его логики по ходу дела. Затем мы создаем градиентную тень, используя псевдоэлемент <sh>.
Как видите, псевдоэлемент использует тот же код, что и все предыдущие примеры. Единственное отличие состоит в том, что 3D-преобразование определено для элемента <sh> вместо псевдоэлемента. На данный момент у нас есть градиентная тень без функции прозрачности.
Обратите внимание, что область элемента <sh> выделена черным контуром. Почему я это делаю? Потому что таким образом я могу применить к нему маску, чтобы скрыть часть внутри зеленой области и оставить переполненную часть там, где нам нужно видеть тень.
Я знаю, что это немного сложно, но, в отличие от clip-path, свойство mask не учитывает область вне элемента для отображения и скрытия элементов. Поэтому я был вынужден ввести дополнительный элемент — моделировать «внешнюю» область.
Также обратите внимание, что я использую комбинацию border и inset для определения этой области. Это позволяет мне сохранить поле заполнения этого дополнительного элемента таким же, как у основного элемента, так что псевдоэлементу не потребуются дополнительные вычисления.
Еще одна полезная вещь, которую мы получаем от использования дополнительного элемента, заключается в том, что элемент фиксируется, а перемещается только псевдоэлемент (используя translate). Это позволит мне легко определить маску, что является последним шагом этого трюка.
Смотрите пример по ссылке.
Готово! У нас есть градиентная тень, и она поддерживает радиус границы! Вы, наверное, ожидали сложного значения маски с кучей градиентов, но нет! Нам нужны только два простых градиента и составная маска, чтобы завершить волшебство.
Давайте изолируем элемент <sh>, чтобы понять, что там происходит:
Вот что мы получаем:
Обратите внимание, как внутренний радиус совпадает с border-radius основного элемента. Я определил большую границу (150 пикселей) и border-radius, равный большой границе плюс радиус основного элемента. Снаружи у меня радиус равен 150px + R. Внутри у меня 150px + R - 150px = R.
Мы должны скрыть внутреннюю (синюю) часть и убедиться, что граница (красная) все еще видна. Для этого я определил два маскирующих слоя: один покрывает только область содержимого, а другой покрывает область рамки (значение по умолчанию). Затем я исключил одно из другого, чтобы выявить границу.
Полный пример можно посмотреть по ссылке.
Я использовал ту же технику для создания границы, которая поддерживает gradients и border-radius.
Есть ли недостатки у этого метода?
Да, это определенно не идеально. Первая проблема, с которой вы можете столкнуться, связана с использованием границы основного элемента. Это может привести к небольшому смещению радиусов, если вы его не учтете. У нас есть эта проблема в нашем примере, но, возможно, вы ее едва заметите.
Исправить это относительно просто: добавьте ширину границы для inset элемента <sh>.
Другим недостатком является большое значение, которое мы используем для границы (150px в примере). Это значение должно быть достаточно большим, чтобы содержать тень, но не слишком большим, чтобы избежать проблем с переполнением и полосой прокрутки. К счастью, онлайн-генератор рассчитает оптимальное значение с учетом всех параметров.
Последний недостаток, о котором я знаю, это когда вы работаете со сложным радиусом границы. Например, если вы хотите, чтобы к каждому углу применялся разный радиус, вы должны определить переменную для каждой стороны. Я полагаю, это не совсем недостаток, но ваш код может немного усложниться в обслуживании.
Онлайн-генератор учитывает только однородный радиус для простоты, но теперь вы знаете, как изменить код, если хотите учитывать сложную конфигурацию радиуса.
Подведение итогов
Мы дошли до конца! Магия градиентных теней больше не является загадкой. Я попытался охватить все возможности и возможные проблемы, с которыми вы можете столкнуться. Если я что-то упустил или вы обнаружите какие-либо проблемы, не стесняйтесь сообщить об этом в разделе комментариев, и я проверю это.
Опять же, многое из этого, вероятно, излишне, учитывая, что решение де-факто покроет большинство ваших вариантов использования. Тем не менее, полезно знать «почему» и «как» этот трюк и как преодолеть его ограничения. Кроме того, мы получили хорошее упражнение, играя с отсечением и маскированием CSS.
И, конечно же, у вас есть онлайн-генератор, к которому вы можете обратиться в любое время, когда захотите избежать хлопот.
Перевод статьи "Different Ways to Get CSS Gradient Shadows".