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

«Инвертированный», «вогнутый», «впуклый» угол, а также negative border radius или inverted border radius. Когда слышите это — знайте, дальше будут страдания.

Проблема в том, что приём выглядит просто, но нативного способа сделать такой угол в CSS нет. Поэтому в ход идут тучи дополнительных обёрток и море стилей для их позиционирования. И всё это ломается на неоднородном фоне.

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

Почему инвертированные углы — это боль

Знакомьтесь с нашим героем на скриншоте:

Видите форму как у красной линии — спасайтесь!
Видите форму как у красной линии — спасайтесь!

Самый популярный способ реализации выглядит примерно так:

  • добавляем дополнительные обёртки и псевдоэлементы;

  • абсолютно позиционируем кусочки;

  • подгоняем радиусы, размеры и отступы.

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

Ниже скриншот одного из популярных роликов про реализацию впуклого угла. На скриншоте видна примерно одна пятая от общего объёма кода реализации.

Пример реализации от Kevin Powell
Пример реализации от Kevin Powell

Проблемы с фоном

Типовые решения работают только на сплошных фонах. Как только появляется неоднородный фон, нужно использовать SVG. И CSS‑боль превращается в SVG‑боль.

Решение мечты для впуклых углов

О чём мечтает разработчик, когда видит впуклый угол:

  • простое решение на чистом CSS;

  • работающее из коробки, в том числе на неоднородных фонах;

  • с возможностью нативной кастомизации размеров и формы углов.

И такое решение существует! Это библиотека nebo.css, с которой мы познакомимся в этой статье. Название происходит от названия паттерна — negative border radius.

Подключение nebo.css и первый впуклый угол

nebo.css — это один CSS‑файл, по духу очень похожий на normalize.css: подключил — и используешь. Внутри нет JavaScript, всё сделано на чистом CSS.

Подключаем библиотеку

<link rel="stylesheet" href="nebo.css">

Этого уже достаточно, чтобы начать.

Добавляем один угол карточке

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

<article class="card nebo">...</article>

Результат:

Впуклый угол с параметрами по умолчанию
Впуклый угол с параметрами по умолчанию

Меняем сторону угла

Сторона задаётся модификаторами:

  • nebo--tr — сверху справа,

  • nebo--tl — сверху слева,

  • nebo--bl — снизу слева,

  • nebo--br — снизу справа.

<article class="card nebo nebo--tr">...</article>

Результат:

Впуклые углы с разных сторон на сложном фоне
Впуклые углы с разных сторон на сложном фоне

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

Базовая кастомизация

Все параметры nebo.css настраиваются через нативные CSS‑переменные, которые подчиняются обычному каскаду.

Общий радиус скруглений: --nb-r

Эта переменная управляет радиусами скруглений в угле. Значение по умолчанию — 20px. Немного уменьшим его.

.nebo {
  --nb-r: 16px;
}

Ширина и высота выреза: --nb-w и --nb-h

Эти переменные задают дополнительные смещения между внутренним и внешними радиусами.

.card--cat1 {
  --nb-w: 42px; 
  --nb-h: 16px;
}

Результат:

Уменьшение радиусов, дополнительная ширина и высота угла
Уменьшение радиусов, дополнительная ширина и высота угла

Разные настройки для разных элементов

Так как мы работаем с обычным каскадом, каждый элемент можно на��траивать по‑своему:

.card--cat1 { --nb-w: 42px; --nb-h: 16px; }
.card--cat2 { --nb-w: 32px; --nb-h: 42px; }
.card--cat3 { --nb-w: 64px; --nb-h: 42px; }
.card--cat4 { --nb-w: 42px; --nb-h: 16px; }

Результат:

Разные параметры углов у разных карточек
Разные параметры углов у разных карточек

Кстати, углы корректно отображаются даже при нулевых значениях (0px) — главное не забывать единицы измерения.

Несколько инвертированных углов у одного элемента

nebo.css изначально решает одну задачу: один элемент — один угол. Но если нужно несколько углов, в ход идёт проверенный приём — дополнительная обёртка.

Используем обёртки

<div class="card-wrapper nebo nebo--bl">
  <article class="card nebo nebo--tr">...</article>
</div>

Результат:

Карточка с двумя впуклыми углами
Карточка с двумя впуклыми углами

Каждый уровень можно настраивать отдельно через CSS‑переменные. Можно собрать и три, и четыре угла — просто добавляя обёртки.

Продвинутая кастомизация

Общий радиус для всех скруглений, который задаётся с помощью переменной --nb-r, можно переопределить для каждого из трёх скруглений: двух внешних и одного внутреннего. Причём можно отдельно задать как вертикальные, так и горизонтальные радиусы. Это делается с помощью шести дополнительных CSS-переменных:

  --nb-cor1-rw,
  --nb-cor1-rh,
  --nb-cor2-rw,
  --nb-cor2-rh,
  --nb-curve-rw,
  --nb-curve-rh,

Благодаря этим переменным можно получать впуклые углы очень сложных форм. Например:

.card--cat1 {
  --nb-r: 30px;
  --nb-w: 0px;
  --nb-h: 0px;

  --nb-curve-rw: 120px;
  --nb-curve-rh: 90px;

  --nb-cor1-rw: 90px;
  --nb-cor1-rh: 90px;

  --nb-cor2-rw: 60px;
  --nb-cor2-rh: 60px;

  --_nb-smooth: 99.5%;
}

Результат:

Впуклый угол с разными радиусами скруглений
Впуклый угол с разными радиусами скруглений

Пример кейса: карточка из двух блоков

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

Разметка

<div class="card">
  <div class="card-header"></div>
  <div class="card-body">
    <div class="price">$114,000</div>
    ...  
  </div>
</div>

Добавляем инвертированные углы

<div class="card-header nebo nebo--bl"></div>
<div class="card-body nebo nebo--tr">...</div>

Настраиваем параметры и совмещаем блоки

В примере использован быстрый грязный приём с отрицательным маргином, чтобы подтянуть блок с картинкой к блоку с описанием. Но цель была — показать простоту создания вырезов.

.card-header {
  --nb-r: 10px;
  --nb-w: 130px;
  --nb-h: 44px;
  margin-bottom: -54px;
}

.card-body {
  --nb-r: 10px;
  --nb-w: 160px;
  --nb-h: 44px;
}

Результат:

Сложный кейс из двух карточек с впуклыми углами
Сложный кейс из двух карточек с впуклыми углами

Обратите внимание, что всё отлично работает с неоднородным фоном.

Где взять nebo.css

nebo.css — это бесплатная опенсорсная библиотека, разработанная автором. Лежит на GitHub.

Репозиторий:
https://github.com/htmlacademy/nebo.css

Библиотеку можно просто подключить отдельным файлом или скопировать код прямо в свои стили. Пользуйтесь и наслаждайтесь!

А где взять ещё больше сложного CSS и код всех примеров?

Подписывайтесь на мой телеграм‑канал «CSS Боль». Там собраны все материалы, видеоролики, ссылки на интерактивные пошаговые демки