Tailwind Utilities
Core utilities cheatsheet — the most-used classes and what changed in v4.
Recipe
Quick-reference recipe card — copy-paste ready.
// Layout
<div className="flex items-center justify-between gap-4">
<div className="grid grid-cols-3 gap-6">
<div className="absolute top-0 right-0">
<div className="sticky top-4">
// Sizing
<div className="h-screen w-full max-w-lg min-h-0">
<div className="size-12"> {/* width AND height */}
// Spacing
<div className="p-4 px-6 py-2 pt-8">
<div className="m-auto mx-4 my-2 mt-0">
<div className="space-y-4"> {/* children gap */}
// Typography
<p className="text-sm font-medium leading-tight tracking-wide">
<p className="text-gray-600 text-balance truncate">
<p className="line-clamp-3 antialiased">
// Colors
<div className="bg-blue-500 text-white border-gray-200">
<div className="bg-blue-500/50"> {/* 50% opacity */}
// Borders & Radius
<div className="rounded-lg border border-gray-200 shadow-md">
<div className="ring-2 ring-blue-500 ring-offset-2">
<div className="divide-y divide-gray-100">
// Interactivity
<button className="cursor-pointer hover:bg-blue-600 focus:outline-none focus-visible:ring-2">
<div className="pointer-events-none select-none">When to reach for this: Every time you write a component. These cover 90% of styling needs.
Working Example
export function ProductCard({
name,
price,
image,
badge,
}: {
name: string;
price: number;
image: string;
badge?: string;
}) {
return (
<article className="group relative overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-shadow hover:shadow-lg">
{/* Image */}
<div className="aspect-square overflow-hidden">
<img
src={image}
alt={name}
className="size-full object-cover transition-transform duration-300 group-hover:scale-105"
/>
</div>
{/* Badge */}
{badge && (
<span className="absolute top-3 left-3 rounded-full bg-blue-500 px-2.5 py-0.5 text-xs font-semibold text-white">
{badge}
</span>
)}
{/* Content */}
<div className="space-y-1.5 p-4">
<h3 className="truncate text-sm font-medium text-gray-900">{name}</h3>
<p className="text-lg font-bold text-gray-900">
${price.toFixed(2)}
</p>
<button className="mt-2 w-full rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:scale-[0.98]">
Add to Cart
</button>
</div>
</article>
);
}What this demonstrates:
group+group-hover:*for parent-child hover effectsaspect-squarefor maintaining image ratiosize-full(shorthand forw-full h-full)transition-*for smooth hover effectsfocus-visible:for keyboard-only focus ringsactive:scale-[0.98]for press feedbacktruncatefor text overflow
Deep Dive
How It Works
- Tailwind scans your source files for class names and generates only the CSS you use
- Each utility maps to one or a few CSS properties (e.g.,
p-4=padding: 1rem) - Arbitrary values use bracket syntax:
w-[347px],bg-[#1da1f2],grid-cols-[200px_1fr] - Modifiers chain left-to-right:
dark:hover:bg-blue-600= dark mode + hover - v4 uses the modern CSS stack —
@layer,@property, native nesting
Variations
New in v4 — key changes:
/* 1. inset-shadow and inset-ring (replaces box-shadow hacks) */
.card { @apply inset-shadow-sm inset-ring inset-ring-gray-200; }
/* 2. Color opacity uses slash syntax (same as v3) */
.overlay { @apply bg-black/50; }
/* 3. not-* variant */
.item { @apply not-last:border-b; }
/* 4. in-* variant for parent state */
.child { @apply in-[.open]:block; }
/* 5. New size-* utilities */
.avatar { @apply size-10; /* = w-10 h-10 */ }
/* 6. text-balance and text-pretty */
.heading { @apply text-balance; }
.paragraph { @apply text-pretty; }Flexbox patterns:
// Centered content
<div className="flex min-h-screen items-center justify-center">
// Sidebar layout
<div className="flex">
<aside className="w-64 shrink-0" />
<main className="min-w-0 flex-1" />
</div>
// Space between with wrap
<div className="flex flex-wrap items-center justify-between gap-4">
// Stack with auto-margin push
<nav className="flex flex-col">
<a>Home</a>
<a>About</a>
<a className="mt-auto">Settings</a> {/* pushed to bottom */}
</nav>Grid patterns:
// Auto-fill responsive grid
<div className="grid grid-cols-[repeat(auto-fill,minmax(250px,1fr))] gap-4">
// Named areas
<div className="grid grid-cols-[250px_1fr] grid-rows-[auto_1fr_auto] [grid-template-areas:'sidebar_header''sidebar_main''sidebar_footer']">
// Subgrid (v4)
<div className="grid grid-cols-subgrid col-span-3">TypeScript Notes
// Type-safe utility composition with cva or clsx
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs: (string | undefined | false | null)[]) {
return twMerge(clsx(inputs));
}
// Usage
<div className={cn(
"rounded border p-4",
isActive && "border-blue-500 bg-blue-50",
disabled && "opacity-50 pointer-events-none",
)} />Gotchas
-
Class order does not determine specificity —
p-4 p-2does not guaranteep-2wins. Both have equal specificity; the CSS source order decides. Fix: Usetailwind-mergeto resolve conflicts:twMerge("p-4 p-2")returns"p-2". -
Arbitrary values need correct type hints —
bg-[--brand]is ambiguous. Fix: Usebg-[color:--brand]to tell Tailwind it is a color value. -
min-w-0on flex children — Flex children default tomin-width: auto, causing overflow. Fix: Addmin-w-0to flex children that should shrink. -
truncateneeds a width constraint —truncatewithout a parent width does nothing. Fix: Ensure the element or a parent has a defined width ormax-width. -
space-y-*with conditionally rendered children — Hidden children still get margins. Fix: Usegap-*with flex/grid instead.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Inline styles | One-off dynamic values (e.g., style={{ width: computed }}) | You have static, repeating patterns |
| CSS Modules | You need scoped CSS for a component library | You prefer utility-first workflow |
| Styled Components | You want CSS-in-JS with dynamic theming | You want zero runtime CSS |
| Panda CSS | You want type-safe utility tokens with zero runtime | You prefer Tailwind's class-based DX |
FAQs
What does the size-12 utility do?
It sets both width and height to 3rem — a shorthand for w-12 h-12.
How do you apply 50% opacity to a background color?
Use the slash syntax: bg-blue-500/50. The number after / is the opacity percentage.
What is the group / group-hover: pattern used for?
- Add
groupto a parent element - Use
group-hover:*on children to style them when the parent is hovered - Useful for card hover effects where multiple children change at once
Why should you use focus-visible: instead of focus:?
focus-visible: only triggers on keyboard focus, not mouse clicks. This avoids unwanted focus rings after clicking a button.
How do you write an arbitrary value for a utility that Tailwind does not include?
Use bracket syntax: w-[347px], bg-[#1da1f2], grid-cols-[200px_1fr].
Gotcha: You wrote p-4 p-2 expecting p-2 to win. Why might it not?
Class order in the className string does not determine CSS specificity. Both have equal specificity, so CSS source order decides. Use twMerge("p-4 p-2") to reliably resolve to p-2.
Gotcha: truncate is on an element but text is not being truncated. Why?
truncate requires the element or a parent to have a defined width or max-width constraint. Without one, there is nothing to truncate against.
Why should you add min-w-0 to flex children?
Flex children default to min-width: auto, which prevents them from shrinking below their content size. min-w-0 allows proper shrinking and prevents overflow.
What is the cn() utility and how is it typed in TypeScript?
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs: (string | undefined | false | null)[]) {
return twMerge(clsx(inputs));
}It merges conditional class names and resolves Tailwind conflicts.
What are text-balance and text-pretty used for in v4?
text-balance— balances line lengths in headings so no line is much shorter than anothertext-pretty— prevents orphaned words at the end of paragraphs
How does modifier chaining work (e.g., dark:hover:bg-blue-600)?
Modifiers chain left-to-right. dark:hover:bg-blue-600 means "in dark mode, on hover, apply bg-blue-600."
What TypeScript pattern ensures safe conditional class composition?
<div className={cn(
"rounded border p-4",
isActive && "border-blue-500 bg-blue-50",
disabled && "opacity-50 pointer-events-none",
)} />Falsy values are filtered out by clsx before twMerge resolves conflicts.
Related
- Setup — v4 installation and configuration
- Responsive — breakpoints and container queries
- Dark Mode — dark mode utilities
- Custom Utilities — creating your own utilities