Tailwind CSS changes how you write styles — but most developers stop at the basics and miss the features that make it genuinely fast to use. These 12 tips come from daily use on production projects, covering patterns that experienced developers rely on.
1. Arbitrary Values for One-Off Styles
When a design calls for a value not in the default scale, use bracket notation instead of writing custom CSS:
<div className="top-[117px] w-[calc(100%-2rem)] bg-[#1a1a2e]">
content
</div>Arbitrary values work for any utility — colours, spacing, widths, transforms, grid templates. They are not a workaround; they are a first-class Tailwind feature.
2. The group and group-hover Pattern
Style a child element based on the hover state of a parent:
<div className="group">
<img className="group-hover:scale-105 transition-transform" />
<span className="opacity-0 group-hover:opacity-100 transition-opacity">
View Project
</span>
</div>group-focus, group-active, and group-has-[...]also exist. Named groups (group/card) let you nest multiple group contexts.
3. peer for Sibling State Styling
Style an element based on a sibling's state — perfect for custom checkboxes, radio buttons, and input labels:
<input type="checkbox" className="peer hidden" id="toggle" />
<label
htmlFor="toggle"
className="peer-checked:bg-blue-500 peer-checked:text-white"
>
Enable notifications
</label>4. @apply for Reusable Component Classes
When you find yourself repeating the same long class string across many elements, extract it:
/* globals.css */
@layer components {
.btn-primary {
@apply px-6 py-3 rounded-xl font-semibold text-white bg-blue-500
hover:bg-blue-600 transition-colors focus:outline-none
focus:ring-2 focus:ring-blue-500 focus:ring-offset-2;
}
}Use sparingly — the whole point of Tailwind is utility classes in the markup. Reserve@apply for truly reused patterns, not one-offs.
5. CSS Variables for Dynamic Values
Arbitrary values support CSS custom properties for runtime-dynamic styles:
<div
className="bg-[var(--accent-color)]"
style={{ '--accent-color': userColor } as React.CSSProperties}
>
Dynamic accent
</div>6. The divide Utilities
Instead of adding border-b to every list item except the last, usedivide on the container:
<ul className="divide-y divide-white/10">
{items.map((item) => <li key={item.id}>{item.label}</li>)}
</ul>divide-x works for horizontal dividers. divide-opacity-* for translucent dividers.
7. Responsive Variants Are Mobile-First
Tailwind's breakpoint prefixes mean “at this width and above” — not “at exactly this width.” Unprefixed classes are the mobile base:
<div className="text-sm md:text-base lg:text-lg">
{/* sm on mobile, base on md+, lg on lg+ */}
</div>A common mistake: applying hidden on desktop with lg:hidden also hides on mobile. Use lg:block on the element you want to appear only on desktop.
8. The not- Variant
Apply styles when a condition is not met:
/* Apply padding to all li except the first */
<li className="not-first:pt-4">item</li>
/* Style placeholder text */
<input className="not-placeholder-shown:border-blue-500" />9. Line Clamping
Truncate multiline text to a fixed number of lines — essential for card excerpts:
<p className="line-clamp-3">{excerpt}</p>This applies -webkit-line-clamp and the associated display/overflow properties. Works in all modern browsers without a plugin.
10. The container Query Plugin
With @tailwindcss/container-queries, you can style elements based on their parent container size rather than the viewport:
<div className="@container">
<div className="@lg:grid-cols-2 grid-cols-1 grid">
{/* 2 columns when container is large, 1 when small */}
</div>
</div>11. Print Utilities
Style elements specifically for print without a separate stylesheet:
<nav className="print:hidden">navigation</nav>
<footer className="print:block hidden">print footer</footer>12. Avoid the Specificity Trap
The most common advanced mistake: adding custom CSS that tries to override Tailwind, then adding !important to win specificity battles. This creates unmaintainable code.
The correct approach: use Tailwind's built-in ! prefix for important utilities (!text-white), or restructure the component so the override is not needed. If you find yourself fighting specificity, the architecture needs rethinking.
Tailwind's power compounds with familiarity. The developers who know these patterns build interfaces at a pace that feels unfair to those writing traditional CSS. The investment in learning the full API pays back on every project.