Тёмная сторона CSS: выходим за рамки и взрываем звезды с border-image и градиентами
Как вы думаете, сколько CSS-градиентов нужно, чтобы нарисовать каждую из этих фигур?
Один! 🤯 Причем его даже не надо дублировать: достаточно указать один раз в одном-единственном свойстве. Таким примером в CodePen на днях поделился Темани Афиф, автор занятного и познавательного проекта css-challenges.com.
Эта «магия» — заслуга свойства border-image
. У нас уже есть статья о нем и его возможностях. Увы, громоздкий синтаксис и неинтуитивное поведение — особенно с градиентами — до сих пор мешают ему стать популярным. Сам Афиф в Твиттере признал, что «border-image
принадлежит к тёмной стороне CSS»: очень уж трудно представить себе наглядно, как масштабируются, нарезаются и потом опять масштабируются части картинки. И многие даже не пытаются разобраться в нем. А зря!
Во-первых, уникальная способность border-image
выходить за края элемента может быть очень полезна в массе практических задач. Например, секции с косыми и неровными границами, выступающими из контейнера:
See the Pen
Untitled by Ilya Streltsyn (@SelenIT)
on CodePen.
или «отзывчивая» имитация старого ЭЛТ-экрана:
See the Pen
responsive old CRT screen with border-image and gradient by Ilya Streltsyn (@SelenIT)
on CodePen.
А во-вторых, border-image
— уже не единственное свойство с таким синтаксисом. Недавно к нему добавилось еще одно — mask-border
, из модуля СSS-масок 1 уровня. Оно уже работает в Safari и Chrome с аналогами (в виде -webkit-mask-box-image
). И задает маску для обрезки всего элемента — включая его фон и рамку, нарисованную через border-image
(можете еще раз посмотреть пример с косыми секциями, в самом низу:).
Чтобы овладеть мощью этой темной стороны CSS, надо поупражняться. Упражнение будет типичное для темной стороны: взорвать звезду! Правда, делать это мы будем на безопасном (относительно: может затянуть, как черная дыра!:) полигоне — в игре CSSBattle, одно из заданий которой (№83) так и называется — «Сверхновая»:
Напомним, задача этой игры — воспроизвести картинку 400×300 в окне такого же размера минимально возможным CSS-кодом. Рисовать «облака плазмы» по отдельности — не наш, не «тёмносторонний» путь 👾. Поступим проще: нарисуем 4 голубых круга на синем фоне, а поверх наложим синий квадрат с желтым кругом и повернем на 45°.
Круги получаются частично «наехавшими» друг на друга. Рисовать их отдельными элементами, множествеными фонами, тенями и т.п. — долго и скучно. Можно проще и короче!
See the Pen
Recreating CSSBattle Target#83 with border-image, part 1 by Ilya Streltsyn (@SelenIT)
on CodePen.
Вот как это работает:
- Определяем контуры будущей рамки. Пользуясь тем, что можно выходить за края элемента, добавляем по
50px
сверху и снизу, получается квадрат 400×400. - Заливаем этот квадрат синим градиентом с голубым кругом радиусом
64px
в центре. Это и будет наша исходная картинка для нарезки. - Из этой картинки выделяем и копируем 4 угловых «плитки» размером 254×254 пикселя (см. прошлую статью для наглядности). Центр круга попадает в каждую «плитку», но два крайних сегмента по 9px оказываются отрезанными.
- Масштабируем плитки до нужной толщины рамки. У нас рамка заполняет весь квадрат, без центральной «дырки», так что толщина —
200px
(половина стороны). Радиусы кругов уменьшатся в 254/200 = 1.27 раза, т.е. до50px
(столько и было нужно!) - Кладем плитки на углы квадрата, обрезанные края кругов стыкуются друг с другом. Всё!
Толщину в п. 4 можно задать не ровно 200px
, но и больше (например, 288px = 3in): итоговый размер плиток всё равно будет 200×200, они всегда ужимаются, чтобы поместиться. Этим можно сэкономить один-два символа.
Теперь маленький квадрат: у него размер 120×120, а круг внутри желтый, радиусом 30px
и строго по центру. Здесь нам фактически не нужна сама рамка, только центральная плитка — аналог фона. За нее отвечает ключевое слово fill
, у которого есть еще и приятный побочный эффект — картинка рисуется независимо от наличия обычного border
-а:
See the Pen
Recreating CSSBattle Target#83 with border-image, part 2 by Ilya Streltsyn (@SelenIT)
on CodePen.
В CSSBattle мы по умолчанию на «тёмной стороне» HTML — в Quirks mode, «режиме совместимости со старыми глюками». А значит, можем отбросить height
(в Quirks mode в Chrome body
само растягивается на доступную высоту html
) и еще кое-что (предупреждение: так можно только в CSSBattle, не повторяйте это в реальных проектах!), чуть уменьшив код. Копируем его в окошко игры, жмем Submit… БУМ! 💥 В космосе звук не распространяется, но радость от успешного испытания слышна аж на сервере 😂
Но писать «border-image: radial-gradient» два раза — всё равно долго и скучно. Подправим-ка стили body
, чтобы унифицировать его border-image
с html
:
body { border-image: radial-gradient(#EEB850 30px,#243D83 0) 0 fill / 0 / 50px 0; margin: 140; /* размер автоматом станет 120x20 */ }
Самому body
не обязательно быть квадратным, квадратной должна быть лишь рамка – так что делаем его прямоугольным (заодно margin
сократился) и используем уже знакомый трюк. Теперь можно написать border-image: radial-gradient()
один раз, для универсального селектора, а для body
только переопределить цвет и радиус внутреннего круга и начальный/конечный размер угловых плиток. Это можно сделать с помощью кастомных свойств (CSS-переменных). Но в CSS есть пара нативных фич, ведущих себя как переменные: currentColor
для цветов и относительные единицы измерения типа em
для размеров. Так что решение сокращается до…
See the Pen
Recreating CSSBattle Target#83 with border-image, part 2 by Ilya Streltsyn (@SelenIT)
on CodePen.
У html
переход от первой цветовой зоны ко второй находится в 4em = 64px
от центра, т.е. дальше края второй (желтой) зоны, а значит, вторая зона не видна. У body
же, наоборот, первая (голубая) зона сжимается в ноль (1em = 0
), и видна только желтая. Заодно обнуляется и толщина рисованной рамки. Лишь для «вырезки» плиток пришлось использовать отдельную переменную, а не размер в em
— это безразмерное число, а не длина, и в CSS это несоизмеримые друг с другом величины (как метры и килограммы).
Путем зверской минификации это решение можно ужать как минимум до 140 знаков. Заявка на место в первой десятке! Еще раз посмотрим на процесс нарезки и масштабирования «плиток» в анимации:
See the Pen
Recreating CSSBattle Target#83 with border-image, part 1 by Ilya Streltsyn (@SelenIT)
on CodePen.
Можете поменять значения вверху и посмотреть, что изменится (примечание: тут отображается только рамка, без центральной «плитки» — так нагляднее). Надеюсь, это упражнение поможет вам легче понять, что и как вырезается, копируется и растягивается в примерах в начале статьи, и создавать свои по аналогии.
Пускай же CSS раскрывает перед вами все свои тайны, даже темные, пополняя ваш арсенал суперсилами для побед над самыми сложными задачами. Дерзайте, не бойтесь экспериментировать! Но не позволяйте тёмной стороне взять верх над вами, и в реальных проектах не забывайте про доктайп 😉
P.S. Это тоже может быть интересно:
Менюшка топ)