Borders, Rings & Shadows
Borders, rounded corners and shadows turn flat boxes into cards, buttons and panels that feel separate from the page. Rings and outlines show which element has keyboard focus. Tailwind has a utility for each, and they combine freely.
In this lesson you will draw borders on any side, round corners, separate list items with dividers, lift elements with shadows, and draw focus rings that do not move your layout. You will also learn the defaults that changed between Tailwind v3 and v4, because they explain many "why does my border look black?" questions.
Borders
border adds a 1px solid border on all sides. Add a number for thicker borders and a side for one edge only:
| Class | Result |
|---|---|
border | 1px on all sides |
border-2, border-4, border-8 | 2px, 4px, 8px on all sides |
border-t, border-b-2 | Top only, bottom only (2px) |
border-x, border-y | Left and right, top and bottom |
border-s-4, border-e | Inline start and end, which flip in right-to-left pages |
border-0 | No border |
Change the style with border-dashed, border-dotted or border-double, and the colour with any palette colour: border-slate-200, border-sky-500, border-red-500/50.
Rounded corners
rounded-* sets border-radius from a named scale:
| Class | Radius |
|---|---|
rounded-xs | 0.125rem (2px) |
rounded-sm | 0.25rem (4px) |
rounded-md | 0.375rem (6px) |
rounded-lg | 0.5rem (8px) |
rounded-xl | 0.75rem (12px) |
rounded-2xl | 1rem (16px) |
rounded-3xl, rounded-4xl | 1.5rem, 2rem |
rounded-full | A pill, or a circle on a square element |
Round only some corners with a side or corner: rounded-t-xl rounds the top two corners, rounded-s-lg the two start corners, and rounded-br-2xl just the bottom-right one.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Borders and radius</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="flex flex-wrap gap-4 p-6 text-sm">
<div class="rounded-lg border border-slate-300 p-4">border rounded-lg</div>
<div class="rounded-xl border-2 border-dashed border-sky-500 p-4">border-2 dashed</div>
<div class="border-s-4 border-amber-500 bg-amber-50 p-4">border-s-4 (callout)</div>
<div class="rounded-t-2xl border-b-4 border-emerald-600 bg-emerald-50 p-4">rounded-t-2xl border-b-4</div>
<div class="rounded-full bg-slate-900 px-5 py-2 text-white">rounded-full pill</div>
<img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Isha" class="size-14 rounded-full border-4 border-white ring-2 ring-sky-500">
</body>
</html>Dividers between items
For a list of rows, putting border-b on each item leaves an extra line under the last one. The divide-* utilities go on the parent and add a border between children only:
<ul class="divide-y divide-slate-200 rounded-xl border border-slate-200">
<li class="p-3">Aarav: 92</li>
<li class="p-3">Priya: 88</li>
<li class="p-3">Rohan: 85</li>
</ul>divide-y draws horizontal lines between rows and divide-x draws vertical lines between columns; divide-slate-200 colours them.
Shadows
shadow-* adds a box shadow from a scale that goes from barely visible to dramatic: shadow-2xs, shadow-xs, shadow-sm, shadow-md, shadow-lg, shadow-xl and shadow-2xl. Remove a shadow with shadow-none.
Shadows can be coloured too: shadow-lg shadow-sky-500/30 gives a soft blue glow under a blue button. Two other kinds are useful:
inset-shadow-*draws the shadow inside the element, which makes it look pressed in, like an input field.drop-shadow-*is a CSS filter. Unlike a box shadow, it follows the actual shape of transparent images and SVG icons instead of their rectangular box.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shadows</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="bg-slate-100 p-8">
<div class="grid max-w-3xl gap-6 text-sm sm:grid-cols-4">
<div class="rounded-xl bg-white p-4 shadow-xs">shadow-xs</div>
<div class="rounded-xl bg-white p-4 shadow-md">shadow-md</div>
<div class="rounded-xl bg-white p-4 shadow-xl">shadow-xl</div>
<div class="rounded-xl bg-white p-4 shadow-2xl">shadow-2xl</div>
<button class="rounded-xl bg-sky-600 p-4 text-white shadow-lg shadow-sky-600/40">coloured shadow</button>
<div class="rounded-xl bg-slate-50 p-4 inset-shadow-sm">inset-shadow-sm</div>
<svg viewBox="0 0 24 24" class="size-16 fill-amber-400 drop-shadow-xl" aria-hidden="true"><path d="M12 2l3 7h7l-5.5 4.5 2 7.5-6.5-4.5-6.5 4.5 2-7.5L2 9h7z"/></svg>
<svg viewBox="0 0 24 24" class="size-16 fill-amber-400 shadow-xl" aria-hidden="true"><path d="M12 2l3 7h7l-5.5 4.5 2 7.5-6.5-4.5-6.5 4.5 2-7.5L2 9h7z"/></svg>
</div>
</body>
</html>Compare the two stars: drop-shadow-xl follows the star's points, while shadow-xl draws a shadow around the invisible square box of the SVG.
Rings and outlines
A ring is a solid line drawn around an element using a box shadow. Because shadows take up no space, a ring never moves other elements, which makes it perfect for selected states and focus indicators.
ringdraws a 1px ring;ring-2,ring-4are thicker.ring-sky-500colours it.ring-offset-2adds a 2px gap between the element and the ring;ring-offset-whitecolours that gap.inset-ring-*draws the ring inside the element instead.
An outline is the CSS property browsers use for focus by default: outline-2 outline-offset-2 outline-sky-600. Both are usually applied only on focus, using the focus-visible: variant you will meet in Hover, Focus & Other States. Press Tab in this example to move between the buttons:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rings and outlines</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="flex flex-wrap gap-4 p-8">
<button class="rounded-lg bg-sky-600 px-4 py-2 text-white focus-visible:ring-4 focus-visible:ring-sky-300 focus-visible:outline-none">Ring on focus</button>
<button class="rounded-lg bg-emerald-600 px-4 py-2 text-white focus-visible:ring-2 focus-visible:ring-emerald-600 focus-visible:ring-offset-2 focus-visible:outline-none">Ring with offset</button>
<button class="rounded-lg bg-slate-800 px-4 py-2 text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-800">Outline on focus</button>
<div class="rounded-lg bg-white px-4 py-2 ring-2 ring-amber-400">Always-on ring (selected plan)</div>
</body>
</html>Common mistakes
- Borders with no colour. They take the text colour in v4. Add
border-slate-200or similar. - Removing focus styles. Never use
outline-nonewithout adding a visiblefocus-visible:ring or outline in its place; keyboard users need to see where they are. - Borders that shift the layout. Adding
border-2on hover makes the element 4px bigger and nudges its neighbours. Use a ring, or keep a transparent border all the time and only change its colour. - Heavy shadows everywhere. Use small shadows for cards and save
shadow-xlandshadow-2xlfor floating things like menus and dialogs.
Key points
border,border-2and side classes likeborder-b-4set width; always add a colour such asborder-slate-200.rounded-*uses a scale fromrounded-xstorounded-4xl, plusrounded-fullfor pills and circles.divide-yanddivide-xon a parent draw lines between children only.shadow-*lifts elements; add a colour likeshadow-sky-500/30, or useinset-shadow-*anddrop-shadow-*for other effects.- Rings (
ring-2 ring-offset-2) and outlines are ideal for focus indicators because they do not change the layout.
Exercise
Build a pricing card for a coaching centre in Jaipur. The card should have a light border, rounded-2xl corners and a medium shadow. Inside, list three features separated by dividers. Mark the card as the "Most popular" plan with a 2px sky-coloured ring and a 4px offset, and give the "Join now" button a visible ring when it receives keyboard focus.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pricing card</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="bg-slate-50 p-10">
<article class="max-w-xs rounded-2xl border border-slate-200 bg-white p-6 shadow-md ring-2 ring-sky-500 ring-offset-4">
<p class="text-sm font-semibold text-sky-700">Most popular</p>
<h2 class="mt-1 text-2xl font-bold">₹4,999 / month</h2>
<ul class="mt-4 divide-y divide-slate-200">
<li class="py-2">Daily doubt classes</li>
<li class="py-2">Weekly mock tests</li>
<li class="py-2">Printed notes</li>
</ul>
<button class="mt-4 w-full rounded-lg bg-sky-600 py-2 text-white focus-visible:ring-4 focus-visible:ring-sky-300 focus-visible:outline-none">Join now</button>
</article>
</body>
</html>ring-2 ring-sky-500 ring-offset-4 highlights the card without changing its size, divide-y separates the features, and the button replaces the default outline with a clearly visible ring only for keyboard focus.
Quick Quiz
1. In Tailwind v4, what colour is a border if you only write border?
Version 4 uses currentColor for borders by default, so always add a colour class.
2. Why is a ring often better than a border for showing a selected state?
Box shadows take no space in the layout, so adding a ring never shifts nearby elements.
3. Which class adds lines between list items but not after the last one, when placed on the <ul>?
divide-y on the parent adds a top or bottom border between children only.
4. What is the v4 equivalent of the v3 class shadow-sm?
The shadow scale moved down one step in v4, so v3's shadow-sm is now shadow-xs.
Learn Tailwind CSS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
