688

I have a <div> element and I want to put a border on it. I know I can write style="border: 1px solid black", but this adds 2px to either side of the div, which is not what I want.

I would rather have this border be -1px from the edge of the div. The div itself is 100px x 100px, and if I add a border, then I have to do some mathematics to make the border appear.

Is there any way that I can make the border appear, and ensure the box will still be 100px (including the border)?

alex
  • 6,818
  • 9
  • 52
  • 103
TheMonkeyMan
  • 8,622
  • 8
  • 27
  • 42
  • 3
    For what it's worth, I posted [a solution](https://stackoverflow.com/a/50347410/703717) for those who came here searching for an inner border with an **offset** – Danield May 15 '18 at 10:24

15 Answers15

807

Set box-sizing property to border-box:

div {
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    width: 100px;
    height: 100px;
    border: 20px solid #f00;
    background: #00f;
    margin: 10px;
}

div + div {
    border: 10px solid red;
}
<div>Hello!</div>
<div>Hello!</div>

It works on IE8 & above.

Gajus
  • 69,002
  • 70
  • 275
  • 438
sandeep
  • 91,313
  • 23
  • 137
  • 155
  • 16
    +1. For a little more background: http://css-tricks.com/box-sizing/ or http://paulirish.com/2012/box-sizing-border-box-ftw/ – isotrope Mar 07 '12 at 14:00
  • 196
    Only flaw with this one is you MUST declare a height for it to work, without a height the border still sits outside the block and affects rendering (i.e. vertical rhythm). – jerclarke Oct 21 '13 at 13:30
  • 1
    this does not allow styling individual border-sides, and in this case many would probably be happy with using the property `outline`. – Lorenz Lo Sauer Aug 10 '15 at 08:05
  • 5
    jeremyclarke note should be in the answer, as the code don't work without setting height. side-note: max-height works as well, as long as the div IS USING that property, if the height don't reach the max-height it won't work. –  Sep 03 '15 at 06:41
  • 11
    Vendor prefixes can be dropped for `box-sizing` http://caniuse.com/#search=box-sizing – myconode Sep 09 '15 at 02:45
  • Perfect answer. We had to use this "borders" trick to get around CSS background-colors working fine on a webpage, but being ignored when printing. – Mike Gledhill Apr 14 '16 at 07:47
  • but there is 1 pixel extra space in old safari 5.1.7 :'( – Mahdi Jazini Nov 20 '16 at 07:25
  • I have found the problem: don't use width and height with this format: x.x% example: 91% OK 91.5% Not OK (actually all browsers support x.x but in old version of safari, not supported) – Mahdi Jazini Nov 20 '16 at 07:31
  • The proper way to use vendor-prefixed properties is to place them before the unprefixed property. We do this to take advantage of the cascading nature of CSS. Browsers will use the last declared version of a property they can understand. – Kunj Mar 03 '17 at 06:05
  • 6
    No that isn't the answer. You have to know the height. box-shadow solution is the correct way to do ths. – Craig Apr 24 '20 at 17:59
  • Nice solution! Unfortunately, if used on an img the img gets scaled down to fit inside the border. The border doesn't get just overimposed the img. :( – metawops May 21 '21 at 16:13
563

You can also use box-shadow like this:

div{
    -webkit-box-shadow:inset 0px 0px 0px 10px #f00;
    -moz-box-shadow:inset 0px 0px 0px 10px #f00;
    box-shadow:inset 0px 0px 0px 10px #f00;
}

Example here: http://jsfiddle.net/nVyXS/ (hover to view border)

This works in modern browsers only. For example: No IE 8 support. See caniuse.com (box-shadow feature) for more info.

mkelley33
  • 5,323
  • 10
  • 47
  • 71
