Иногда кажется, что дизайнеры придумывают интерфейсные паттерны, чтобы поиздеваться над разработчиками. Впуклый угол — один из них.
«Инвертированный», «вогнутый», «впуклый» угол, а также negative border radius или inverted border radius. Когда слышите это — знайте, дальше будут страдания.
Проблема в том, что приём выглядит просто, но нативного способа сделать такой угол в CSS нет. Поэтому в ход идут тучи дополнительных обёрток и море стилей для их позиционирования. И всё это ломается на неоднородном фоне.
В этой статье я покажу простой способ создания впуклых углов с помощью опенсорсной CSS‑библиотеки, которую я разработал специально под эту задачу.
Почему инвертированные углы — это боль
Знакомьтесь с нашим героем на скриншоте:

Самый популярный способ реализации выглядит примерно так:
добавляем дополнительные обёртки и псевдоэлементы;
абсолютно позиционируем кусочки;
подгоняем радиусы, размеры и отступы.
На практике получаем море кода на каждый угол и хрупкое решение, которое сложно изменять.
Ниже скриншот одного из популярных роликов про реализацию впуклого угла. На скриншоте видна примерно одна пятая от общего объёма кода реализации.

Проблемы с фоном
Типовые решения работают только на сплошных фонах. Как только появляется неоднородный фон, нужно использовать 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 Боль». Там собраны все материалы, видеоролики, ссылки на интерактивные пошаговые демки
Комментарии (67)

Dima8249
22.01.2026 19:16А разве свойств
clip-pathилиmask-imageнедостаточно?
Однако одна простая библиотека всяко проще, если нужно сделать что-то подобное на скорость.
AlexPershin Автор
22.01.2026 19:16А библиотека и построена на
mask-imageВесь вопрос в том, как сделать удобную кастомизацию с помощью нескольких понятных переменных. Ведь не будешь каждый раз готовить отдельную картинку для маски, когда надо такой типой паттерн реализовать.

SelenIT3
22.01.2026 19:16clip-pathпока не везде достаточно. Нужную форму адаптивно можно сделать только сshape(), которой еще нет в Фоксе и старых Сафарях, а более распространеннаяpath()понимает только фиксированный размер в пикселях.
SolidSnack
22.01.2026 19:16А еще можно только с polygon который задает точки в процентах и прекрасно адаптируется и работает у меня в фоксе почему-то...

AlexPershin Автор
22.01.2026 19:16Там внизу попытались сделать аналогичное решение на
clip-pathЯ его тестирую, но как-то оно не очень работает. Видимо не так прост клип-пас
https://habr.com/ru/articles/987944/comments/#comment_29425270

Belarus
22.01.2026 19:16Не думали скруглять углы как Apple?

AlexPershin Автор
22.01.2026 19:16а как это выглядит?

Belarus
22.01.2026 19:16Называетса квадрокруг, квадратокруг или сквиркл:

Он справа -> 
Leif
22.01.2026 19:16Есть же corner-shape
https://habr.com/ru/articles/938886/?ysclid=mkqds7o6px813303863

Belarus
22.01.2026 19:16По ссылке этот сквиркл идёт сразу после обычново скругления. Или есть ещё варианты? Иначе не понимаю, почему ответили этим мне, а не автру.

AlexPershin Автор
22.01.2026 19:16Для этой штуки есть нативная фича (но достаточно свежая), выше ссылку дали. Плюс что-то такое можно обычным border попытаться имитировать.

SolidSnack
22.01.2026 19:16Проблема в том, что приём выглядит просто, но нативного способа сделать такой угол в CSS нет.
Интересно как, а clip-path это для вас шутка какая-то?) Всё скрины что вы показали делаются буквально за пару минут.
Ещё и 29 лайков на инструмент который заменяется чтением mdn спеки...

CzarOfScripts
22.01.2026 19:16Clip-path умеет скруглять именно полигоны?

AlexPershin Автор
22.01.2026 19:16А можете прислать пример кода?

SolidSnack
22.01.2026 19:16
Вот вам пример кода сразу с результатом из документации)) Там не то что закруглить как-то можно, можно все что угодно)

AlexPershin Автор
22.01.2026 19:16Спасибо за SVG! Особенно, когда речь идёт о том, чтобы избавиться от SVG и добиться лёгкой кастомизации.
Мне почему-то показалось, что вы хотели за пару минут воспроизвести на clip-path такой же впуклый угол, как в статье. А вы случайные примеры присылаете
SolidSnack
22.01.2026 19:16А за HTML не спасибо? Документацию открыли бы хоть...

