Accessibility is not a topping you sprinkle on at the end. It grows out of a component's structure. Those who use semantic HTML, make focus visible and take the keyboard seriously build interfaces that work for more people. This guide shows the most important building blocks with React and Tailwind CSS.
- Accessibility starts with the right element. A button is a button, not a div with onClick.
- Everything that works with a mouse has to work with a keyboard. That is the fastest test.
- The focus ring is not a blemish. Removing it takes away people's orientation.
- Contrast is measurable: 4.5 to 1 for body text, 3 to 1 for large type and controls.
Start with semantic HTML
The right element is half the battle. A button is a button, not a div with onClick. Native elements come with focus behavior, keyboard operation and the right role for screen readers built in. That saves you a lot of manual follow-up work.
Structure matters too. Headings in a logical order, nav for navigation, main for the main content and lists for enumerations give assistive technologies a map of your page. In React that costs nothing extra, you simply choose the fitting tag.
- No focus, the keyboard never reaches it.
- Enter and space do nothing.
- The screen reader announces: group.
- You rebuild role, state and keyboard by hand.
- Focus, tab order and ring are there.
- Enter and space trigger it.
- The screen reader announces: button.
- All you write is what should happen.
Make focus visible
Those navigating by keyboard must see where they currently are. So never remove the focus ring without a replacement. In Tailwind you control it deliberately through focus-visible, so it appears on keyboard use but does not disturb on mouse clicks.
A class like focus-visible with ring-2 and a clear ring color is often enough. Make sure there is enough contrast to the background so the state stays clear even on light surfaces. A clearly visible focus is a sign of care, not a flaw.
<button className="rounded-lg bg-neutral-900 px-5 py-3 text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900 focus-visible:ring-offset-2" Termin buchen</button>
Use ARIA only when needed
ARIA attributes add meaning that does not come from the HTML alone. But the first rule is: no ARIA is better than wrong ARIA. Where a native element suffices, you need no extra role.
ARIA becomes useful for custom widgets. A toggle gets aria-pressed, an expandable menu aria-expanded, a purely decorative icon aria-hidden. For buttons without visible text, aria-label provides an understandable label. Set these attributes deliberately and keep them in sync with your component's actual state.
<button aria-pressed={aktiv} className={aktiv ? "bg-neutral-900 text-white" : "bg-neutral-100"} Wöchentlich</button>
Consider keyboard operation
Everything that works with the mouse should also work with the keyboard. Native buttons and links already respond to Enter and Space. If you build interactive elements yourself, you have to provide these keys and a sensible tab order on your own.
For overlays like dialogs, focus comes into play. On opening it moves into the dialog, on closing back to the triggering element, and while it is open it stays trapped inside the dialog. This focus management decides whether a component is truly operable.
Check contrast and colors
Text must stand out clearly from the background. As a guideline, at least 4.5 to 1 for normal text and 3 to 1 for large type. Very light grays on white look elegant but often fail this test.
Color should also never be the only carrier of information. An error state needs a word or an icon next to the red, an active link more than just a different color. That keeps your interface understandable for people with limited color vision too.
Account for images and motion
Every content-carrying image needs a short, fitting alternative in the alt attribute. Purely decorative images get an empty alt so screen readers skip them and do not interrupt the reading flow with decoration.
Motion should be possible to turn off. Through prefers-reduced-motion and the hook useReducedMotion you offer a calm variant. Together with good contrast and clear structure your component becomes usable for many more people.
The five minute test
You do not need an audit tool to find the obvious problems. These four moves surface most of them.
- Put the mouse away. Move through the page with Tab. Can you reach everything, and can you always see where you are?
- Zoom to 200 percent. Does the layout break, does text overlap, does something disappear? Zoom is not an edge case, many people browse that way all the time.
- Check the colors. Grey text on a light background is the classic. 4.5 to 1 for body text, 3 to 1 for large type.
- Turn off images. What remains is what a screen reader hears. Decoration needs an empty alt, content needs a short description.
Accessible does not mean plainer. It means the design still holds when someone operates it differently.
Where Tailwind helps and where it does not
Tailwind does not make a page accessible, but it makes the right things convenient. focus-visible is its own variant, sr-only ships as a utility, and tokens keep contrast in one place.
What Tailwind does not do for you: pick the right element, write labels, announce state. A div with a hover class looks like a button and still is not one.







