Forms and Accessible UI Components
Build practical inputs, buttons, cards, navigation, dialogs, and accessible feedback patterns.
Inputs, labels, and validation
A polished form combines visual styling with native semantics. Use a real <label> connected to the input, a suitable input type, and clear helper or error text. Tailwind can style focus, invalid, disabled, and placeholder states, but it does not validate business rules or announce errors by itself.
Do not rely on placeholder text as the only label: it disappears when users type and may have poor contrast. Provide a visible label and make required fields and error messages clear.
<div>
<label for="task-title" class="block text-sm font-medium text-slate-800">Task title</label>
<input id="task-title" name="title" required minlength="3"
aria-describedby="task-title-help"
class="mt-1.5 w-full rounded-lg border border-slate-300 px-3 py-2.5 text-slate-900 placeholder:text-slate-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
placeholder="e.g. Review pull request">
<p id="task-title-help" class="mt-1.5 text-sm text-slate-600">Use at least three characters.</p>
</div>for and id connect the label to the input. Native required and minlength constraints provide baseline validation. In a full form, connect any error text and update it when validation runs.Why should an input have a programmatically associated label?
Dialogs, overlays, and drawers
A dialog is more than a positioned box. It needs a sensible focus strategy, a way to close, keyboard behavior, and an appropriate accessible name. Use native <dialog> where it fits or a well-tested accessible dialog primitive. Tailwind handles the visual overlay and panel, but it does not trap focus or manage Escape behavior automatically.
Watch for clipping from ancestor overflow-hidden and stacking-context issues. A portal can help place overlays near the document root, but the component's behavior still needs to be implemented correctly.
A modal overlay that looks correct but allows keyboard focus to escape or leaves background controls interactive is incomplete. Use a tested accessible primitive or implement the full interaction contract.
<div class="fixed inset-0 z-50 grid place-items-center bg-slate-950/50 p-4">
<section role="dialog" aria-modal="true" aria-labelledby="confirm-title"
class="w-full max-w-md rounded-2xl bg-white p-6 shadow-2xl">
<h2 id="confirm-title" class="text-lg font-semibold text-slate-900">Delete this task?</h2>
<p class="mt-2 text-sm leading-6 text-slate-600">This action cannot be undone.</p>
<div class="mt-6 flex justify-end gap-3">
<button class="rounded-lg border px-4 py-2 font-medium">Cancel</button>
<button class="rounded-lg bg-red-600 px-4 py-2 font-semibold text-white">Delete task</button>
</div>
</section>
</div>