Borders
Border width, colour, style and radius — plus outlines, rings and dividers, and the default border colour change in v4.
Borders, outlines and rings are three different ways to draw a line around an element. Knowing which to use is most of the battle.
Width#
| Utility | Value |
|---|---|
border | 1px, all sides |
border-0 border-2 border-4 border-8 | Explicit widths |
border-x-2 / border-y-2 | Horizontal / vertical pairs |
border-t border-r border-b border-l | Individual sides |
border-s-2 / border-e-2 | Logical start / end |
border-bs-2 / border-be-* | Logical block start / end v4.2 |
border-[3px] | Arbitrary |
Default colour#
To restore the v3 default project-wide:
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}That said, being explicit is better — it makes dark mode obvious.
Colour and style#
<div class="border border-gray-200">Solid, 1px</div>
<div class="border-2 border-dashed border-indigo-500">Dashed</div>
<div class="border-t border-b border-gray-200">Top and bottom only</div>
<div class="border border-indigo-500/30">Translucent</div>| Style utility | CSS |
|---|---|
border-solid | Default |
border-dashed border-dotted border-double | Alternatives |
border-hidden border-none | No border |
Radius#
| Utility | Value |
|---|---|
rounded-none | 0 |
rounded-xs | 0.125rem |
rounded-sm | 0.25rem |
rounded-md | 0.375rem |
rounded-lg | 0.5rem |
rounded-xl | 0.75rem |
rounded-2xl | 1rem |
rounded-3xl | 1.5rem |
rounded-full | 9999px |
Per-corner and per-side#
<div class="rounded-t-xl">Top corners</div>
<div class="rounded-tl-xl rounded-br-xl">Diagonal corners</div>
<div class="rounded-s-lg">Logical start side</div>Nested radii#
An inner element inside a padded rounded box needs a smaller radius to look right:
<div class="rounded-xl border border-gray-200 p-1">
<div class="rounded-[calc(0.75rem-0.25rem)] bg-gray-100 p-4">…</div>
</div>The formula is outer radius − padding. With theme variables:
<div class="rounded-[calc(var(--radius-xl)-4px)]">…</div>Outline#
Outlines are drawn outside the border box and do not affect layout — which makes them ideal for focus indicators.
| Utility | Purpose |
|---|---|
outline / outline-2 / outline-4 | Width (also sets outline-style: solid) |
outline-offset-2 | Gap between element and outline |
outline-indigo-500 | Colour |
outline-dashed outline-dotted | Style |
outline-hidden | Hide, but keep it in forced-colours mode |
outline-none | outline-style: none |
<div class="flex gap-3 p-4">
<button class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
Tab to me
</button>
<button class="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500">
Then me
</button>
</div>Rings#
A ring is a box-shadow shaped like an outline. Unlike a real outline it follows border-radius exactly.
| Utility | Purpose |
|---|---|
ring | 1px ring |
ring-2 ring-4 ring-8 | Widths |
ring-indigo-500 | Colour |
ring-offset-2 ring-offset-white | Gap and its colour |
ring-inset | Draw inside the box |
inset-ring-2 | Dedicated inset ring |
<div class="flex flex-wrap gap-4 p-4 text-sm">
<div class="rounded-lg bg-white px-4 py-3 ring-1 ring-gray-900/10">ring-1 ring-gray-900/10</div>
<div class="rounded-lg bg-white px-4 py-3 ring-2 ring-indigo-500">ring-2 ring-indigo-500</div>
<div class="rounded-lg bg-white px-4 py-3 ring-2 ring-indigo-500 ring-offset-2">with ring-offset-2</div>
</div>Border vs outline vs ring#
border | outline | ring | |
|---|---|---|---|
| Affects layout | Yes — adds to the box | No | No |
Follows border-radius | Yes | Mostly | Exactly |
| Can be per-side | Yes | No | No |
| Best for | Structure, dividers | Focus indicators | Focus rings, subtle elevation |
Dividers#
divide-* puts borders between children, with none on the last one.
<ul class="mx-auto max-w-sm divide-y divide-gray-200 rounded-lg border border-gray-200 text-sm">
<li class="px-4 py-3 text-gray-700">Inbox</li>
<li class="px-4 py-3 text-gray-700">Drafts</li>
<li class="px-4 py-3 text-gray-700">Sent</li>
</ul>| Utility | Purpose |
|---|---|
divide-y / divide-x | Direction |
divide-y-2 | Width |
divide-gray-200 | Colour |
divide-dashed | Style |
Common patterns#
Card#
<div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">…</div>Input with a focus ring#
<input class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none" />Button group#
<div class="inline-flex rounded-lg border border-gray-300 p-4 text-sm shadow-sm">
<button class="rounded-l-md border-r border-gray-300 bg-white px-3 py-1.5 font-medium text-gray-700 hover:bg-gray-50">Day</button>
<button class="border-r border-gray-300 bg-white px-3 py-1.5 font-medium text-gray-700 hover:bg-gray-50">Week</button>
<button class="rounded-r-md bg-white px-3 py-1.5 font-medium text-gray-700 hover:bg-gray-50">Month</button>
</div>Selected state without layout shift#
<label class="rounded-lg border border-gray-300 p-4 has-checked:border-indigo-500 has-checked:ring-1 has-checked:ring-indigo-500">
<input type="radio" name="plan" class="accent-indigo-600" /> Pro
</label>Common mistakes#
- Relying on the v3 default border colour.
borderalone iscurrentColorin v4. border-2on hover. Causes a 1px layout jump. Use a ring, or set the border width in both states.outline-nonewith no replacement. Breaks keyboard navigation. Useoutline-hiddenplus a visible ring.- Rounded parent without
overflow-hidden. Child images poke through the corners. - Forgetting the dark-mode border.
border-gray-200glows on a dark surface. - Expecting
divide-*to work on a flex container withgap. It works, but the gap and the divider fight; pick one.
Related topics#
- Shadows — rings and elevation
- Colors — border colour and opacity
- Forms — focus and validation styling
- Deprecated APIs — the full list of renames