Tailwind CSS is a utility-first CSS framework. Instead of writing custom CSS, you apply small, single-purpose utility classes directly in your HTML. Every class does exactly one thing — and knowing the most-used classes by heart dramatically speeds up development.
What makes Tailwind different
Traditional CSS frameworks give you pre-designed components (buttons, cards, navbars). Tailwind gives you low-level building blocks that you compose into anything.
<!-- Traditional framework -->
<button class="btn btn-primary btn-lg">Click me</button>
<!-- Tailwind -->
<button class="bg-blue-600 hover:bg-blue-700 text-white font-semibold px-6 py-3 rounded-xl transition-colors">
Click me
</button>
The Tailwind approach eliminates naming decisions, keeps styles with the markup, and makes it impossible to accidentally break other components when changing styles.
Most-used classes by category
Display and layout
block → display: block
inline-block → display: inline-block
flex → display: flex
grid → display: grid
hidden → display: none
relative → position: relative
absolute → position: absolute
fixed → position: fixed
sticky → position: sticky
Flexbox essentials
flex-row → flex-direction: row
flex-col → flex-direction: column
flex-wrap → flex-wrap: wrap
items-center → align-items: center
items-start → align-items: flex-start
items-end → align-items: flex-end
justify-center → justify-content: center
justify-between → justify-content: space-between
justify-end → justify-content: flex-end
gap-4 → gap: 16px
flex-1 → flex: 1 1 0%
flex-none → flex: none
Grid
grid-cols-1 → grid-template-columns: repeat(1, minmax(0, 1fr))
grid-cols-2 → repeat(2, ...)
grid-cols-3 → repeat(3, ...)
col-span-2 → grid-column: span 2 / span 2
col-span-full → grid-column: 1 / -1
gap-4 → gap: 16px
Spacing (1 unit = 4px)
p-0 = 0px p-1 = 4px p-2 = 8px
p-3 = 12px p-4 = 16px p-5 = 20px
p-6 = 24px p-8 = 32px p-10 = 40px
p-12 = 48px p-16 = 64px p-20 = 80px
The same scale applies to: px (horizontal), py (vertical), pt/pr/pb/pl (individual sides), and all m-* margin equivalents.
Typography
text-xs = 12px text-sm = 14px
text-base = 16px text-lg = 18px
text-xl = 20px text-2xl = 24px
text-3xl = 30px text-4xl = 36px
text-5xl = 48px text-6xl = 60px
font-normal = 400 font-medium = 500
font-semibold = 600 font-bold = 700
leading-none = 1 leading-tight = 1.25
leading-normal = 1.5 leading-loose = 2
tracking-tight = -0.05em tracking-wide = 0.025em
Border radius
rounded-none = 0 rounded-sm = 2px
rounded = 4px rounded-md = 6px
rounded-lg = 8px rounded-xl = 12px
rounded-2xl = 16px rounded-3xl = 24px
rounded-full = 9999px
Colors
Tailwind ships 22 color scales (slate, gray, zinc, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose) each with 11 shades (50 through 950).
text-blue-600 → color: #2563eb
bg-blue-50 → background-color: #eff6ff
border-blue-200 → border-color: #bfdbfe
ring-blue-500 → --tw-ring-color: #3b82f6
Responsive design
Tailwind is mobile-first. Unprefixed utilities apply at all screen sizes. Prefixed utilities apply from that breakpoint up:
| Prefix | Min-width |
|---|---|
sm: |
640px |
md: |
768px |
lg: |
1024px |
xl: |
1280px |
2xl: |
1536px |
Example: class="flex-col md:flex-row" — column on mobile, row on tablet and up.
State variants
hover:bg-blue-700 → on mouse hover
focus:ring-2 → on keyboard focus
active:scale-95 → on click
disabled:opacity-50 → when disabled
dark:bg-gray-900 → in dark mode
group-hover:opacity-100 → when parent .group is hovered
Tailwind CSS v4 changes
Tailwind v4 introduced significant changes:
- Configuration moves to CSS using
@theme— no moretailwind.config.jsrequired - New Oxide engine delivers up to 5× faster build times
- Native CSS cascade layers
color-mix()based opacity modifiers- New 3D transform utilities:
rotate-x-*,rotate-y-*,perspective-* starting:variant for entry animations
How to use the Tailwind Cheatsheet
- Go to Tailwind CSS Cheatsheet
- Type any class name or CSS property in the search box
- Press
/anywhere on the page to focus search instantly - Click any class card to copy it to clipboard
- Filter by category to browse related utilities
- Toggle v3/v4 to see version-specific changes
Class strings must be complete at build time
Tailwind scans source files for class names as plain text. It does not evaluate your code, so any class assembled at runtime is never generated:
// broken — Tailwind never sees "text-red-500"
<div className={`text-${color}-500`}>
// correct — full class names present in source
const colors = { red: 'text-red-500', blue: 'text-blue-500' }
<div className={colors[color]}>
This is the single most common Tailwind bug, and it is confusing because the class appears correctly in the DOM while no CSS exists for it. If a style works in development and vanishes in production, or works for some values and not others, this is why.
The rule: every class name must appear as a complete, literal string somewhere in a scanned file.
Conflicting classes and the merge problem
Tailwind does not resolve conflicts by class order in the attribute — output order in the generated stylesheet decides. class="p-4 p-8" produces an unpredictable winner.
This matters for components accepting a className prop, where a caller's override may lose to the component's default:
// caller's p-8 may not win
<Button className="p-8" /> // Button has p-4 internally
The standard fix is tailwind-merge, which understands which utilities conflict and keeps the last:
import { twMerge } from 'tailwind-merge'
twMerge('p-4', 'p-8') // -> 'p-8'
Combined with clsx for conditionals, this is the near-universal pattern in Tailwind component libraries.
Arbitrary values
For one-off values that do not fit the scale, square brackets work:
<div class="top-[117px] bg-[#1da1f2] grid-cols-[1fr_500px_2fr]">
Useful for matching a design exactly or a third-party widget's dimensions. Use sparingly — the point of a design system is a constrained scale, and a codebase full of arbitrary values has abandoned it.
Extracting components, not @apply
@apply looks like the answer to repetition but usually is not. It moves the styles back into a stylesheet, which reintroduces the naming and dead-code problems Tailwind exists to avoid, and it defeats the utility-first workflow.
The idiomatic solution is a component:
function Button({ className, ...props }) {
return <button className={twMerge('rounded px-4 py-2 font-medium', className)} {...props} />
}
The abstraction lives where abstractions belong — in the component layer — rather than in a parallel CSS naming scheme.
Dark mode
Two strategies, and the choice is hard to reverse later:
media— follows the OS preference automatically. No JavaScript, no toggle.class— activates on adarkclass on<html>. Requires JavaScript but allows a user-controlled toggle.
Most products want class, since users expect a manual override. Applying it before first paint avoids a flash of the wrong theme, which usually means a small inline script in <head> rather than a React effect.
Frequently asked questions
Does Tailwind produce large CSS files?
No. Only classes found in your source are generated, so production stylesheets are typically small and stop growing once the design system stabilises — unlike a traditional stylesheet that accumulates.
Is long class markup a performance problem?
Not meaningfully. HTML gzips extremely well and repeated class strings compress to almost nothing. It is a readability trade-off, not a performance one.
How do I handle hover on touch devices?
hover: styles can stick on touch. Tailwind's default hover behaviour only applies where hover is genuinely supported, which handles most cases automatically.
Can Tailwind coexist with existing CSS?
Yes. A prefix option avoids class collisions, and Tailwind's preflight reset can be disabled if it conflicts with existing base styles.
What changed in v4?
Configuration moved into CSS with @theme rather than a JavaScript config file, and the engine is substantially faster. Most utility class names are unchanged, so markup largely carries over.