This is episode #23 in a series examining modern CSS solutions to problems I've been solving over the last 14+ years of being a frontend developer.
When it comes to CSS, sometimes a
border is not really a
In this episode, we'll cover the differences between:
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.
borderis precisely the boundary of the element, sitting between its padding and margin, and it's width will impact the computed element dimensions
outlineis next to but outside of the
border, overlapping both
margin, but not affecting the element's dimensions
- by default,
box-shadowextends 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-stylevalues and see examples.
When to Use
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
outline Syntax and Usage#
For outlines, the only required property to make it visible is to provide a style since the default is
Like border, it will gain color via
currentColor and it's default width will be
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
When to Use
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
y axis and a color:
box-shadow: 5px 8px black;
Optionally, add a third unit to create
blur, and a fourth to add
Check out my 4.5 minute video demo on egghead to learn more about the expanded syntax as well as tips on using
To use it to create a border, we set the
y axis values as well as the
0. Then set a positive number for
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
When to Use
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.
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
Would you like CSS tips in your inbox? 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
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 typical solution is usually increasing the non-bordered button dimensions equal to the
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:
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
: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.
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
Here's the reduced code for the above example:
transition: box-shadow 180ms ease-in;
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.
outline: 1px solid red;
Pop quiz: where will the
outlinebe 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
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
One practical impact is that if you have removed
: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
outline: 2px solid transparent;
For a bit more context on this specific issue, review the episode on button styling.
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
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:
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.
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:
/* 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 😉