How to make round corners to both inside of a box and its border?
Asked Answered
U

13

68

I guess the title is kind of hard to understand, so I'll explain. I am trying to achieve this effect:

enter image description here

(a box which has rounded corners and its border, which also has rounded borders).

I've managed to do this, by using the background-clip property:

enter image description here

(rounded corners for border but not for inner box)

The question is, how can I achieve rounded corners for the inner box?

Thank you!

EDIT

The HTML I am using:

<header class="body template-bg template-border radius-all">
        <nav>
            <ul>
                <li><a href="#">Link 1</a></li>
                <li><a href="#">Link 2</a></li>
                <li><a href="#">Link 3</a></li>
                <li><a href="#">Link 4</a></li>
            </ul>
        </nav>
    </header>

And the CSS:

.radius-all {
  border-radius: 10px;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

.template-bg {
  background: #FFF;
  -moz-background-clip: padding;
  -webkit-background-clip: padding;
  background-clip: padding-box;
}

.template-border {
  border: 5px solid rgba(255, 255, 255, 0.2);
}
Upbraiding answered 29/1, 2011 at 21:40 Comment(0)
J
64

Inner border calculations

First, you'll need to remove -vendor-background-clip: padding-box or set them to border-box the default in order to achieve the inner border radius.

The inner border radius is calculated as the difference of the outer border radius (border-radius) and the border width (border-width) such that

inner border radius = outer border radius - border width

Whenever the border-width is greater than the border-radius, the inner border radius is negative and you get some awkward inverted corners. Currently, I don't believe there is a property for adjusting the inner-border-radius, so you'll need to calculate it manually.

In your case:

inner border radius = 6px - 5px = 1px

Your new CSS should be:

.radius-all { border-radius: 6px; -moz-border-radius: 6px; -webkit-border-radius: 6px; }
.template-bg { background: #FFF; }
.template-border { border: 5px solid rgba(255, 255, 255, 0.2); }

Simply subtract the border-radius (6px) values from the border-width value (5px) in order to achieve your desired inner-border-radius:


Code that works for me

Tested on Firefox 3.x, Google Chrome, and Safari 5.0

 .radius-all { border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; }
.template-bg { background: #FFF; }
.template-border { border: 5px solid rgba(0, 0, 0, 0.2); } /* Note that white on white does not distinguish a border */

Adding color overlays in JavaScript

<script type="text/javascript">
    var bodyBgColor = document.getElementsByTagName('body')[0].style.backgroundColor;;

    // insert opacity decreasing code here for hexadecimal

    var header = document.getElementsByTagName('header')[0];
    header.style.backgroundColor = bodyBgColor;
</script>

I'm not entirely sure how to do hexadecimal arithmetic in JavaScript but I'm sure you can find an algorithm in Google.


Applying General Borders

Are you using a separate box <div> for your border through its background property? If so, you'll need to apply border-radius and its vendor specific properties on both the border box and the inner box:

<div id="border-box" style="border-radius: 5px;">
    <div id="inner-box" style="border-radius: 5px;">
    </div>
</div>

A much more efficient way would simply have the inner-box manage its own border:

<div id="inner-box" style="border: 4px solid blue; border-radius: 5px">
    <!-- Content -->
</div>

CSS-wise, you could just declare a .rounded-border class and apply it to every box that will have rounded borders:

.rounded-borders {
    border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    -khtml-border-radius: 5px;
}

And apply the class to any boxes that will have rounded borders:

<div id="border-box" class="rounded-borders">
    <div id="inner-box" class="rounded-borders">
    </div>
</div>

For a single box element, you'll still be required to declare the border size in order to be shown:

<style type="text/css">
    #inner-box { border: 4px solid blue; }
</style>

<div id="inner-box" class="rounded-borders">
</div>
Janusfaced answered 29/1, 2011 at 22:0 Comment(9)
Thank you for your answer. My current solution is very similar to the one you are suggesting, and it doesn't work. What else should I try?Upbraiding
@linkyndy, Thanks for posting the markup, the issue you're face with is inner border calculations.Janusfaced
Thank you very much for clarifying. I manage to achieve the 'inner' border, but only for Firefox and IE9. In Chrome, Safari and Opera, the 'inner' box remains without any rounded corner, and the sharp edges nearly get through the border's rounded corners. Is there a 'remedy' for this? It's worth mentioning that I need the background-clip because my border is the same colour as the background, only has the opacity lower. If I set background clip to border-box, then the border wouldn't be seen anymore. Thank you again.Upbraiding
Tested on Chromium and Safari, they work fine, did you change the -webkit-border-radius and remove the -webkit-background-clip? I don't think outer border radii, inner border radii, and color can be controlled simultaneously without some hacking (or JavaScript). I would go with Shaun McCran's answer and simply use two <div> elements if you really want overlapping colors with flexibility over the border-radii otherwise JavaScript would need to be involved to control complex behavior. You could always pressure w3 and CSS working groups to add more CSS properties.Janusfaced
I changed the radius and, as I said, I need the background clip. So, you say there isn't any other method besides two <div>s? I would have wanted something cleaner. But I wonder why FF and IE display correctly these borders and the other three don't...What are those hacks, anyway? Maybe they'll work in my situation.Upbraiding
If you post the updated code, I might be able to see where it's gone wrong. Also, I added a sample JavaScript snippet to retrieve the body's background-color and set the header's background-color to the same; you'll just need to figure out how to add and subtract arithmetic in JavaScript since it's loosely typed.Janusfaced
The only thing I have changed is the border radius, from 6px to 10px, so that the inner corner would be visible.Upbraiding
I'm using Google Chrome 8.0.552.237 and Safari 5.0 (7533.86). I wouldn't know if it worked on Opera since I never use or test it but I know -o- is the vendor prefix for their CSS3 properties. Also, I added the code that works for me.Janusfaced
Thank you for your answer. Unfortunately, I need a transparent white border around a full white background. That's why I've messed up with background clipping and so. It distinguishes now, but in Saf, Chr and Opr, the inner corner remains 'unrounded'. In FF and IE, the inner corner displays well.Upbraiding
G
24

Another solution is to have matching inner and outer borders combined with border-radius is to "fake" the border using the <spread-radius> value of the box-shadow property. This produces a solid shadow which can easily pass for a regular border.

For instance, to achieve a 4px border and a 4px white border radius, try this:

/* rounded corners */
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;

/* drop shadow */
-webkit-box-shadow: 0px 0px 0px 4px #fff;
-moz-box-shadow: 0px 0px 0px 4px #fff;
box-shadow: 0px 0px 0px 4px #fff;

If you want to add a "real" drop shadow to the entire container, you can simply chain your shadow statements like so:

/* drop shadow */
-webkit-box-shadow: 0px 0px 0px 4px rgba(255,255,255,1.0),
        1px 1px 8px 0 rgba(0,0,0,0.4);
-moz-box-shadow: 0px 0px 0px 4px rgba(255,255,255,1.0),
        1px 1px 8px 0 rgba(0,0,0,0.4);
box-shadow: 0px 0px 0px 4px rgba(255,255,255,1.0),
        1px 1px 8px 0 rgba(0,0,0,0.4);

Note: Keep in mind here that the order of the statements is the order in which it will be rendered.

The only thing to beware of is that the initial "faux border" will overlap the first X pixels (where X is the width of the border) of any shadow you want beneath it (and combine, if you're using RGBa opacity on it below 100%.)

So it won't work in all situations, but it'll get the majority. I use this pretty frequently when regular borders are not ideal.

Gorilla answered 28/12, 2011 at 23:27 Comment(0)
P
19

Since there is no such thing as inner-border-radius for CSS, the browsers default it to border-radius - border-width. If you don't like that, the typical solution is to create two divs with borders to mimic the inner border radius but this solution brings in more design into the html. It is also a pain if it's a common border template used through out the site.

I managed to figure a way to keep it all in css by producing the inner div using :after and content: "". So for your case it would be:

.template-border {
    position: relative;
    border-radius: 5px;
    background-color: #000;
    border: 10px solid #000;
    z-index: -2;
}

.template-border:after {
    content: "";
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 5px;
    background-color: #FFF;
    z-index: -1;
}
Perionychium answered 27/2, 2013 at 23:31 Comment(2)
FANTASTIC ANSWER from grandmaster leo wuHinch
very good answer. But I had to add "top: 0; left: 0;" to the :after. Also with modern CSS you can use variables like --arb-border-color to make it customizable for any kind of border.Cayla
S
8

Most of the solutions on this page are from the web stone ages (before 2013 - i.e. even before IE11).

Since IE11, the way to do this is easy...

Just in case someone is Googling for this answer after 2013 (it's almost 2020 today) and got sent here, here is the most simple, compatible, and easy way to do this, even if you need to support IE11...

(Feel free to change the px values for the look you want, or better yet, use variables and transpile with Stylus or SASS)

Example HTML...

<div class="wrapper">
    <div class="content">
        your content goes here
    </div>
</div>

Example CSS...

.wrapper {
    border-radius: 25px;
    border: solid 25px blue;
    background-color: blue;
}
.content {
    border-radius: 10px;
    background-color: white;
}

...Presto. enter image description here

Starfish answered 18/3, 2019 at 1:52 Comment(2)
Your "newer" solution just copies the same solution as the "stone age" ones you diss on: use two divs and set border radii for them. If you're going to post an answer to a question that already has multiple answers, it's important to make sure your answer is actually new first. Especially if you're going to then diss those older answers.Granular
Thank you for the comment. I was only saying supporting E10 is impracticle as of 2019 (i.e. it's the "stone age" for web as nobody used it), and if you drop it, the method I posted was the most compatible and easy to understand otherwise as of 2019 when I made the post. Also, 2 wraps and 5 CSS elements was the simplest and most compatible solution across browsers at the time. My comment meant no disrespect and I appologize if I offended you. This was not my intention in any way.Starfish
L
4

The problem is not the coding of the CSS but the mathematics of a circle. Essentially your border-inner-radius (I know this property does not exist) is equal to the border-radius - border-width.

Quite simply work out what you want your inner radius to be and then add the width of the border to achieve the desired effect.

border-inner-radius + border-width = border-radius

Lashelllasher answered 19/1, 2017 at 1:10 Comment(0)
F
3

You need to have two div elements, one inside the other, and use a cross browser rounded corner css, like this:

.small-rounded {
    border: 1px solid ##000;
    -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px;
    -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px;
    -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px;
    -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px;
    border-radius: 5px;
}
Flagstaff answered 29/1, 2011 at 21:55 Comment(3)
Thank you for your answer, but I would like to achieve this by using a single element.Upbraiding
For the record, -moz-border-radius and -webkit-border-radius work.Deroo
They both work now, it was only a problem of proportion between the border's width and radius.Upbraiding
V
2

Based on Leo Wu's idea, here it is my solution:

.my-div
{
  background-color: white;
  border: solid 20px black;
  border-radius: 10px;
  box-shadow: 0 0 10px black;
  height: 100px;
  left: 30px;
  position: relative;
  top: 20px;
  width: 200px;
}
.my-div:before
{
  background-color: white;
  border-radius: 5px;
  content: "";
  display: block;
  height: calc(100% + 20px);
  left: -10px;
  position: absolute;
  top: -10px;
  width: calc(100% + 20px);
  z-index: 1;
}
.some-content
{
  height: calc(100% + 20px);
  left: -10px;
  position: relative;
  top: -10px;
  width: calc(100% + 20px);
  z-index: 3;
}
.some-header
{
  background-color: green;
  border-radius: 5px 5px 0 0;
  height: 30px;
}
<html>
	<body>
		<div class="my-div">
			<div class="some-content">
				<div class="some-header">my title</div>
				<div>some other content</div>
			</div>
		</div>
	</body>
</html>
Vigesimal answered 26/1, 2017 at 20:19 Comment(0)
P
2

Another idea is to consider multiple radial-gradient to simulate the inner radius and you can control the outer and inner radius like you want without the need of any extra element:

.box {
  width:150px;
  height:150px;
  margin:10px;
  border:10px solid red;
  border-radius:10px; /* Outer Radius*/
  background:
   radial-gradient(farthest-side at bottom right,#0000 98%,red) top left,
   radial-gradient(farthest-side at top    right,#0000 98%,red) bottom left,
   radial-gradient(farthest-side at bottom left ,#0000 98%,red) top right,
   radial-gradient(farthest-side at top    left ,#0000 98%,red) bottom right,
   blue;
  background-size:25px 25px; /* inner Radius*/
  background-repeat:no-repeat;
  background-origin:padding-box;
}
<div class="box">

</div>

You can also have different values for each side:

.box {
  width:150px;
  height:150px;
  margin:10px;
  border:10px solid red;
  border-radius:10px; /* Outer Radius*/
  background:
   radial-gradient(farthest-side at bottom right,#0000 98%,red) top left / 30px 30px,
   radial-gradient(farthest-side at top    right,#0000 98%,red) bottom left / 20px 20px,
   radial-gradient(farthest-side at bottom left ,#0000 98%,red) top right / 50px 50px,
   radial-gradient(farthest-side at top    left ,#0000 98%,red) bottom right/ 10px 10px,
   blue;
  background-repeat:no-repeat;
  background-origin:padding-box;
}
<div class="box">

</div>
Piperonal answered 18/12, 2019 at 0:19 Comment(0)
A
1

Today I run into this "problem". My solution uses two divs and overlaps the inner div on the outer one.

A good thing about my solution is that it does not alter the background color (it can be transparent).

You can control the outer border radius by modifying the outer-border class and the inner border with the inner-border class.

.outer-border {
  border: 10px solid #20b2aa;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
  min-height: 100px;
}

.inner-border, .inner-border-evidence {
  flex: 1;
  border: 10px solid #20b2aa;
  border-radius: 30px;
  margin: -9px;
}

.inner-border-evidence {
  border-color: #0a3b8a;
}
<div class="outer-border">
  <div class="inner-border">
  </div>
</div>
<br />
<p>Here you can see how the inner div overlaps the outer div.</p>
<div class="outer-border">
  <div class="inner-border-evidence">
  </div>
</div>
Adriatic answered 20/6, 2021 at 3:10 Comment(0)
T
0

You need to make the border-radius to a value greater than the border-width until you start to see a curve. It's not a set formula to set the border-radius of +1px greater than border-width. However, it's going to be a positive value, definitely. You need to experiment in the different browsers where you need this until you see the smallest possible border-radius value that works good enough for you in most browsers. (Some browsers may not support this.) For instance, in Google Chrome, I set a border-width to 10px, but had to set the border-radius to 13px before I started to see a semblance of an inner border curve, while 15px worked even better.

Tremann answered 31/12, 2015 at 8:1 Comment(0)
O
0

The best and fastest way is to do this

.curve {
  width : 300px;
  height: 100px;
  border: 4px solid black;
  border-bottom-left-radius: 20px;
  border-bottom-right-radius: 20px;
  border-top-right-radius: 20px;
  border-top-left-radius: 20px;
} 
<div class='curve'></div>
Oliy answered 27/2, 2020 at 18:34 Comment(0)
J
0

If you can't add an extra div you can accomplish this with a background images in each corner.

inner-radius

#nice-corners {
  border: 5px solid green;
  border-radius: 5px;
  background-image: url(top-left.svg), url(top-right.svg), url(bottom-left.svg), url(bottom-right.svg);
  background-position: left top, right top, left bottom, right bottom;
  background-repeat: no-repeat;
  background-size: 16px
}
Juvenescent answered 3/2, 2023 at 9:52 Comment(0)
C
0

I read through some of the answers and they seemed unnecessarily complicated.

You want an inner-border-radius? Make one.

#container {
    --inner-border-radius: 24px;
    --border: 64px solid black;
    border: var(--border);
    position: relative;

    &::before {
        ...

        border-radius: var(--inner-border-radius);
        outline: var(--border);
    }
}

Working example:

#container {
    --inner-border-radius: 8px;
    --border: 8px solid black;
    border: var(--border);
    position: relative;

    &::before {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        content: '';
        border-radius: var(--inner-border-radius);
        outline: var(--border);
        z-index: 999;
    }

    border-radius: 16px;
    background: coral;
    height: 75vh;
    width: 75vw;
    margin: 0 auto;
}

html, body { min-height: 100%; align-content: center; }
<div id="container"></div>
Cyndy answered 7/6 at 10:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.