CSS Outline
The CSS outline property draws a visible line around an element that sits outside its border, without changing the element’s size or shifting surrounding layout. It exists mainly to mark focus and interactive state — the ring you see around a button or input when you tab to it with a keyboard — but it can also be used as a lightweight decorative device. Understanding it well matters for two reasons: getting it wrong breaks keyboard accessibility for real users, and getting it right lets you add emphasis without disturbing the box model math your layout depends on.
Overview: How the Outline Property Works
Every element generates a box from the CSS box model: content, padding, border, and margin, moving outward in that order. The outline property adds a visual layer drawn outside the border edge, but — critically — it is not part of the box model at all. It does not add to an element’s width or height, it does not push margins or neighboring elements around, and changing it never triggers a layout reflow. This is the single most important thing to understand about outlines: they are a paint-time effect, computed after layout has already finished.
By default the outline is drawn flush against the outer edge of the border, all the way around the box, as a rectangle. You can pull it away from the border with outline-offset, which inserts (or, if negative, removes) a gap between the border’s outer edge and the outline itself. Because the outline is painted outside the border but never reserved for in layout, it can visually overlap sibling elements, scrollbars, or the edge of the viewport if there isn’t enough surrounding space — nothing in the layout algorithm accounts for it.
Historically, outlines were always drawn as sharp rectangles, even on elements with a rounded border-radius. Modern evergreen browsers (current Chrome, Firefox, Safari, and Edge) now round the outline to follow the border-radius automatically, which makes outlines look far more natural on cards, buttons, and pills. If you must support very old browsers this behavior isn’t guaranteed, but for modern-browser targets you can rely on it.
The outline property participates in the cascade exactly like any other property — normal specificity and !important rules apply to it and its longhands (outline-color, outline-style, outline-width, outline-offset). What makes it special isn’t the cascade, it’s the rendering model: no layout impact, painted on top of everything else in the box, and — unlike border — it has no per-side control. You cannot give an element an outline only on the top or only on the left; it is always a single rectangle (or rounded shape) around the entire box.
Syntax
The shorthand outline accepts outline-color, outline-style, and outline-width in any order, separated by spaces, the same pattern border uses. outline-offset is always set as its own declaration — it is not part of the outline shorthand.
selector {
outline: <outline-color> || <outline-style> || <outline-width>;
outline-offset: <length>;
}
| Property | Values | Default | Notes |
|---|---|---|---|
outline-style |
none, solid, dotted, dashed, double, groove, ridge, inset, outset | none | Required — without a style, no outline renders even if color/width are set |
outline-color |
any valid color, or the legacy keyword invert (poor support) |
currentColor | Inherits the element’s computed text color if omitted |
outline-width |
thin, medium, thick, or a length (e.g. 2px) |
medium | Behaves like border-width |
outline-offset |
a length (e.g. 4px); may be negative | 0 | Gap between the border’s outer edge and the outline |
Examples
Example 1: A minimal, accessible focus ring
Applies to a plain <button>Submit</button> element.
button {
padding: 10px 20px;
border: 1px solid #444444;
border-radius: 4px;
background-color: #f0f0f0;
font-size: 16px;
}
button:focus-visible {
outline: 3px solid #2684ff;
outline-offset: 2px;
}
Result: The button renders as a light gray rectangle with a thin dark border and 4px rounded corners. Nothing changes when hovered with a mouse or clicked. When the user tabs to it with the keyboard, a bright blue 3px ring appears floating 2px outside the button’s border, following its rounded corners, without shifting the button or any surrounding content.
This is the canonical outline use case: a state indicator that appears and disappears without ever affecting layout, because outline is excluded from the box model entirely.
Example 2: Marking a selected card with an inset dashed outline
Applies to <div class=\”card is-selected\”>…</div>.
.card {
width: 220px;
padding: 16px;
border: 2px solid #cccccc;
border-radius: 8px;
background-color: #ffffff;
}
.card.is-selected {
outline: 2px dashed #ff6b00;
outline-offset: -6px;
}
Result: A 220px-wide white card with a light gray border and rounded corners. When the is-selected class is present, an orange dashed line appears 6px inside the card’s border edge, overlapping the card’s own padding area, visually framing the content without adding a second border ring outside the card.
This demonstrates a negative outline-offset: instead of floating outside the box, the outline is pulled inward and overlaps the element’s own painted content, which is useful for a \”selected\” indicator that doesn’t add extra dimensions to a grid of cards.
Example 3: A themed focus outline using a custom property
Applies to <input class=\”input-field\” type=\”text\”>.
:root {
--focus-color: #6200ee;
}
.input-field {
padding: 8px 12px;
border: 1px solid #999999;
border-radius: 6px;
outline: none;
}
.input-field:focus-visible {
outline: 2px solid var(--focus-color);
outline-offset: 3px;
transition: outline-offset 0.15s ease;
}
Result: A text input with a gray 1px border and rounded corners, and no outline in its resting state. When focused via keyboard, a solid 2px purple outline fades in 3px outside the border, and because outline-offset is transitioned, the gap animates open over 0.15 seconds rather than snapping in instantly.
Note that transition can animate outline-offset, outline-width, and outline-color smoothly since they are all animatable properties, which lets you build polished focus states instead of an abrupt on/off ring.
How It Works Step by Step (Under the Hood)
- The browser first computes the element’s border box — content, padding, and border — exactly as it would if
outlinedidn’t exist, using whateverbox-sizingis in effect. - Layout for the element and all of its siblings is finalized using only that border box. The outline is never consulted during this pass, because it isn’t a box-model property.
- During the paint phase, the browser draws the background, then the border, in the normal box-model paint order.
- The outline is drawn afterward, as a separate paint step, positioned relative to the border’s outer edge and shifted by whatever
outline-offsetspecifies (inward if negative, outward if positive). - Because painting happens after layout is locked in, an outline can overlap adjacent elements, scrollbars, or the viewport edge with no adjustment being made to accommodate it — unlike growing a
border-width, which does reflow the layout. - Separately, the browser’s input-modality heuristics decide whether
:focus-visibleshould match at all — generally true for keyboard/programmatic focus and false for a mouse click on most elements — and only then are any:focus-visibleoutline rules applied during paint.
Common Mistakes
Mistake 1: Removing all focus outlines for aesthetics
Wrong:
button:focus,
input:focus,
a:focus {
outline: none;
}
Why it’s wrong: This is one of the most common accessibility failures on the web. It removes the only visual signal that tells a keyboard-only or switch-device user which element currently has focus, making the page effectively unusable without a mouse. Browsers include a default focus ring specifically so this never has to be guessed at — deleting it with no replacement is a regression, not a cleanup.
Corrected:
button:focus-visible,
input:focus-visible,
a:focus-visible {
outline: 2px solid #2684ff;
outline-offset: 2px;
}
This keeps a strong, deliberately styled ring for keyboard/programmatic focus (via :focus-visible) while letting you skip showing it for an ordinary mouse click, satisfying both the designer’s preference and accessibility requirements.
Mistake 2: Setting color and width but forgetting outline-style
Wrong:
.alert {
outline-color: #d32f2f;
outline-width: 3px;
}
Why it’s wrong: outline-style defaults to none, and an outline with style: none never renders, regardless of what color or width is specified. This is the exact same gotcha as leaving border-style unset on a border declaration — the color and width are computed but nothing is painted.
Corrected:
outline-style: solid;
outline-color: #d32f2f;
outline-width: 3px;
Adding an explicit style (here, solid) is what actually makes the outline visible; the shorthand outline: solid #d32f2f 3px; avoids this trap entirely because it forces you to think about all three parts at once.
Best Practices
- Never write a blanket
outline: nonerule; scope removal to:focus-visibleexceptions and always supply a replacement indicator. - Prefer
outlineoverborderfor focus and selection states, since it never affects layout or causes reflow when it appears or disappears. - Use
outline-offsetto give the ring breathing room, especially on rounded or tightly-packed elements, so it doesn’t look glued to the border. - Make sure
outline-colorhas sufficient contrast against the background per WCAG guidance — a focus ring that blends into the page defeats its purpose. - Always test focus styles by pressing Tab through the page, not just by clicking —
:focus-visiblebehavior differs from plain:hover/click testing. - Use the shorthand
outline: color style width;when possible so you never accidentally omitoutline-style.
Practice Exercises
- Style every <input>, <textarea>, and <select> on a form so their default state has no outline, but their
:focus-visiblestate shows a 2px solid outline in a color of your choice with a 3px offset. Verify with Tab navigation that the ring appears. - Build a card component with
border-radius: 12pxand add a dashed outline with an offset of-8pxthat appears only on:hover. Describe in a comment which part of the card the dashed line ends up overlapping. - Create a \”skip to content\” link that is visually hidden in its resting state and, when it receives keyboard focus, becomes visible with a highly visible 4px solid outline offset 4px outward, in a high-contrast color.
Summary
outlinedraws outside the border edge but is excluded from the box model — it never changes an element’s size or triggers layout reflow.- The shorthand combines
outline-color,outline-style, andoutline-width;outline-offsetis always declared separately. outline-styledefaults tonone, so color and width alone render nothing — a style must be set explicitly.outline-offsetcan be negative to pull the outline inward, overlapping the element’s own content, or positive to float it outward.- Modern browsers round outlines to match
border-radiusautomatically; older browsers drew them as sharp rectangles. - Never remove focus outlines without a deliberate, visible replacement — pair removal with
:focus-visiblestyling to preserve keyboard accessibility.
