Is a concave border radius possible?
Asked Answered
C

7

22

Here is a simple convex example.

http://jsfiddle.net/swY5k/

#test{
    width: 200px;
    height: 200px;
    background: #888888;
    border-radius: 50px;
}

However, I want a concave border radius.

I tried making the border-radius negative but this did not work.

Example of concave/convex:

enter image description here

Cooperage answered 5/5, 2013 at 19:5 Comment(1)
found it ... jsfiddle.net/cogent/6A5Lb ... uses convex to create concave.Cooperage
M
19

You can give the impression of a concave border using radial gradients on the background. For example, something like this:

#test {
    width: 200px;
    height: 200px;
    background: #888888;
    background:
      radial-gradient(circle 20px at -20% 50%,transparent,transparent 100px,#888888 100px),
      radial-gradient(circle 20px at 120% 50%,transparent,transparent 100px,#888888 100px);
    background-size:100px 200px, 100px 200px;
    background-position:0 0,100% 0;
    background-repeat:no-repeat;
}

Note that most webkit browsers still require prefixes for radial-gradients, and if you want to fully support older browsers you may need to implement the older gradient syntax too.

Milliard answered 5/5, 2013 at 19:18 Comment(1)
Can't see any border at all when I chuck that into JSFiddle. Am I missing something? (Edit Ah I do in IE. Guess it needs some vendor prefixes on Chrome)Chisolm
F
16

With clever use of the :before and :after pseudo classes, we can simulate a concave form:

    #test{
        width: 100px;
        height: 300px;
        background: green;
        position: relative;
        display: inline-block;
    }
    
    #test:before{
        background: white;
        height: 300px;
        width: 30px;
        border-radius: 0 60px 60px 0 / 0 300px 300px 0;
        display: inline-block;
        content: '';
    }
    
    #test:after{
        background: white;
        height: 300px;
        width: 30px;
        border-radius: 60px 0 0 60px / 300px 0 0  300px;
        display: inline-block;
        content: '';
        position: relative;
        left: 40px;
    }
<div id="test"></div>

The #test div is a plain rectangle. But its :before and :after elements are half-side concave filled with the background color (white in this case).

See this jsfiddle.

Frants answered 5/5, 2013 at 20:26 Comment(3)
+1, I read the question, thought 'hey this would be a funny use case to fake it with :before and :after', then saw your answer hehe - nice one.Wedekind
Thanks :) BTW: This should even work with Chrome/Firefox 4.0+, IE 9+, Opera 10.5 (even though I haven't tested them all).Frants
The downside to this method is that the underlying background won't show through the concave areas. You couldn't use something like this on top of an image for example.Milliard
G
7

SVG is the recommended way to create such shapes. It offers simplicity and scale-ability.

We can use SVG's path element to create a shape like above and fill it with some solid color, gradient or a pattern.

Only one attribute d is used to define shapes in path element. This attribute itself contains a number of short commands and few parameters that are necessary for those commands to work.

Following code will create a convex shape:

<path d="M 150,25
         Q 115,100 150,175
         Q 185,100 150,25" />

And following one will create a concave shape:

<path d="M 30,25
         L 80,25
         Q 50,100 80,175
         L 30,175
         Q 60,100 30,25" />

Below is a brief description of path commands used in above code:

  • M command is used to define the starting point. It appears at the beginning and specify the point from where drawing should start.
  • L command is used to draw straight lines.
  • Q command is used to draw curves.

Output Image:

Convex and concave shapes

Working Demo:

<svg width="300" height="200" viewBox="0 0 300 200">
  <defs>
    <linearGradient id="grad">
      <stop offset="0" stop-color="#ddd" />
      <stop offset=".5" stop-color="#fff" />
      <stop offset="1" stop-color="#ddd" />
    </linearGradient>
  </defs>
  <g stroke-width="1" stroke="#000" fill="url(#grad)">
    <path d="M30,25 L80,25 Q50,100 80,175 L30,175 Q60,100 30,25" />
    <path d="M150,25 Q115,100 150,175 Q185,100 150,25" />
  </g>
</svg>

Useful Resources:

Below are some useful links for SVG:

Grani answered 29/8, 2017 at 10:11 Comment(0)
T
3

To generate the shape, you can use pseudo elements

div {
  height: 250px;
  width: 100px;
  background: tomato;
  position: relative;
  margin:0 auto;
}
div:before {
  content: "";
  height: 100%;
  width: 50%;
  position: absolute;
  background: white;
  border-radius: 50%;
  left: -25%;
  transition: all 0.8s;
}
div:after {
  content: "";
  height: 100%;
  width: 50%;
  position: absolute;
  background: white;
  border-radius: 50%;
  right: -25%;
  transition: all 0.8s;
}
div:hover:before,
div:hover:after {
  background: blue;
}
hover the shape to see how it works:
<div></div>
Targe answered 10/2, 2015 at 16:39 Comment(1)
Haha, was thinking of posting the exact same and then saw your answer :)Economical
S
2

I suggest using border-image, with a scalable SVG image in the border.

That way you can have any shape you want in the border; no need to be restricted to the shapes offered by border-radius, and no need to do any clever hacks or extra markup either.

The down-side is that neither border-image nor SVG is supported in older browsers (ie old IE versions). But of course, border-radius isn't either, so you don't lose much with this technique, compared with the flexibility you gain.

Synclastic answered 5/5, 2013 at 20:31 Comment(0)
N
2

There are several ways to make the concave border. And I prefer to use the radial gradients on the background. https://jsfiddle.net/black_horse/qygmb8z9/

Sample Picture

.single-border{
  height: 50px;
  padding: 20px;
  background:-moz-radial-gradient(0 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -moz-radial-gradient(100% 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -moz-radial-gradient(100% 0, circle, rgba(204,0,0,0) 20px, #c00 21px), -moz-radial-gradient(0 0, circle, rgba(204,0,0,0) 20px, #c00 21px);
    background:-o-radial-gradient(0 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -o-radial-gradient(100% 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -o-radial-gradient(100% 0, circle, rgba(204,0,0,0) 14px, #c00 21px), -o-radial-gradient(0 0, circle, rgba(204,0,0,0) 20px, #c00 21px);
    background:-webkit-radial-gradient(0 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -webkit-radial-gradient(100% 100%, circle, rgba(204,0,0,0) 20px, #c00 21px), -webkit-radial-gradient(100% 0, circle, rgba(204,0,0,0) 20px, #c00 21px), -webkit-radial-gradient(0 0, circle, rgba(204,0,0,0) 20px, #c00 21px);
    background-position:left bottom, right bottom, right top, left top;
    -moz-background-size:51% 51%;
    -webkit-background-size:51% 51%;
    background-size:51% 51%;
    background-repeat:no-repeat;
}


<div class="single-border">
  Single border
</div>
Nosegay answered 21/6, 2019 at 18:33 Comment(0)
M
0

Welcome to 2024. Simply achievable using two radial gradients in a single element. No pseudo elements, nor additional, nor shenanigans.

image of the snippet rendered below

div {
  --size: 50px;
  --concave-size: calc(var(--size) / 2);
  
  height: var(--size);
  width: var(--size); /* can be whatever */
  background-color: red;
  background-image:
      radial-gradient(circle at left, yellow, yellow var(--concave-size), transparent var(--concave-size)),
      radial-gradient(circle at right, blue, blue var(--concave-size), transparent var(--concave-size));
}
<div></div>
Minica answered 25/6 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.