Utility classes make it easy to build something that looks finished but cannot be used with a keyboard, a screen reader or a reduced-motion setting. Tailwind ships the variants needed to fix each problem; the work is knowing which to reach for. In this lesson you will learn the focus-ring pattern, text for assistive technology, contrast and motion preferences, and touch target sizing.
Every interactive element must show where keyboard focus is. The browser outline does this but also appears on mouse clicks, so teams remove it — and forget to replace it. focus-visible: applies only when the browser decides focus should be shown:
<button class="rounded-md bg-blue-600 px-4 py-2 text-white
focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2">
Continue
</button>
<input class="rounded-md border px-3 py-2 focus:border-blue-500 focus:outline-hidden focus:ring-2 focus:ring-blue-500/30" />Use focus-visible: for buttons and links and plain focus: for text inputs, where a ring on click helps. Prefer outline-hidden over outline-none: both hide the outline, but outline-hidden keeps a transparent one that reappears in Windows High Contrast mode. Never remove focus styles without adding a ring.
A skip link lets keyboard users jump past the navigation: hidden with sr-only, revealed on focus with focus:not-sr-only, as in the sample code.
Icons, color and position carry meaning assistive technology cannot see. Provide it as text:
<!-- Icon-only button -->
<button class="p-2">
<svg class="size-5" aria-hidden="true">...</svg>
<span class="sr-only">Close dialog</span>
</button>
<!-- Status conveyed by color and by text -->
<span class="inline-flex items-center gap-1 text-sm text-green-700">
<span class="size-2 rounded-full bg-green-500" aria-hidden="true"></span>
Online
</span>sr-only positions text off-screen while keeping it in the accessibility tree; aria-hidden="true" removes decorative graphics from it. Keep visual and announced state in sync by styling from the attribute — aria-expanded:rotate-180, aria-selected:bg-blue-50 — instead of toggling separate classes.
Text needs a contrast ratio of at least 4.5:1 against its background (3:1 for large text). In the default palette, gray-500 on white is borderline; gray-600 passes, and every dark: pair needs checking again. Two variants adapt to users who have asked for more: contrast-more: when the OS requests higher contrast, and forced-colors: in Windows High Contrast mode.
<p class="text-gray-600 contrast-more:text-gray-950">Helper text</p>
<div class="bg-blue-600 text-white forced-colors:border forced-colors:border-current">Card</div>In forced-colors mode the browser replaces your colors with the system palette, so anything defined only by background disappears; a border under forced-colors: keeps the shape visible.
Three more preferences are one variant each:
<div class="motion-safe:animate-bounce motion-reduce:animate-none">Scroll hint</div>
<button class="min-h-11 min-w-11 p-2 pointer-coarse:p-3">Tap me</button>
<h2 id="pricing" class="scroll-mt-24">Pricing</h2>motion-safe: and motion-reduce: respect the reduced-motion setting; put animations behind motion-safe: by default.min-h-11 min-w-11. pointer-coarse: (v4.1+) adds padding only for touch input.scroll-mt-* offsets anchor targets so a sticky header does not cover them, and motion-safe:scroll-smooth on <html> animates the jump only when allowed.outline-none on everything, which removes focus for keyboard users and for High Contrast mode.hover: alone to reveal controls; pair it with focus-within: or focus-visible:.text-gray-400 for body text; it fails contrast on white.Why is `outline-hidden` preferred over `outline-none` when adding a custom focus ring?
focus-visible:ring-2 plus outline-hidden, never with outline-none alone.sr-only text and mark decorative graphics aria-hidden; style state from aria-* attributes.contrast-more: and forced-colors: to adapt to user settings.motion-safe:, size targets with min-h-11 min-w-11, and offset anchors with scroll-mt-*.sr-only focus:not-sr-only with positioning applied on focus.Next lesson: Tailwind with React and Vue: clsx, tailwind-merge and cva — manage conditional classes and component variants in JavaScript frameworks.