Вырезы в блоках по-старому и по-новому
В этом курсе мы создадим популярный интерфейсный паттерн — карточку с круглым вырезом. Реализуем это двумя способами и в финале сравним получившийся код.
Видеоверсия этого курса доступна во ВКонтакте и на Ютюбе
Важные особенности карточки:
- Под карточкой расположен неоднородный фон — фотография.
- Содержание карточки может изменяться, и при этом должна корректироваться её высота.
Сначала используем традиционный подход: находим прямоугольную область, в которой будет размещён изменяющийся контент (выделено жёлтым), а затем добавляем к этой области элементы, отвечающие за вырез.
Вырез состоит из трёх областей: сам вырез с фиксированными размерами (выделено зелёным) и области, которые по высоте подстраиваются под высоту карточки (выделены красным).
Удалим вспомогательные стили и начнём верстать с состояния, когда у нас есть только начальная разметка и стили для текстовых элементов карточки.
Добавим дополнительные div
для выреза — обёртку с классом .cutout
, содержащую три контейнера.
Обернём содержимое карточки в дополнительный контейнер с классом .card-content
.
Сделаем верхний контейнер компонента, .card
, флекс-контейнером и зададим ему фиксированную ширину.
Временно добавим обводки, чтобы сделать структуру более наглядной.
Зададим фиксированную ширину для обёртки содержания — .card-content
. Временно добавим обводки.
Добавим .card-content
внутренние отступы.
Зададим для .card-content
цвет текста и фона. Стилизация колонки с содержимым завершена.
Зададим контейнеру .cutout
фиксированную ширину и добавим временные обводки. Верхнеуровневая геометрия карточки готова.
Уберём лишние обводки и сосредоточимся на стилизации элементов внутри .cutout
.
Преобразуем .cutout
во флекс-контейнер, основная ось у которого направлена вниз. Это необходимо, чтобы впоследствии флекс-элементы могли растягиваться по высоте всей карточки.
Зададим крайним элементам .cutout-top
и .cutout-bottom
CSS-свойство flex-grow: 1
и цвет фона. Таким образом, они начинают растягиваться на весь контейнер. Их размер одинаков, потому что оба пустые и у обоих одинаковый коэффициент жадности.
Зададим для .cutout-center
фиксированную высоту. Крайние элементы «расступаются» и заполняют всё пространство сверху и снизу от будущего выреза.
Добавим внутрь .cutout-center
изображение выреза. Мы сделали это с помощью радиального градиента, но можно было также использовать любое изображение с полупрозрачностью и подходящей формой (например, PNG или SVG).
Уберём лишние обводки. Стилизация карточки с вырезом завершена.
Подобную реализацию можно было сделать не только с помощью флексов, но и с использованием гридов или абсолютного позиционирования. Объём кода и сложность при этом были бы примерно одинаковыми.
Проверим, как карточка ведёт себя при изменении текста. После добавления текста высота карточки увеличилась. Вырез ведёт себя отлично.
Однако если мы захотим добавить карточке неоднородный фон, например, градиент, то столкнёмся с проблемой.
Карточка разбита на несколько областей, поэтому добавить ей неоднородный фон, который правильно стыкуется на границах этих областей, практически невозможно. Это обусловлено тем, что стыковка должна сохраняться даже при изменении высоты.
В целом, дедовский способ вёрстки вырезов хорошо решает задачу, но не обеспечивает полной гибкости.
Вернёмся в исходное состояние — удалим все стили и дополнительные обёртки в разметке.
Попробуем сверстать карточку современным способом — с помощью CSS-масок.
Зададим для .card
цвета текста и фона.
Зададим для .card
фиксированный размер и внутренние отступы. Отступ слева отличается от предыдущего варианта (там был 75px
, а здесь 150px
).
Добавим одно свойство с маской и...
Всё. Вёрстка полностью завершена. Это удалось сделать с помощью одного свойства mask-image
с радиальным градиентом внутри.
Давайте разберёмся, как работает эта маска. Для этого временно заменим mask-image
на background-image
, чтобы визуализировать форму маски.
И упростим радиальный градиент внутри маски. Сейчас это простейший радиальный градиент из двух цветов: прозрачный внутри и чёрный снаружи. Градиенту принудительно задана круглая форма.
Добавим в градиент позиции цветов, чтобы получился резкий цветовой переход нужного нам размера. Позиция первого цвета специально установлена на полпикселя меньше (74.5px), чем у второго, чтобы сгладить переход между цветами.
С помощью at 0 50%
смещаем центр градиента в середину левой стороны контейнера .card
.
Нужная нам форма маски завершена. Когда мы активируем маску, прозрачная область будет вырезана, а чёрная останется видимой.
Заменим background-image
обратно на mask-image
и активируем маску.
Протестируем поведение выреза при изменении содержимого. Вырез ведёт себя идеально. Но это ещё не всё.
Мы не разрезали .card
на несколько разных блоков. .card
— это цельный блок.
Поэтому мы можем легко использовать любые неоднородные фоны: градиенты, изображения и так далее.
Мы можем нарисовать «линию отреза». Также можем использовать несколько изображений, комбинируя линию отреза с другими фонами.
Современное решение с масками в 10 раз быстрее традиционного. Вы можете самостоятельно сравнить объём кода и убедиться в этом. К тому же оно даёт невероятную гибкость в использовании фонов.
Это решение можно использовать уже сейчас, так как маски стали широко доступны в 2023 году, а радиальные градиенты — ещё раньше. Будущее наступило — поздравляем всех с этим!