This is part 4 of the Tailwind CSS series. The page has a grid of cards from part 3. Now we will make the interactive parts feel finished: buttons that respond to the mouse and the keyboard, a pricing card that stands out, and the question every Tailwind project eventually faces, which is how to avoid repeating a long class list.
Buttons are where most beginners either ignore accessibility or drown in CSS. Tailwind handles both well, provided you remember two rules. A button needs a visible change on hover, and it needs a visible focus ring for keyboard users. The second rule is the one people forget, so we will do it first.
Give a button hover and focus states
Add this button under the grid. Every class is a state or a style, and nothing else is needed:
<button class="rounded-md bg-teal-600 px-4 py-2 font-medium text-white transition hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-teal-400 disabled:opacity-50">
Save changes
</button>
Walk through the states. hover:bg-teal-700 darkens the button under the pointer. focus:ring-2 draws a ring when the button is reached with the Tab key, which is how keyboard users see where they are. The ring is teal, so it matches the brand. disabled:opacity-50 fades the button when it cannot be pressed. And transition softens the colour change so it does not snap.
Try it: press Tab until the button is selected. If you see a ring, the focus state works. If you cannot see one, check that you did not remove the focus:ring-2 class, because removing the outline without adding a ring is the most common accessibility mistake in this kind of code.
Build a pricing card that stands out
A pricing card is a good test of everything so far. It needs a width cap, a border that highlights the recommended plan, a large price with a muted period, a list of features and a button. Here is one, styled to be highlighted:
<div class="max-w-xs rounded-2xl border-2 border-teal-500 bg-white p-8 shadow-lg">
<p class="text-sm font-semibold uppercase tracking-wide text-teal-700">Pro</p>
<p class="mt-4 text-4xl font-bold text-slate-900">$12
<span class="text-base font-normal text-slate-500">/ month</span>
</p>
<ul class="mt-6 space-y-3 text-slate-600">
<li>Unlimited projects</li>
<li>Priority support</li>
</ul>
<button class="mt-8 w-full rounded-md bg-teal-600 py-2 font-medium text-white transition hover:bg-teal-700">Start trial</button>
</div>
Two details are worth noticing. space-y-3 spaces the list items without adding a margin to each one, which keeps the markup clean. w-full stretches the button to fill the card. Each would need its own rule in plain CSS, and here they are one class each.
When to stop repeating and make a component
The pricing card has more than twenty classes. That is fine the first time, but if you need three of them, copying the list three times is how bugs get in: you fix one copy and forget the others. The fix is a component. In a framework such as React or Vue, render the card from a function and pass in the plan name, price and features. In plain HTML, you can keep the markup in one template and generate the copies with a small script.
Avoid @apply for everyday work. It lets you move a class list into your stylesheet under a new name, which sounds tidy, but it recreates the separate stylesheet you were trying to get away from. Reach for it only when you cannot pass classes at all, such as in a CMS field that only accepts a fixed class name.
Keyboard users deserve the same care
Many people never touch a mouse. They move through a page with the Tab key, and they need to know which element is active. The focus ring from earlier is that signal. Browsers draw a default outline, and Tailwind’s focus:outline-none removes it. That is only acceptable when you replace it with something visible, which is why the button pairs focus:outline-none with focus:ring-2.
Make the same check on every interactive element you add: links, inputs, cards that act as buttons. If you can tab to it and cannot see where you are, the element needs a focus style. It takes a minute and it is one of the most common fixes an accessibility audit asks for.
Keep going in the series
This series builds one project, so each part picks up where the last one stopped. Here are the other parts:
- Part 1: Set up Tailwind CSS v4 with Vite
- Part 2: Build your first card with utility classes
- Part 3: Responsive layouts with grid and breakpoints
- Part 5: Dark mode and a brand theme
Frequently asked questions
Why does my focus ring not show?
Check that you have focus:ring-2 and not only focus:outline-none. Removing the outline without adding a ring hides the focus from keyboard users. Tab through the page to confirm the ring appears.
Should I use hover styles on touch screens?
Tailwind wraps hover: in a hover media query, so touch devices do not get a stuck hover state after a tap. Keep hover for decoration and make sure the important feedback also works on focus and active.
How do I make a highlighted card that does not look cramped?
Give it more padding than its neighbours, a slightly thicker border in the brand colour, and a larger shadow. Keep the text size the same so the highlight comes from the frame, not from shouting.