SolidSnack
22.01.2026 19:16Мде, я если честно не знаю что у вас за претензия к svg, но clip-path принимает результат в виде basic-shape, легко может через функцию path и svg формат употребить, если это вам что-то дает...
Отличные минусы в карму, за ваше нежелание почитать документацию))
init0
22.01.2026 19:16Я подозреваю потому, что вы написали "Всё скрины что вы показали делаются буквально за пару минут." а в итоге у вас невнятный и не очень релевантный пример из документации

SolidSnack
22.01.2026 19:16Извините, вы меня путаете с чатомГПТ который по вашему велению выдает код. Все относительно, если документацию почитаете, то пара минут. Если нет, ну нет.

SolidSnack
22.01.2026 19:16Как говорится - покажи каплю воды мудрецу, он поймет что в мире существует водопад. Укажи фронтендеру на то что надо читать документацию, тебе налепят минусов. Старая мудрость

ionicman
22.01.2026 19:16Оно реально и делается за 5 минут.
Кастомизировать можно через переменные/классы и это будет праильно, нативно и без костылей. Человек выше все праавильно написал.
И чтобы не быдь голословным и чтобы отстали от человека выше:) вот тотже шейп, что и в статье (правда абсолютно непонятно, где такую всрат..ю форму можно применить):
clip-path: path( "m14.065 2.4e-4c-7.7921 0-14.065 6.2732-14.065 14.065v27.491c0 7.7921 6.2735 13.997 14.065 14.065l62.031 0.54156c18.869 0.15509-1.4926-18.141 22.957-17.76 19.856 0.3092 19.105-4.2759 19.002-9.8528l0.18552-14.485c0.0998-7.7914-6.2732-14.065-14.065-14.065z" );Чтобы получить заготовку, можно, например, в Inkscape (бесплатно) отрисовать любую форму или заготовки форм, материлизовать все трансформации и сохранить как "Optimized SVG", затем открываете SVG текстовым редактором и то что в path переносите в cli-path: path( "вот сюда" ), используя CSS-пременные, можно сделать любой конструктор.
Это следование стандартам, лучшая оптимизация и не изобретение своего велосипеда. Читать документацию иногда реально полезно.

AlexPershin Автор
22.01.2026 19:16Спасибо! Хоть кто-то заморочился и постарался сделать пример на
clip-path.
Но у меня возникла проблема при использовании. Я взял обычную карточку в потоке и применил к ней ваш код. Получилось не так, как в примерах из статьи. Скрины с кодом прикладываю
исходное состояние 
применил clip-path

eungenue
22.01.2026 19:16Круто, красиво, правда!
Но! Какая-же все-таки дичайшая дичь все эти ваши «дизайнерские» изыски поверх html + css. Оставьте уже в покое версию моего браузера и батарейку. Да, и оперативу теперь тоже оставьте вдвойне.

AlexPershin Автор
22.01.2026 19:16Это всё вопросики к дизайнерам =) Тут можно ещё ликвид-гласс вспомнить. Дизайнеры придумали, а разработчикам и батареям страдать

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

AlexPershin Автор
22.01.2026 19:16звучит выразительнее =) ну и устоявшегося термина нет.
когда делал доклад для фронтендконфа перебирали разные варианты, и этот зашёл больше всех. вот его и в статье решил придерживаться

Bardakan
22.01.2026 19:16звучит скорее как галюцинация нейросети)

AlexPershin Автор
22.01.2026 19:16Ну нее, это тёплый, авторскии и человеческий эпитет =)
Но я согласен, что нейросети настолько всех достали, что в любом непонятном случае уже начинаешь их видеть =)

AlexPershin Автор
22.01.2026 19:16
Кстати, помучал чатгпт. Он термин "впуклый угол" даже не предлагает =)

1dNDN
22.01.2026 19:16Почему нет? Я тоже так называю.

WASD1
22.01.2026 19:16Потому, для чего существуют устоявшиеся термины: что само-придуманные названия будут хуже гуглится, чем устоявшиеся (после того, как человек прочитав статью начнёт что-то ещё и искать).
Но вообще я так со стороны проходил посмотреть, фронтэндом не знанимаюсь.
AlexPershin Автор
22.01.2026 19:16В англоязычной среде есть два более-менее устоявшихся, в русскоязычной не думаю, что что-то устоялось. Так что можно творить! =)

justingotch
22.01.2026 19:16Эх молодежь)


AlexPershin Автор
22.01.2026 19:16Если честно, страшно смотреть. И вот вопрос чем это решение лучше и проще решения на css-градиентах и css-масках?
И вдогонку. Как там сделать кастомизацию, чтобы как в статье можно было одной переменной радиусы скруглений настроить?

