On this page

Essential Utilities

14 min read TextCh. 1 — Utility-first fundamentals

Essential Utilities

Tailwind CSS has hundreds of utility classes, but a small core set covers roughly 80% of real-world styling needs. This lesson walks through each category with practical examples and explains how they map to CSS properties.

Spacing: padding and margin

Tailwind uses a spacing scale built on multiples of 0.25rem (4px at the default 16px font size). The scale runs from 0 through 96, with each number representing n × 0.25rem.

Class CSS value
p-0 padding: 0
p-1 padding: 0.25rem (4px)
p-4 padding: 1rem (16px)
p-8 padding: 2rem (32px)
p-16 padding: 4rem (64px)

Use p for all sides, px/py for horizontal/vertical axes, and pt/pr/pb/pl for individual sides. The same m prefix works for margins, and mx-auto centers block elements.

<!-- Padding examples -->
<div class="p-4">All sides 1rem</div>
<div class="px-6 py-3">Horizontal 1.5rem, vertical 0.75rem</div>
<div class="pt-2 pr-4 pb-2 pl-4">Individual sides</div>

<!-- Margin and auto-centering -->
<div class="mt-8 mb-4">Top 2rem, bottom 1rem</div>
<div class="mx-auto max-w-lg">Centered block</div>

<!-- Negative margins (use for overlapping elements) -->
<div class="-mt-10">Overlap by 2.5rem upward</div>

Sizing: width and height

<!-- Fixed sizes from the spacing scale -->
<div class="w-8 h-8">2rem × 2rem square</div>

<!-- Percentage widths -->
<div class="w-1/2">50% width</div>
<div class="w-2/3">66.66% width</div>
<div class="w-full">100% width</div>

<!-- Viewport units -->
<div class="w-screen h-screen">Full viewport</div>

<!-- Min/max constraints -->
<div class="w-full max-w-md">Fluid up to 28rem</div>
<div class="min-h-screen">At least full viewport height</div>

<!-- Arbitrary values -->
<div class="w-[372px] h-[48px]">Exact pixel dimensions</div>

Background and borders

<!-- Background colors (uses the Tailwind color palette) -->
<div class="bg-white">White</div>
<div class="bg-gray-50">Very light gray</div>
<div class="bg-indigo-600">Indigo</div>
<div class="bg-transparent">Transparent</div>

<!-- Background opacity modifier (new in v4: uses color-mix) -->
<div class="bg-indigo-600/50">50% opacity indigo</div>
<div class="bg-indigo-600/20">20% opacity indigo</div>

<!-- Border width and color -->
<div class="border">1px border (default: gray-200)</div>
<div class="border-2 border-indigo-500">2px indigo border</div>
<div class="border-t border-gray-200">Top border only</div>

<!-- Border radius -->
<div class="rounded">0.25rem radius</div>
<div class="rounded-lg">0.5rem radius</div>
<div class="rounded-xl">0.75rem radius</div>
<div class="rounded-2xl">1rem radius</div>
<div class="rounded-full">Fully circular (50%)</div>

Typography

<!-- Font size (and matching line-height is built in) -->
<p class="text-xs">Extra small (0.75rem)</p>
<p class="text-sm">Small (0.875rem)</p>
<p class="text-base">Base (1rem)</p>
<p class="text-lg">Large (1.125rem)</p>
<p class="text-xl">Extra large (1.25rem)</p>
<p class="text-2xl">2× extra large (1.5rem)</p>
<p class="text-4xl">4× extra large (2.25rem)</p>

<!-- Font weight -->
<p class="font-normal">Normal (400)</p>
<p class="font-medium">Medium (500)</p>
<p class="font-semibold">Semibold (600)</p>
<p class="font-bold">Bold (700)</p>

<!-- Text color -->
<p class="text-gray-900">Near black</p>
<p class="text-gray-500">Muted gray</p>
<p class="text-indigo-600">Brand color</p>

<!-- Alignment, decoration, transform -->
<p class="text-center">Centered</p>
<p class="text-right">Right-aligned</p>
<p class="underline">Underlined</p>
<p class="uppercase tracking-widest">UPPERCASE WITH WIDE TRACKING</p>
<p class="truncate">Long text that gets truncated with an ellipsis...</p>

Shadows and rings

<!-- Box shadows -->
<div class="shadow-sm">Subtle shadow</div>
<div class="shadow">Default shadow</div>
<div class="shadow-md">Medium shadow</div>
<div class="shadow-lg">Large shadow</div>
<div class="shadow-xl">Extra large shadow</div>
<div class="shadow-none">No shadow</div>

<!-- Rings (outline shadows — better for focus indicators) -->
<button class="focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2">
  Accessible focus ring
</button>

Display and visibility

<!-- Display -->
<div class="block">Block element</div>
<span class="inline-block">Inline block</span>
<div class="hidden">Visually hidden (display: none)</div>
<div class="invisible">Hidden but takes space</div>

<!-- Overflow -->
<div class="overflow-hidden">Clips content</div>
<div class="overflow-auto">Scrollable when needed</div>
<div class="overflow-x-auto overflow-y-hidden">Horizontal scroll only</div>

