Spacing and sizing

Borders, radius, and focus rings

Add borders and rounded corners while using ring and outline utilities to keep keyboard focus visible.

Borders in Tailwind use pieces you already know from CSS:

<div class="rounded-md border border-gray-300 p-4">...</div>

border sets a 1px solid border. border-gray-300 sets its color. rounded-md rounds the corners. In the generated CSS these are border-width, border-color, and border-radius, nothing more.

A border adds to the box size. A radius only clips the visual corner. Pick your radius from the design once, and reuse it. Don’t put rounded-* on every element by reflex, or adjacent controls end up looking like separate pills instead of one group.

Focus rings

Rings and outlines draw around a control without changing its layout. That’s what makes them right for focus states. Here’s a button with a visible keyboard focus:

<button class="rounded-md bg-blue-600 px-4 py-2 text-white
               focus-visible:outline-2 focus-visible:outline-offset-2
               focus-visible:outline-blue-600">
  Save
</button>

Click the button with a mouse and nothing extra appears. Press Tab to reach it and a 2px blue outline shows up, offset 2px from the edge. That’s the focus-visible behavior: browsers show it for keyboard focus and hide it for pointer clicks.

Never remove the browser’s default outline unless you replace it with something just as clear. outline-none without a replacement makes your site unusable for keyboard users.

Check every surface

A 2px blue outline disappears on a blue panel. Test the indicator in dark mode and on error backgrounds. The outline-offset-2 creates a gap that helps, and a contrasting color or a ring-* in a second color makes the state visible everywhere.

Color alone is not enough

An invalid input shouldn’t rely on a red border. Add a text message and connect it with aria-describedby. A disabled button needs the real disabled attribute, not just opacity-50. Opacity changes how it looks. It doesn’t stop clicks.

When a focus style doesn’t show, first check that the element can actually receive focus. A <div> can’t unless you add tabindex. Then check that the variant matches. DevTools shows you whether the rule is a border, an outline, or a box-shadow ring.

Try this: build a button and an invalid input. Press Tab through them in light and dark themes, zoom to 200%, and confirm focus and error states stay visible without depending on color.

Lesson completed