justingotch
22.01.2026 19:16Это решение лучше тем, что легко адаптивить, с маской не заадаптивить корректно, тем более не подогнать под разное количество контента, она просто будет сплющиваться. Если используешь переменные, то с clip-path меняются просто значения для определенного радиуса, которые устанавливаются в одну и ту же формулу. Я написал себе скрипт, который вычисляет значения для определенного радиуса, в него просто закидываешь svg. Также же есть онлайн-сервисы для конвертации

AlexPershin Автор
22.01.2026 19:16То есть для того, чтобы как-то поддерживать и модифицировать это решение нужны специальные скрипты и специальные генераторы? Выглядит очень дорогим, сложным и тяжеловесным. А ещё очень плохо передаваемым другим разработчикам, которым в наследство достанется этот код. Короче, clip-path тоже эту задачу решает, но не быстрее и точно не проще.
Что касается адаптивности, и сплющивания решения на масках. Почему вы так решили? Вы тестировали и не сработало? Покажете примеры скриншотами

justingotch
22.01.2026 19:16Я не покажу сейчас примеры, т.к. я не храню такое по несколько лет, но ты можешь показать свой пример, где все ок с одной и той же маской? По поводу тяжеловесности сомнительно, т.к. скрипт сторонний, да даже онлайн-сервис какой-то, это скрипт не для проекта, а упрощения генерации таких вещей

AlexPershin Автор
22.01.2026 19:16если говорить про сложные произвольные формы, то видимо без клип-паса никуда. это статья про решение для конкретного интерфейсного паттерна. вы скорее всего привыкли везде использовать клип-пас, у вас есть для этого инструменты и навыки.
но не все так могут. поэтому им проще подключить уже готовую библитечку

justingotch
22.01.2026 19:16Да, я глянул подробнее библиотеку, там решено все с адаптивом, но остаются минусы с кликами, и в целом размер решения больше, чем clip-path. У меня на скрине просто скомпилированный код из scss, на scss формулы с переменными максимально понятны, есть онлайн генератор адаптивных clip-path, загрузил обычный svg, настроил точки визуально, относительно которых высчитывать расстояния, скопировал clip-path и готово

AlexPershin Автор
22.01.2026 19:16Это да, за гибкость расплачиваемся размером.
По поводу scss у меня мысли следующие. Они дают возможность написать генератор, но генератор выдаст статичный код, без возможности изменять что-то в рантайме. А вот использование нативный css-переменных даёт возможнось кастомизации в рантайме. То есть можно менять параметры угла при наведении или при адаптиве.
Вот тут в самом конце показано, как можно адаптивно перемещать вырез в разные углы https://htmlacademy.ru/demos/197
Короче, нативные css-переменные дают дикую гибкость, причём в рантайме
Насчёт кликов не понял. А что с ними за минусы?
justingotch
22.01.2026 19:16Я писал в одном из комментариев, если например на карточке нужно обработать нажатие, то логично, что клик по вырезу не должен сработать. может я конечно не до такой глубины проанализировал эту библиотеку и там есть возможность исключить вырез из области нажатия

justingotch
22.01.2026 19:16То есть, чтобы использовать маски, тебе нужны для каждого разрешения при адаптиве разные маски, и то, при изменении контента, его количества, все ломается. С помощью clip-path это максимально адптивно, ты просто в процентах указываешь на каком расстоянии от какого угла должен быть вырез, и все, пусть там хоть в 10 раз будет больше контента, ничего не сломается

AlexPershin Автор
22.01.2026 19:16Вы про css-маски вообще или конкретно про библиотеку из статьи? Она как раз тоже под это заточена

justingotch
22.01.2026 19:16Вообще про css-маски. Тащить ради такого библиотеку сомнительное решение.

AlexPershin Автор
22.01.2026 19:16ну по сути это сниппет css-кода, в который уже вшита кастомизация с помощью css-переменных и calc
я бы напротив сомнительным назвал решение для простой типовой задачи тащить в проект адскую сгенерированную кашу на клип-пасе. банально из-за вопросов дальнейшей поддержки. такой код будет очень сложно кому-то передать на поддержку/модификацию и так далее
justingotch
22.01.2026 19:16Не согласен, ты видел, что под капотом этого css? Гораздо более непонятная каша. Clip-path в моем примере полностью на формуле основано. Плюс нашел еще минус библиотеки, если нужно кликать по карточке, то клик по пустому пространству проходит, а не должен.

