CSS Borders
A border is the visible line drawn around an element’s padding, sitting between the padding and the margin in the CSS box model. Borders are used everywhere in real interfaces: to outline cards and buttons, separate table cells, draw dividers, and even to fake shapes like triangles and arrows. Understanding how the border properties interact — and how they affect an element’s rendered size — is essential for building layouts that don’t unexpectedly shift or overflow.
Overview / How it works
Every element’s box, by default, is made of four concentric regions: content, padding, border, and margin, from the inside out. The border sits directly on top of the padding edge and is drawn as a ring around the box. A border has three independent aspects for each of its four sides: a width (how thick the line is), a style (what pattern it’s drawn in — solid, dashed, dotted, etc.), and a color.
Critically, a border only renders if it has a style other than none (the default) or hidden. If you set border-width and border-color but never set border-style, the browser reserves no space and paints nothing — this is the single most common border mistake, covered below.
By default (box-sizing: content-box), the border adds to an element’s total rendered size: a box with width: 200px and border: 5px solid on all sides renders 210px wide (200 + 5 + 5), because the border is drawn outside the declared content width. Most modern stylesheets set box-sizing: border-box globally so that width and height include the border and padding, keeping sizes predictable when borders are added or changed.
CSS lets you control each side independently with border-top, border-right, border-bottom, and border-left (each themselves shorthands for width/style/color on that one side), or control one aspect across all sides with border-width, border-style, and border-color, each accepting one to four values in the familiar top/right/bottom/left order. The border-radius property rounds the corners by drawing an elliptical arc where two adjacent border edges would otherwise meet at a sharp corner, and it also clips the background and, in most browsers, overflowing content.
Syntax
border: <width> <style> <color>;
border-top | border-right | border-bottom | border-left: <width> <style> <color>;
border-width | border-style | border-color: <value> [<value> <value> <value>];
border-radius: <length|percentage> [/ <length|percentage>];
| Part | Description |
|---|---|
<width> |
A length (e.g. 2px, 0.25rem) or a keyword: thin, medium, thick. Defaults to medium if omitted. |
<style> |
Required for the border to render. Common values: solid, dashed, dotted, double, groove, ridge, inset, outset, none, hidden. |
<color> |
Any valid color value. If omitted, defaults to currentColor — the element’s computed text color. |
| Multi-value shorthands | 1 value = all sides; 2 values = top/bottom, left/right; 3 values = top, left/right, bottom; 4 values = top, right, bottom, left (clockwise). |
Examples
Example 1: A simple bordered card
.card {
border: 2px solid #2563eb;
border-radius: 6px;
padding: 16px;
box-sizing: border-box;
}
Result: The .card element gets a crisp 2px solid blue line drawn around its padded content, with the four corners rounded to a 6px radius. Because box-sizing: border-box is set, the border and padding are subtracted from the declared width instead of adding to it, so the box stays exactly as wide as specified.
This is the most common pattern: a single-color, single-width border using the shorthand, which sets width, style, and color for all four sides in one declaration.
Example 2: An accent-left callout box
.callout {
border: 1px solid #ddd;
border-left: 6px solid #f59e0b;
border-radius: 4px;
padding: 12px 16px;
background-color: #fffbeb;
}
Result: The box gets a thin 1px light-gray border on all four sides, but the left edge is overridden with a thicker 6px solid amber border, creating a highlighted “accent stripe” down the left side — a common pattern for note or warning boxes.
This works because border-left is more specific (in terms of which sides it targets, not CSS specificity) than the general border shorthand, and it’s declared after it, so the cascade lets it override just the left side’s width and color while leaving the other three sides untouched.
Example 3: A rounded, interactive button
.btn {
display: inline-block;
padding: 10px 20px;
border: 2px solid #2563eb;
border-radius: 9999px;
background-color: transparent;
color: #2563eb;
transition: background-color 0.2s ease, color 0.2s ease;
}
.btn:hover {
background-color: #2563eb;
color: #ffffff;
}
Result: The button renders as a pill shape (a very large border-radius makes the ends fully rounded) with a solid blue outline and blue text on a transparent background. On hover, the background smoothly transitions to solid blue and the text becomes white, while the 2px border stays the same color and width throughout.
This example shows borders combined with border-radius to create a pill/capsule shape, plus a :hover state that changes fill color without touching the border itself, so the outline stays visually stable during the transition.
How it works step by step / Under the hood
When the rendering engine lays out a box, it computes sizes in this order: content box, then padding box (content + padding), then border box (padding + border), then margin box (border + margin). For each of the four sides, the engine reads the resolved border-*-width, border-*-style, and border-*-color values and paints a trapezoid-shaped ring segment for that side; where a border-style is none or the computed width is 0, the engine treats that side’s border width as 0 for layout purposes, even if you specified a width.
At each corner, without border-radius, the two adjacent side segments meet at a 45-degree miter, which is why a thick solid border with different colors on adjacent sides (like the classic CSS-triangle trick using border-color: transparent on three sides) produces diagonal, triangular shapes. When border-radius is applied, the engine instead clips each corner with a quarter-ellipse arc sized by the radius value, and it recalculates where each straight border segment starts and ends so the border and the corner arc blend into one continuous outline.
Because border declarations follow the normal cascade, the final winning value for each of the twelve sub-properties (width/style/color × four sides) is simply whichever declaration for that specific sub-property appears last among equally-specific selectors — this is why shorthand-after-longhand ordering matters, as shown in the mistakes below.
Common Mistakes
Mistake 1: Setting width and color but forgetting style.
.box {
border-width: 5px;
border-color: red;
}
This renders no visible border at all. The initial value of border-style is none, and a none-style border always computes to a width of 0, regardless of what border-width says. The fix is to always include a style:
.box {
border-width: 5px;
border-style: solid;
border-color: red;
}
Mistake 2: A shorthand accidentally resetting a longhand you set earlier.
.box {
border-bottom: 4px solid blue;
border: 1px solid black;
}
Here the intent was probably a thin black border with a thicker blue bottom accent, but because border is a shorthand for all four sides’ width, style, and color at once, the second rule resets border-bottom back to 1px solid black too, erasing the earlier declaration. The fix is to declare the general shorthand first, then override just the side you want to differ:
.box {
border: 1px solid black;
border-bottom: 4px solid blue;
}
Best Practices
- Set
box-sizing: border-box(often globally via a reset) so adding or changing a border doesn’t change an element’s rendered width or height. - Always pair
border-width/border-colorwith an explicitborder-style— without it, nothing renders. - When mixing the
bordershorthand with a per-side override likeborder-left, declare the shorthand first so the cascade order does what you expect. - Use
currentColor(the default when no color is specified) to make a border automatically match the element’s text color, which is useful for buttons and links that change color in different states. - Prefer logical properties like
border-inline-startoverborder-leftwhen building layouts that need to support right-to-left languages. - Use a single
border-radiusvalue equal to half the element’s height (or a very large value like9999px) to reliably create pill and circle shapes. - Avoid layering a border and a
box-shadowthat mimics a border for the same edge — pick one, since both add visual weight and the shadow one doesn’t affect layout the same way.
Practice Exercises
Exercise 1: Create a .notice class with a 1px solid gray border on all sides, a 4px solid red border only on the top edge, and 8px of padding. Think about which order your two declarations need to be in.
Exercise 2: Create a 40px-by-40px circle using only width, height, background-color, and border-radius. What value of border-radius is required, and why does it only work when width and height are equal?
Exercise 3: Write a rule for .card with a 1px transparent border by default and a 1px solid blue border on :hover. Explain in a comment why declaring the transparent border up front prevents the card from jumping in size when the visible border appears on hover.
Summary
- A border sits between an element’s padding and margin, and needs a non-
noneborder-styleto render at all. - The
bordershorthand sets width, style, and color for all four sides at once;border-top/-right/-bottom/-lefttarget one side at a time. - By default, borders add to an element’s declared size unless
box-sizing: border-boxis used. - Multi-value shorthands like
border-widthfollow top/right/bottom/left order depending on how many values are given. - Declaration order matters: a later shorthand overrides an earlier longhand for the same sides, even without higher specificity.
border-radiusrounds corners by clipping them with elliptical arcs, and equal width/height plus a large enough radius produces a circle.
