The 3 CSS Methods for Adding Element Borders

When it comes to CSS, sometimes a border is not really a border.

In this episode, we'll cover the differences between:

  • border
  • outline
  • box-shadow

We'll also discuss when you might use one over the other.

Refresher on the CSS Box Model

A key difference between our three border methods is where they are placed on an element and how they affect its dimensions. This behavior is controlled by the CSS box model.

An illustration of the CSS box model, with the relevant parts described following this image

  • the border is precisely the boundary of the element, sitting between its padding and margin, and it's width will impact the computed element dimensions
  • the outline is next to but outside of the border, overlapping both box-shadow and margin, but not affecting the element's dimensions
  • by default, box-shadow extends out from edge of the border covering the amount of space in the direction(s) defined, and it will also not affect the element's dimensions

border Syntax and Usage

Borders have been a standard in design since the beginning of the web.

An important difference compared to the other two methods we're going to cover is that by default borders are included in the computed dimensions of an element. Even if you set box-sizing: border-box the borders still figure into the calculation.

The most essential syntax for a border defines it's width and style:

border: 3px solid;

If not defined, the default color will be currentColor which means it will use the nearest definition for color in the cascade.

But there are more styles available for border, and using border-style you can define a different style for each side if you'd like.

Visit MDN docs to review all available border-style values and see examples.

When to Use border

Border is a solid choice (pun intended) for when it's acceptable for the style to be computed in the element's dimensions. And the default styles give a lot of design flexibility.

Keep reading for a bonus tip about something only border can do!

outline Syntax and Usage

For outlines, the only required property to make it visible is to provide a style since the default is none:

outline: solid;

Like border, it will gain color via currentColor and it's default width will be medium.

The typical application of outline is by native browser styles on :focus of interactive elements like links and buttons.

This particular application should be allowed to occur for purposes of accessibility unless you are able to provide a custom :focus style that meets the WCAG Success Criterion 2.4.7 Focus Visible.

For design purposes, an often noted issue with outline is that it is unable to inherit the curve from any border-radius styles.

When to Use outline

Use of outline may be desirable when you don't want to impact the element's dimensions, and you don't need it to follow a border-radius. It happens to use the same style values as border so you can achieve a similar look.

box-shadow Syntax and Usage

The minimal required properties for box shadow are values for the x and y axis and a color:

box-shadow: 5px 8px black;

Optionally, add a third unit to create blur, and a fourth to add spread.

Check out my 4.5 minute video demo on egghead to learn more about the expanded syntax as well as tips on using box-shadow.

To use it to create a border, we set the x and y axis values as well as the blur to 0. Then set a positive number for spread:

box-shadow: 0 0 0 3px blue;

This will create the appearance of a border around the element and it can even follow an applied border-radius:

an element using box-shadow in place of a border for the effect of a rounded blue border

When to Use box-shadow

You may prefer box-shadow particularly when you want to animate a border without causing layout shift. The next section will demonstrate an example of this context.

And since box-shadow can be layered, it's an all-around good tool to get to know to enhance your layouts.

However, it will not be able to fully mimic some of the styles provided by border and outline.

Join my newsletter for article updates, CSS tips, and front-end resources!

Putting It All Together

Here are a few practical scenarios where you may need to use a border alternative.

Button Borders

A common case of the real border becoming an issue is when providing styles for both bordered and non-bordered buttons, and the scenario of them lining up next to each other.

a button using a border which appears visually larger than the second button with a background but no border

A typical solution is usually increasing the non-bordered button dimensions equal to the border-width.

An alternate solution with our new knowledge is to use box-shadow along with the inset keyword to place the pseudo border visually inside the button:

updated styles using box-shadow on the first button resulting in the buttons appearing equal size

Note that your padding will have to be larger than the border-width to prevent overlap of the text content.

Alternatively, perhaps you want to add a border on :hover or :focus. Using the real border, you will have an undesirable visual jump from layout shift since the border will briefly increase the dimensions in those states.

demo of a border being added on hover and causing the button to jump in place

In this case, we can use box-shadow to create the pseudo border so that the true dimensions are not increased - plus we can animate it using transition:

demo of the box-shadow border on hover which no longer causes the button to jump

Here's the reduced code for the above example:

button {
  transition: box-shadow 180ms ease-in;
}

button:hover {
  box-shadow: 0 0 0 3px tomato;
}

Upgrading Your CSS Debugging Method

A classic CSS joke is that to figure out CSS issues particularly for overflow scrolling or positioning is to add:

* {
  border: 1px solid red;
}

Which will add a red border to every element.

But as we've learned, this will also affect their computed dimensions, thus potentially accidentally causing you additional issues.

Instead, use:

* {
  outline: 1px solid red;
}

Pop quiz: where will the outline be placed, and why is this a better solution?

One potential consequence of using border is adding scrollbars once content is re-drawn. This side-effect will not happen when using outline.

Additionally, you're likely to be using border for elements already, so universally changing the border will cause layout shifts which is certainly likely to introduce new problems.

Side note: Browser DevTools also have evolved more sophisticated methods of helping you identify elements, with Firefox even adding both a "scroll" and "overflow" tag that is helpful in the case of debugging for overflow. I encourage you to spend some time learning more about DevTool features!

Ensuring Visible Focus

For accessibility, one scenario you may not be aware of is users of Windows High Contrast Mode (WHCM).

In this mode, your provided colors are stripped away to a reduced high contrast palette. Not all CSS properties are allowed, including box-shadow.

One practical impact is that if you have removed outline upon :focus and replaced it with box-shadow, users of WHCM will no longer be given visible focus.

To remove this negative impact, you can apply a transparent outline on :focus:

button:focus {
  outline: 2px solid transparent;
}

For a bit more context on this specific issue, review the episode on button styling.

Pitfalls of outline and box-shadow

Because outline and box-shadow sit outside of the border in the box model, one consequence you may encounter is having them disappear under the edges of the viewport. So, you may need to add margin to the element or padding to the body as a countermeasure if you want it to remain visible.

Their placement also means they can be sheared off by properties such as overflow: hidden or the use of clip-path.

Bonus Tip: Gradient Borders

As promised, here's a bonus tip about something that - of the methods we've discussed - only border can do.

An often forgotten border-related property is border-image. The syntax can be a bit strange when trying to use it with actual images.

But it has a hidden power - it also allows you to create gradient borders since CSS gradients are technically images:

preview of an element that has a pastel rainbow gradient applied with the text "Hello World"

This requires defining a regular border width and style (although it will only display as solid regardless of style value), followed by the border-image property that can use the gradient syntax with one addition. The number after the gradient is the slice value which for our gradient we can simply use a value of 1 to essentially not alter the sizing/distortion of the gradient.

div {
  border: 10px solid;
  /* simplified from preview image */
  border-image: linear-gradient(to right, purple, pink) 1;
}

To place the border on only one side, be sure to set the other sides to zero first or some browsers will still add it to all sides:

div {
  border-style: solid;
  border-width: 0;
  border-left-width: 3px;
  /* border-image */
}

The downside is that these borders do not respect border-radius, so if you need a solution that does, you can use Inspector to peek at how the gradients are added for the cards on the ModernCSS home page 😉