10 Tailwind CSS patterns we use on every project
After enough projects, you notice the same twenty lines of Tailwind doing most of the heavy lifting. These are the ten patterns we reach for constantly — none of them clever, all of them load-bearing. (Nova itself uses nearly every one; view source and you'll spot them.)
1. The stretched link
Make an entire card clickable without wrapping everything in an anchor (which hurts semantics and screen readers). Put relative on the card and stretch the link's pseudo-element over it:
<article class="group relative rounded-2xl p-6">
<h3>
<a href="/post" class="after:absolute after:inset-0">Post title</a>
</h3>
<p>Excerpt stays plain text, but the whole card is the hit area.</p>
</article>
One real link, full-card click target, clean accessibility tree.
2. Rings instead of borders
border participates in layout; ring is a shadow, so it never shifts anything. For hairline card edges — especially over images or gradients — rings win:
<div class="rounded-2xl bg-white ring-1 ring-zinc-900/5 dark:bg-zinc-800 dark:ring-white/10">
That zinc-900/5 trick — a nearly transparent dark ring — reads as a crisp edge on any background, which is why you see it all over well-made templates.
3. divide-* for lists
Stop putting border-b on items and special-casing the last one. Put dividers on the parent:
<ul class="divide-y divide-zinc-100 dark:divide-zinc-800">
<li class="py-4">…</li>
<li class="py-4">…</li>
</ul>
4. group for coordinated hovers
Hover the card, move the arrow; hover the row, tint the title. group on the parent, group-hover: on any child:
<a class="group flex items-center gap-2">
Read article
<span class="transition-transform group-hover:translate-x-0.5">→</span>
</a>
Small motion, big perceived quality. Its sibling peer does the same for form states.
5. Space through the parent
space-y-* and gap-* mean children never carry their own margins, so components stay reusable in any context. If you're typing mb-4 on five siblings, the parent wants space-y-4 (or better, make it flex/grid with gap-4).
6. The sr-only label
Icon-only buttons still need names. Either an aria-label, or visually hidden text:
<button class="rounded-full p-2.5 ring-1 ring-zinc-900/5">
<span class="sr-only">Toggle dark mode</span>
<svg class="h-5 w-5">…</svg>
</button>
Costs nothing, and it's the difference between "button" and "Toggle dark mode" in a screen reader.
7. Dark mode as paired tokens
Write the pair at the moment you write the class, not in a later "dark pass": bg-white dark:bg-zinc-800, text-zinc-600 dark:text-zinc-400, ring-zinc-900/5 dark:ring-white/10. Once the pairs become muscle memory, dark mode stops being a chore because it's never a separate task.
8. line-clamp for tidy excerpts
Cards stay aligned when text can't run long:
<p class="line-clamp-3 text-sm text-zinc-600 dark:text-zinc-400">…</p>
Pair it with a fixed aspect ratio on the media (aspect-video, aspect-[16/9]) and a grid of mixed-length posts stays perfectly ranked.
9. Sticky sidebars, the non-broken way
sticky fails silently when the element stretches to its container's height — there's nowhere to stick. The fix is self-start:
<aside class="lg:sticky lg:top-8 lg:self-start">…</aside>
self-start collapses the sidebar to its natural height so it can travel. This one line has saved us hours across projects.
10. tabular-nums for anything that counts
Proportional digits wobble: timers jitter, price columns won't align, "12" is narrower than "44". One utility fixes it:
<td class="text-right tabular-nums">1,204.00</td>
Use it on tables, stats, countdowns — anywhere digits change or line up.
The meta-pattern
Notice what these have in common: each encodes a decision — how cards click, how edges draw, how space flows, how themes pair — into a few characters that travel with the markup. That's Tailwind's real value on a team. The utilities aren't the design; they're how the design stops drifting. Steal all ten, and your next project starts 80% decided.