HTML Diff
0 added 0 removed
Original 2026-02-18
Modified 2026-03-09
1 <p>Jan 26, 2026</p>
1 <p>Jan 26, 2026</p>
2 <p>Sometimes it feels like designers invent interface patterns just to mess with developers. A concave corner is one of those.</p>
2 <p>Sometimes it feels like designers invent interface patterns just to mess with developers. A concave corner is one of those.</p>
3 <p>“Inverted”, “concave”, “negative” corners - also known as <em>negative border radius</em>or <em>inverted border radius</em>. If you hear any of that, brace yourself. Pain is coming.</p>
3 <p>“Inverted”, “concave”, “negative” corners - also known as <em>negative border radius</em>or <em>inverted border radius</em>. If you hear any of that, brace yourself. Pain is coming.</p>
4 <p>The problem is that the pattern looks simple, but there’s no native way to create such a corner in CSS. So people resort to piles of extra wrappers and oceans of positioning styles. And all of that immediately falls apart on non-uniform backgrounds.</p>
4 <p>The problem is that the pattern looks simple, but there’s no native way to create such a corner in CSS. So people resort to piles of extra wrappers and oceans of positioning styles. And all of that immediately falls apart on non-uniform backgrounds.</p>
5 <p>In this article, I’ll show a simple way to create concave corners using an <em>open-source CSS library</em>that I built specifically to solve this problem.</p>
5 <p>In this article, I’ll show a simple way to create concave corners using an <em>open-source CSS library</em>that I built specifically to solve this problem.</p>
6 <h2>Why inverted corners are a pain</h2>
6 <h2>Why inverted corners are a pain</h2>
7 <p>Meet our hero in the screenshot below:</p>
7 <p>Meet our hero in the screenshot below:</p>
8 <p>If you see a shape like the red outline - run</p>
8 <p>If you see a shape like the red outline - run</p>
9 <p>The most common implementation usually looks like this:</p>
9 <p>The most common implementation usually looks like this:</p>
10 <ul><li><p>add extra wrappers and pseudo-elements;</p>
10 <ul><li><p>add extra wrappers and pseudo-elements;</p>
11 </li>
11 </li>
12 <li><p>absolutely position all the pieces;</p>
12 <li><p>absolutely position all the pieces;</p>
13 </li>
13 </li>
14 <li><p>tweak radii, sizes, and offsets.</p>
14 <li><p>tweak radii, sizes, and offsets.</p>
15 </li>
15 </li>
16 </ul><p>In practice, this gives you tons of code per corner and a fragile solution that’s painful to maintain.</p>
16 </ul><p>In practice, this gives you tons of code per corner and a fragile solution that’s painful to maintain.</p>
17 <p>Below is a screenshot from a popular video about concave corner implementations. What you see here is roughly one fifth of the total implementation code.</p>
17 <p>Below is a screenshot from a popular video about concave corner implementations. What you see here is roughly one fifth of the total implementation code.</p>
18 <p>Example implementation by Kevin Powell</p>
18 <p>Example implementation by Kevin Powell</p>
19 <h3>Background issues</h3>
19 <h3>Background issues</h3>
20 <p>Typical solutions work<em>only</em>on solid backgrounds. As soon as the background becomes non-uniform, you have to switch to SVG. And CSS pain turns into SVG pain.</p>
20 <p>Typical solutions work<em>only</em>on solid backgrounds. As soon as the background becomes non-uniform, you have to switch to SVG. And CSS pain turns into SVG pain.</p>
21 <h3>The dream solution for concave corners</h3>
21 <h3>The dream solution for concave corners</h3>
22 <p>What does a developer dream of when facing a concave corner?</p>
22 <p>What does a developer dream of when facing a concave corner?</p>
23 <ul><li><p>a simple pure-CSS solution;</p>
23 <ul><li><p>a simple pure-CSS solution;</p>
24 </li>
24 </li>
25 <li><p>working out of the box, including on complex backgrounds;</p>
25 <li><p>working out of the box, including on complex backgrounds;</p>
26 </li>
26 </li>
27 <li><p>native customization of corner size and shape.</p>
27 <li><p>native customization of corner size and shape.</p>
28 </li>
28 </li>
29 </ul><p>Good news: such a solution exists. It’s called nebo.css, and that’s what we’ll explore in this article. The name comes from the pattern itself -<strong>ne</strong>gative<strong>bo</strong>rder radius.</p>
29 </ul><p>Good news: such a solution exists. It’s called nebo.css, and that’s what we’ll explore in this article. The name comes from the pattern itself -<strong>ne</strong>gative<strong>bo</strong>rder radius.</p>
30 <h2>Installing nebo.css and creating your first concave corner</h2>
30 <h2>Installing nebo.css and creating your first concave corner</h2>
31 <p>nebo.css is a single CSS file, very much in the spirit of normalize.css: include it - and start using it. There’s no JavaScript inside, everything is done with pure CSS.</p>
31 <p>nebo.css is a single CSS file, very much in the spirit of normalize.css: include it - and start using it. There’s no JavaScript inside, everything is done with pure CSS.</p>
32 <h3>Include the library</h3>
32 <h3>Include the library</h3>
33 &lt;link rel="stylesheet" href="nebo.css"&gt;<p>That’s all you need to get started.</p>
33 &lt;link rel="stylesheet" href="nebo.css"&gt;<p>That’s all you need to get started.</p>
34 <h3>Add a single corner to a card</h3>
34 <h3>Add a single corner to a card</h3>
35 <p>To add an inverted corner, the element needs the base class nebo. By default, the corner appears in the bottom-right.</p>
35 <p>To add an inverted corner, the element needs the base class nebo. By default, the corner appears in the bottom-right.</p>
36 &lt;article class="card nebo"&gt;...&lt;/article&gt;<p>Result:</p>
36 &lt;article class="card nebo"&gt;...&lt;/article&gt;<p>Result:</p>
37 <p>Concave corner with default parameters</p>
37 <p>Concave corner with default parameters</p>
38 <h3>Changing the corner position</h3>
38 <h3>Changing the corner position</h3>
39 <p>The position is controlled by modifiers:</p>
39 <p>The position is controlled by modifiers:</p>
40 <ul><li><p>nebo--tr - top right,</p>
40 <ul><li><p>nebo--tr - top right,</p>
41 </li>
41 </li>
42 <li><p>nebo--tl - top left,</p>
42 <li><p>nebo--tl - top left,</p>
43 </li>
43 </li>
44 <li><p>nebo--bl - bottom left,</p>
44 <li><p>nebo--bl - bottom left,</p>
45 </li>
45 </li>
46 <li><p>nebo--br - bottom right.</p>
46 <li><p>nebo--br - bottom right.</p>
47 </li>
47 </li>
48 </ul>&lt;article class="card nebo nebo--tr"&gt;...&lt;/article&gt;<p>Result:</p>
48 </ul>&lt;article class="card nebo nebo--tr"&gt;...&lt;/article&gt;<p>Result:</p>
49 <p>Concave corners on different sides over a complex background</p>
49 <p>Concave corners on different sides over a complex background</p>
50 <p>At this point, an important thing becomes obvious: the corner works correctly<strong>even on non-uniform backgrounds</strong>, with no extra tricks.</p>
50 <p>At this point, an important thing becomes obvious: the corner works correctly<strong>even on non-uniform backgrounds</strong>, with no extra tricks.</p>
51 <h2>Basic customization</h2>
51 <h2>Basic customization</h2>
52 <p>All nebo.css parameters are controlled via native CSS variables that follow the standard cascade.</p>
52 <p>All nebo.css parameters are controlled via native CSS variables that follow the standard cascade.</p>
53 <h3>Base radius: --nb-r</h3>
53 <h3>Base radius: --nb-r</h3>
54 <p>This variable controls the corner radii. The default value is 20px. Let’s make it a bit smaller.</p>
54 <p>This variable controls the corner radii. The default value is 20px. Let’s make it a bit smaller.</p>
55 .nebo { --nb-r: 16px; }<h3>Cutout width and height: --nb-w and --nb-h</h3>
55 .nebo { --nb-r: 16px; }<h3>Cutout width and height: --nb-w and --nb-h</h3>
56 <p>These variables define additional offsets between the inner and outer radii.</p>
56 <p>These variables define additional offsets between the inner and outer radii.</p>
57 .card--cat1 { --nb-w: 42px; --nb-h: 16px; }<p>Result:</p>
57 .card--cat1 { --nb-w: 42px; --nb-h: 16px; }<p>Result:</p>
58 <p>Reduced radii with additional corner width and height</p>
58 <p>Reduced radii with additional corner width and height</p>
59 <h3>Different settings for different elements</h3>
59 <h3>Different settings for different elements</h3>
60 <p>Since this is just the regular CSS cascade, each element can be customized independently:</p>
60 <p>Since this is just the regular CSS cascade, each element can be customized independently:</p>
61 .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; }<p>Result:</p>
61 .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; }<p>Result:</p>
62 <p>Different corner parameters for different cards</p>
62 <p>Different corner parameters for different cards</p>
63 <p>By the way, corners render correctly even with zero values (0px) - just don’t forget the units.</p>
63 <p>By the way, corners render correctly even with zero values (0px) - just don’t forget the units.</p>
64 <h2>Multiple concave corners on a single element</h2>
64 <h2>Multiple concave corners on a single element</h2>
65 <p>nebo.css is intentionally designed around a simple rule: one element - one corner. But if you need more, there’s a proven technique - extra wrappers.</p>
65 <p>nebo.css is intentionally designed around a simple rule: one element - one corner. But if you need more, there’s a proven technique - extra wrappers.</p>
66 <h3>Using wrappers</h3>
66 <h3>Using wrappers</h3>
67 &lt;div class="card-wrapper nebo nebo--bl"&gt; &lt;article class="card nebo nebo--tr"&gt;...&lt;/article&gt; &lt;/div&gt;<p>Result:</p>
67 &lt;div class="card-wrapper nebo nebo--bl"&gt; &lt;article class="card nebo nebo--tr"&gt;...&lt;/article&gt; &lt;/div&gt;<p>Result:</p>
68 <p>Card with two concave corners</p>
68 <p>Card with two concave corners</p>
69 <p>Each level can be customized independently using CSS variables. You can build three or even four corners - just by stacking wrappers.</p>
69 <p>Each level can be customized independently using CSS variables. You can build three or even four corners - just by stacking wrappers.</p>
70 <h2>Advanced customization</h2>
70 <h2>Advanced customization</h2>
71 <p>The base radius defined by --nb-r can be overridden separately for each of the three curves: two outer corners and the inner curve. Both horizontal and vertical radii can be set independently using six additional CSS variables:</p>
71 <p>The base radius defined by --nb-r can be overridden separately for each of the three curves: two outer corners and the inner curve. Both horizontal and vertical radii can be set independently using six additional CSS variables:</p>
72 --nb-cor1-rw, --nb-cor1-rh, --nb-cor2-rw, --nb-cor2-rh, --nb-curve-rw, --nb-curve-rh,<p>These variables allow you to create concave corners with very complex shapes. For example:</p>
72 --nb-cor1-rw, --nb-cor1-rh, --nb-cor2-rw, --nb-cor2-rh, --nb-curve-rw, --nb-curve-rh,<p>These variables allow you to create concave corners with very complex shapes. For example:</p>
73 .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%; }<p>Result:</p>
73 .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%; }<p>Result:</p>
74 <p>Concave corner with different corner radii</p>
74 <p>Concave corner with different corner radii</p>
75 <h2>Case study: a two-block card</h2>
75 <h2>Case study: a two-block card</h2>
76 <p>Now let’s look at a more realistic example - a card consisting of a top image block and a bottom content block.</p>
76 <p>Now let’s look at a more realistic example - a card consisting of a top image block and a bottom content block.</p>
77 <h3>Markup</h3>
77 <h3>Markup</h3>
78 &lt;div class="card"&gt; &lt;div class="card-header"&gt;&lt;/div&gt; &lt;div class="card-body"&gt; &lt;div class="price"&gt;$114,000&lt;/div&gt; ... &lt;/div&gt; &lt;/div&gt;<h3>Adding concave corners</h3>
78 &lt;div class="card"&gt; &lt;div class="card-header"&gt;&lt;/div&gt; &lt;div class="card-body"&gt; &lt;div class="price"&gt;$114,000&lt;/div&gt; ... &lt;/div&gt; &lt;/div&gt;<h3>Adding concave corners</h3>
79 &lt;div class="card-header nebo nebo--bl"&gt;&lt;/div&gt; &lt;div class="card-body nebo nebo--tr"&gt;...&lt;/div&gt;<h3>Tuning parameters and merging blocks</h3>
79 &lt;div class="card-header nebo nebo--bl"&gt;&lt;/div&gt; &lt;div class="card-body nebo nebo--tr"&gt;...&lt;/div&gt;<h3>Tuning parameters and merging blocks</h3>
80 <p>This example uses a quick and dirty trick with negative margins to pull the image block closer to the content block. The goal here was to demonstrate how easy it is to create the cutouts.</p>
80 <p>This example uses a quick and dirty trick with negative margins to pull the image block closer to the content block. The goal here was to demonstrate how easy it is to create the cutouts.</p>
81 .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; }<p>Result:</p>
81 .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; }<p>Result:</p>
82 <p>Complex two-block card with concave corners</p>
82 <p>Complex two-block card with concave corners</p>
83 <p>Notice that everything works perfectly on non-uniform backgrounds.</p>
83 <p>Notice that everything works perfectly on non-uniform backgrounds.</p>
84 <h2>Where to get nebo.css</h2>
84 <h2>Where to get nebo.css</h2>
85 <p>nebo.css is a free open-source library developed by the author. It’s hosted on GitHub.</p>
85 <p>nebo.css is a free open-source library developed by the author. It’s hosted on GitHub.</p>
86 <p>Repository:<a>https://github.com/htmlacademy/nebo.css</a></p>
86 <p>Repository:<a>https://github.com/htmlacademy/nebo.css</a></p>
87 <p>You can include the library as a separate file or copy the code directly into your styles. Use it - and enjoy!</p>
87 <p>You can include the library as a separate file or copy the code directly into your styles. Use it - and enjoy!</p>
88 <p>The complete code lives in an <a>interactive, step-by-step demo</a> - you can go through the examples at your own pace and play with the parameters along the way.</p>
88 <p>The complete code lives in an <a>interactive, step-by-step demo</a> - you can go through the examples at your own pace and play with the parameters along the way.</p>