State variants: hover, focus, active, disabled

Variants are prefixes that conditionally apply a utility. They follow the pattern variant:utility.

<!-- Hover effects -->
<button class="bg-indigo-600 hover:bg-indigo-700 transition-colors">
  Hover me
</button>

<!-- Focus-visible (keyboard focus only — not triggered by mouse click) -->
<a
  href="/about"
  class="text-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
>
  About
</a>

<!-- Active state (while pressed) -->
<button class="bg-blue-500 active:scale-95 transition-transform">
  Press me
</button>

<!-- Disabled styling -->
<button
  class="bg-indigo-600 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
  disabled
>
  Submit
</button>

<!-- Group hover: child reacts to parent hover -->
<div class="group border rounded-lg p-4 hover:bg-indigo-50">
  <h3 class="font-semibold group-hover:text-indigo-700 transition-colors">Card title</h3>
  <p class="text-gray-500">Supporting text</p>
</div>

Arbitrary values: escaping the scale

When a design requires an exact value not in the default scale, use square brackets:

<!-- Exact pixel values -->
<div class="w-[372px]">Exact width</div>
<div class="h-[calc(100vh-4rem)]">Viewport minus header</div>
<div class="top-[117px]">Exact top position</div>

<!-- Arbitrary colors -->
<div class="bg-[#1da1f2]">Twitter blue</div>
<div class="text-[#ff6b6b]">Custom red</div>

<!-- Arbitrary CSS properties (v4 feature) -->
<div class="[mask-image:linear-gradient(to_bottom,black,transparent)]">
  Faded bottom
</div>

Putting it all together

Here is a notification banner that combines all the categories covered:

<div class="flex items-start gap-3 px-4 py-3 bg-amber-50 border border-amber-200 rounded-lg max-w-md">
  <!-- Icon -->
  <div class="shrink-0 w-5 h-5 mt-0.5 text-amber-500">
    <svg fill="currentColor" viewBox="0 0 20 20">
      <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
    </svg>
  </div>
  <!-- Text -->
  <div>
    <p class="text-sm font-semibold text-amber-800">Attention needed</p>
    <p class="text-sm text-amber-700 mt-1">Your subscription expires in 3 days. Renew now to avoid interruption.</p>
  </div>
</div>

Practice

  1. Build a pricing card with a title, price, bullet list of features, and a CTA button. Use shadow-lg, rounded-2xl, proper padding and typography utilities.
  2. Add hover:scale-105 and transition-transform to the card wrapper and observe the scale animation.
  3. Create a badge component using arbitrary values: bg-[#10b981]/20 text-[#065f46] text-xs font-medium px-2.5 py-0.5 rounded-full.

Arbitrary values escape the scale
When the default scale does not have what you need, use square-bracket syntax: w-[372px], text-[#e91e63], mt-[13px]. These are compiled to exact CSS values with no configuration needed. Use them sparingly — if you reach for them often, consider adding the value to @theme instead.
State variants stack infinitely
Tailwind variants can be stacked: dark:hover:bg-indigo-700 applies the background only in dark mode AND on hover. md:focus:ring-4 applies the ring only at the md breakpoint AND on focus. There is no practical limit to stacking.
Do not construct class names dynamically
Tailwind scans your source files for complete class name strings. Writing 'bg-' + color will NOT work because the scanner will not find the full class name. Always write complete class names: bg-red-500, bg-green-500, etc.
html
<!-- Profile card using spacing, color, typography, border, shadow, and state utilities -->
<div class="max-w-sm mx-auto bg-white rounded-2xl shadow-lg overflow-hidden border border-gray-100">

  <!-- Cover image area -->
  <div class="h-24 bg-gradient-to-r from-indigo-500 to-purple-600"></div>

  <!-- Content -->
  <div class="px-6 pb-6">
    <!-- Avatar -->
    <div class="-mt-10 mb-4">
      <img
        src="/avatar.jpg"
        alt="User avatar"
        class="w-20 h-20 rounded-full border-4 border-white shadow-md object-cover"
      />
    </div>

    <!-- Name and role -->
    <h2 class="text-xl font-bold text-gray-900 leading-tight">Alice Johnson</h2>
    <p class="text-sm text-gray-500 mb-4">Senior Frontend Engineer</p>

    <!-- Stats row -->
    <div class="flex gap-6 mb-5 text-center">
      <div>
        <p class="text-lg font-semibold text-gray-900">128</p>
        <p class="text-xs text-gray-400 uppercase tracking-wide">Posts</p>
      </div>
      <div>
        <p class="text-lg font-semibold text-gray-900">4.2k</p>
        <p class="text-xs text-gray-400 uppercase tracking-wide">Followers</p>
      </div>
      <div>
        <p class="text-lg font-semibold text-gray-900">312</p>
        <p class="text-xs text-gray-400 uppercase tracking-wide">Following</p>
      </div>
    </div>

    <!-- Follow button -->
    <button
      type="button"
      class="w-full bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-800 text-white font-semibold py-2 rounded-xl transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2"
    >
      Follow
    </button>
  </div>
</div>