AlexPershin Автор
22.01.2026 19:16если что-то непонятно вам, то это не значит, что решение плохое или неэффективное. для конечного пользователя всё определяется простотой и скоростью использования.
в случае впуклого угла ты подключаешь стили, накидываешь класс, подбираешь параметры и готово
в целом, вам никто не мешает сделать похожий css-сниппет для вогнутых углов, внутри которого вместо масок и градиентов будет использоваться клип-пас. но главное, подготовить удобные css-переменные ручки для настроек
justingotch
22.01.2026 19:16Я нигде и не говорил, что решение в целом плохое или неэффективное. Кому-то да, это решение эффективное и отличное, кому-то нет. Для меня лично неэффективное и раздувает css/html, а также не предусматривает возможность клика. Если все это неважно кому-то, ради бога) Я изначально лишь показал, как это делается короче, более гибко и конкретно мной за несколько минут. Ну и плюс, если конкретно тебе непонятно мое решение, это не значит, что оно плохое или неэффективное, а именно это ты и пытаешсья с первого комментария мне донести.

AlexPershin Автор
22.01.2026 19:16А я не пишу, что ваше решение мне непонятно. Там всё понятно. Проблема в другом. Это трудоёмкая штука, которая вручную не делается. Подозреваю, что сначала надо в каком-то редакторе нарисовать нужную форму, потом её экспортировать, загнать в генератор клип-пасов и только потом вставлять в стили.
Первый комментарий был как раз ваш про клип-пас. Ну собственно разобрались, что клип-пас — это ваш стандартный инструментарий. Ок, нужная штука. Просто эта библиотека написана не на нём.
И ещё важный момент, который повторю специально. Ваше решение по сути универсальное, но оно не подразумевает удобной кастомизации. Библиотека из статьи очень узкая, зато с удобной кастомизацией

justingotch
22.01.2026 19:16Ах, ну и да, эта библиотека только для одного кейса, а если вырезы сложнее, и не один? Поможет ли данная библиотека?

AlexPershin Автор
22.01.2026 19:16статья описывает возможности мини-библиотеки для решения одной конкретной задачи. как вы думаете, заточена ли она под другие задачи =)

ivanstrilets
22.01.2026 19:16Зачем это все если есть clip-path?

AlexPershin Автор
22.01.2026 19:16Как раз для того чтобы не мучаться c
clip-pathВыше отличный пример решения, он даже страшнее чем SVG

ivanstrilets
22.01.2026 19:16Мучаться с clip-path?) Один раз понять и проблем не будет. Можно писать его по разному, человек просто полигон использовал а не rect.

AlexPershin Автор
22.01.2026 19:16а можно не надо?
https://habr.com/ru/articles/987944/comments/#comment_29427354
GlazOtca
Это конечно, здорово! А как можно сделать так, чтобы при наведении курсора на блок с вырезом у него появлялся бордер синего цвета и плавно переходил в бордер красного цвета?
AlexPershin Автор
Если вас дизайнер заставляет делать такое, то искренне сочувствую =)
Вообще, есть задумка, как и такое реализовать, надо будет поиграться. Если получится — покажу
AlexPershin Автор
@Ankhena у меня куда-то пропал ваш комментарий. но по сути да, нужна обложка, потом её масштабировать. и затем подгонять маску обложки. про это буду на митапе питерджиес рассказывать на следующей неделе как раз
Ankhena
Почему-то комментарий был отклонён.
Суть в том, что маске псевдика задаем inherit, чтобы не играть в примешивание стилей из библиотеки. Корректируем кастомками размеры скруглений и размеров вырезов. Можно через calc. И абсолютим.
AlexPershin Автор
Это может быть я по невнимательности промахнулся. Хабр автору статьи отдаёт на модерацию =)
Ну вот, наверное, что-то такое придётся делать в случае с этой библиотекой. Я то надеялся просто добавить на подложку маску с такими же параметрами, а потом подложку масштабировать трансформ-скейлом. Но чую, что будут проблемы с радиусами. Хотя на достаточно тонких "рамках" должно смотреться нормально
Ankhena
А, понятно ))
Тогда восстановлю с куском кода, вдруг кому пригодится.
Что-нибудь в таком духе. Но потребуется обертка для внутреннего контента. Либо наоборот, делаем внешнюю обертку, ей классы nebo, а внутренней mask: inherit и т.д.
Но суть одна, нужно что-то (другой блок, обертка, псевдик) для второй маски и так, чтобы маленькая маска не обрезала большую.
AlexPershin Автор
Вот, кстати, подробная демка, где разбирается следующий шаг — подгонка масок
https://htmlacademy.ru/demos/247