caitriona
  • 8,569
  • 4
  • 32
  • 36
  • 51
    Love this solution because it keeps the border completely inside the box regardless of having a height set. Perfect if you want borders that have no effect at all outside the box. Here's the CSS for a top-border of 2px: "inset 0px 2px 0px 0px #DDD" – jerclarke Oct 21 '13 at 13:32
  • 17
    Preferred solution. Btw, all major browsers today support plain box-shadow without prefix. – Pointer Null Feb 16 '14 at 12:04
  • 4
    One downside is that some browsers fail to print box-shadow correctly and always print it as #000. This COULD be a show stopper if you need to be able to print the page. – Rob Fox Jul 31 '14 at 07:52
  • @caitriona great suggestion. I made a couple of minor edits to the answer to hopefully improve an already great answer :) I know you answered this forever ago, but some of us still have to code for IE 8 a little longer (hopefully a very short while longer) Thanks! – mkelley33 Aug 02 '14 at 22:41
  • Very good solution for this question. I tried it. But I want a dashed border inside. How can I get this? – Abhi Nov 26 '15 at 09:57
  • Awesome solution! Box visualization in development never was so easy like this, especially with the `:hover` tag! – Lanti Feb 08 '16 at 09:26
  • 3
    This doesn't work on an img element as box-shadow gets rendered under the image. – Organic Dec 12 '16 at 15:21
  • A downside is that (as far as I can see) one can not have this kind of border only on one side, i.e., there is no way to simulate `border-bottom` with this. – cgogolin Nov 05 '17 at 11:26
  • 1
    @cgogolin Nope, that's totes do-able! eg: box-shadow: 0 10px 0 0 #f00; – caitriona Nov 06 '17 at 09:33
  • 1
    I almost got so excited before I realised the same problem as @Organic, img element doesn't honour it – Moseleyi Dec 04 '17 at 05:14
  • Exelent solution it does not change anything in my table when I am doing tr:hover, but it even interacts well with the header, love it thanks! Some of the other solutions gave me seasickness with the small shifts in content. – Michael Larsson Nov 11 '20 at 09:21
  • See this answer to work around it not working for images https://stackoverflow.com/a/29794582/1508189 – antirealm Sep 03 '22 at 12:18
  • The one big downside of this solution (in some specific use-cases of course) is that inset box shadows are painted **"below content"** (according to MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow#values), so no matter even if you specify 0 transparency, e.g., using the RGBA color system, the color will still "appear to be transparent" as it remains below the text or any other content. – aderchox Sep 15 '22 at 05:53
135

Probably it is belated answer, but I want to share with my findings. I found 2 new approaches to this problem that I have not found here in the answers:

Inner border through box-shadow css property

Yes, box-shadow is used to add box-shadows to the elements. But you can specify inset shadow, that would look like a inner border rather like a shadow. You just need to set horizontal and vertical shadows to 0px, and the "spread" property of the box-shadow to the width of the border you want to have. So for the 'inner' border of 10px you would write the following:

div{
    width:100px;
    height:100px;
    background-color:yellow;
    box-shadow:0px 0px 0px 10px black inset;
    margin-bottom:20px;
}

Here is jsFiddle example that illustrates the difference between box-shadow border and 'normal' border. This way your border and the box width are of total 100px including the border.

More about box-shadow:here

Border through outline css property

Here is another approach, but this way the border would be outside of the box. Here is an example. As follows from the example, you can use css outline property, to set the border that does not affect the width and height of the element. This way, the border width is not added to the width of an element.

div{
   width:100px;
   height:100px;
   background-color:yellow;
   outline:10px solid black;
}

More about outline: here

Shukhrat Raimov
  • 2,137
  • 4
  • 21
  • 27
65

You can use the properties outline and outline-offset with a negative value instead of using a regular border, works for me:

div{
    height: 100px;
    width: 100px;
    background-color: grey;
    margin-bottom: 10px; 
}

div#border{
    border: 2px solid red;
}

div#outline{
    outline: 2px solid red;
    outline-offset: -2px;
}
Using a regular border.
<div id="border"></div>

Using outline and outline-offset.
<div id="outline"></div>
Kelvin
  • 320
  • 4
  • 19
Wilson Delgado
  • 651
  • 5
  • 2
63

Yahoo! This is really possible. I found it.

For Bottom Border:

div {box-shadow: 0px -3px 0px red inset; }

For Top Border:

div {box-shadow: 0px 3px 0px red inset; }
xBoss naYan
  • 631
  • 5
  • 2
48

Although this question has already been adequately answered with solutions using the box-shadow and outline properties, I would like to slightly expand on this for all those who have landed here (like myself) searching for a solution for an inner border with an offset

So let's say you have a black 100px x 100px div and you need to inset it with a white border - which has an inner offset of 5px (say) - this can still be done with the above properties.

box-shadow

The trick here is to know that multiple box-shadows are allowed, where the first shadow is on top and subsequent shadows have lower z-ordering.

With that knowledge, the box-shadow declaration will be:

box-shadow: inset 0 0 0 5px black, inset 0 0 0 10px white;

div {
  width: 100px;
  height: 100px;
  background: black;
  box-shadow: inset 0 0 0 5px black, inset 0 0 0 10px white; 
}
<div></div>

Basically, what that declaration is saying is: render the last (10px white) shadow first, then render the previous 5px black shadow above it.

outline with outline-offset

For the same effect as above the outline declarations would be:

outline: 5px solid white;
outline-offset: -10px;

div {
  width: 100px;
  height: 100px;
  background: black;
  outline: 5px solid white;
  outline-offset: -10px;
}
<div></div>

NB: outline-offset isn't supported by IE if that's important to you.


Codepen demo

Danield
  • 121,619
  • 37
  • 226
  • 255
38

Use pseudo element:

.button {
    background: #333;
    color: #fff;
    float: left;
    padding: 20px;
    margin: 20px;
    position: relative;
}

.button::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border: 5px solid #f00;
}
<div class='button'>Hello</div>

