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

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);
}

Solution 1:

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>

Solution 2:

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.

Solution 3:

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;
}

Solution 4:

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