Using ::after you are styling the virtual last child of the selected element. content property creates an anonymous replaced element.

We are containing the pseudo element using absolute position relative to the parent. Then you have freedom to have whatever custom background and/or border in the background of your main element.

This approach does not affect placement of the contents of the main element, which is different from using box-sizing: border-box;.

Consider this example:

.parent {
    width: 200px;
}

.button {
    background: #333;
    color: #fff;
    padding: 20px;
    border: 5px solid #f00;
    border-left-width: 20px;
    box-sizing: border-box;
}
<div class='parent'>
    <div class='button'>Hello</div>
</div>

Here .button width is constrained using the parent element. Setting the border-left-width adjusts the content-box size and thus the position of the text.

.parent {
    width: 200px;
}

.button {
    background: #333;
    color: #fff;
    padding: 20px;
    position: relative;
}

.button::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border: 5px solid #f00;
    border-left-width: 20px;
}
<div class='parent'>
    <div class='button'>Hello</div>
</div>

Using the pseudo-element approach does not affect the content-box size.

Depending on the application, approach using a pseudo-element might or might not be a desirable behaviour.

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • If your box contains an image, this is the best solution. Everything else here shrinks the image size down. – Jason Dec 21 '22 at 17:29
8

11 Years Later but heres the answer:

Just use outline:
outline: 0.2vw solid red;

I hope i can help someone who sees this question also 11 Yeas Later.

20maty
  • 131
  • 1
  • 5
6

I know this is somewhat older, but since the keywords "border inside" landed me directly here, I would like to share some findings that may be worth mentioning here. When I was adding a border on the hover state, i got the effects that OP is talking about. The border ads pixels to the dimension of the box which made it jumpy. There is two more ways one can deal with this that also work for IE7.

1) Have a border already attached to the element and simply change the color. This way the mathematics are already included.

div {
   width:100px;
   height:100px;
   background-color: #aaa;
   border: 2px solid #aaa; /* notice the solid */
}

div:hover {
   border: 2px dashed #666;
}

2 ) Compensate your border with a negative margin. This will still add the extra pixels, but the positioning of the element will not be jumpy on

div {
   width:100px;
   height:100px;
   background-color: #aaa;
}

div:hover {
  margin: -2px;
  border: 2px dashed #333;
}
Daniel
  • 4,816
  • 3
  • 27
  • 31
3

for consistent rendering between new and older browsers, add a double container, the outer with the width, the inner with the border.

<div style="width:100px;">
<div style="border:2px solid #000;">
contents here
</div>
</div>

this is obviously only if your precise width is more important than having extra markup!

Evert
  • 8,161
  • 1
  • 15
  • 17
2

If you use box-sizing: border-box means not only border, padding,margin, etc. All element will come inside of the parent element.

div p {
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    width: 150px;
    height:100%;
    border: 20px solid #f00;
    background-color: #00f;
    color:#fff;
    padding: 10px;
  
}
<div>
  <p>It was popularised in the 1960s with the release of Letraset sheets</p>
</div>
Ayyappan K
  • 1,111
  • 8
  • 9
0

Best cross browser solution (mostly for IE support) like @Steve said is to make a div 98px in width and height than add a border 1px around it, or you could make a background image for div 100x100 px and draw a border on it.

Milan and Friends
  • 5,560
  • 1
  • 20
  • 28
0

You can look at outline with offset but this needs some padding to exists on your div. Or you can absolutely position a border div inside, something like

<div id='parentDiv' style='position:relative'>
  <div id='parentDivsContent'></div>
  <div id='fakeBordersDiv' 
       style='position: absolute;width: 100%;
              height: 100%;
              z-index: 2;
              border: 2px solid;
              border-radius: 2px;'/>
</div>

You might need to fiddle with margins on the fake borders div to fit it as you like.

Gorky
  • 1,393
  • 19
  • 21
0

A more modern solution might be to use css variables and calc. calc is widely supported but variables is not yet in IE11 (polyfills available).

:root {
  box-width: 100px;
  border-width: 1px;
}

#box {
  width: calc(var(--box-width) - var(--border-width));
}

Although this does use some calculations, which the original questions was looking to avoid. I think this is an ok time to use calculations as they are controlled by the css itself. It also has no need for additional markup or misappropriating other css properties that may be needed later on.

This solution is only really useful if a fixed height isn't needed.

Andy Polhill
  • 6,858
  • 2
  • 24
  • 20
0

One solution I didn't see mentioned above is the case where you have padding on your input, which I do 99% of the time. You can do something along the lines of...

input {
  padding: 8px;
}

input.invalid {
  border: 2px solid red;
  padding: 6px; // 8px - border or "calc(8px - 2px)"
}

What I like about this is that I have the full menu of border + padding + transition properties for each side.

Gobot
  • 2,464
  • 1
  • 21
  • 26