Skip to content

Tailwind CSS Pseudo-Classes — First, Last, Odd, Even, and Empty

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Tailwind CSS Pseudo. We cover key concepts, practical examples, and best practices to help you master this topic.

Tailwind CSS pseudo-class variants style elements based on structural position (first:, last:, odd:, even:, only:), content state (empty:), and element state (required:, invalid:, read-only:, placeholder-shown:).

What You'll Learn

You will learn how to use structural pseudo-class variants for lists and grids, form state variants for validation, and content-based variants for dynamic styling.

Why It Matters

Pseudo-class variants eliminate custom CSS for common patterns like alternating row colors, first/last element spacing, and form validation states -- all inline in HTML.

Real-World Use

Doda Browser uses odd:bg-gray-50 for alternating table rows, last:border-b-0 for list items, and empty:hidden for dynamic content containers that should collapse when empty.

flowchart LR
    A[Variants] --> B[Pseudo-Classes]
    B --> C[Positional]
    B --> D[Form States]
    B --> E[Content]
    B --> F[Combined]
    style B fill:#38bdf8,stroke:#0284c7,color:#fff
    style C fill:#22c55e,stroke:#16a34a,color:#fff

Structural Pseudo-Classes

<div class="max-w-md mx-auto p-4">
  <h3 class="font-bold mb-2">List with first/last styling:</h3>
  <ul class="divide-y divide-gray-200 border rounded-lg">
    <li class="px-4 py-3 first:rounded-t-lg last:rounded-b-lg first:bg-blue-50 last:bg-green-50">
      First item (rounded top + blue bg)
    </li>
    <li class="px-4 py-3">Second item</li>
    <li class="px-4 py-3">Third item</li>
    <li class="px-4 py-3 last:rounded-b-lg last:bg-green-50">
      Last item (rounded bottom + green bg)
    </li>
  </ul>

  <h3 class="font-bold mt-6 mb-2">Odd/Even row colors:</h3>
  <div class="border rounded-lg overflow-hidden">
    <div class="px-4 py-3 even:bg-gray-50">Row 1 (odd)</div>
    <div class="px-4 py-3 even:bg-gray-50">Row 2 (even - gray bg)</div>
    <div class="px-4 py-3 even:bg-gray-50">Row 3 (odd)</div>
    <div class="px-4 py-3 even:bg-gray-50">Row 4 (even - gray bg)</div>
  </div>
</div>

Expected output: First and last items have rounded corners and background colors. Even-numbered rows have alternate background.

Form State Pseudo-Classes

<form class="max-w-md mx-auto p-6 space-y-4">
  <div>
    <label class="block text-sm font-medium text-gray-700 mb-1">Email *</label>
    <input type="email" required placeholder="your@email.com"
           class="w-full px-4 py-2 border rounded-lg
                  required:border-blue-500
                  valid:border-green-500 valid:bg-green-50
                  invalid:border-red-500 invalid:bg-red-50
                  focus:ring-2 focus:ring-blue-500 focus:border-transparent
                  placeholder-shown:border-gray-300 placeholder-shown:bg-white
                  transition-colors">
    <p class="text-xs text-gray-500 mt-1">Try typing an invalid email to see validation styles.</p>
  </div>

  <div>
    <label class="block text-sm font-medium text-gray-700 mb-1">Age *</label>
    <input type="number" required min="18" max="120" placeholder="18"
           class="w-full px-4 py-2 border rounded-lg
                  valid:border-green-500
                  invalid:border-red-500
                  focus:ring-2 focus:ring-blue-500">
  </div>

  <div>
    <label class="block text-sm font-medium text-gray-700 mb-1">Bio</label>
    <textarea placeholder="Optional bio..."
              class="w-full px-4 py-2 border border-gray-300 rounded-lg
                     placeholder-shown:text-gray-400
                     focus:ring-2 focus:ring-blue-500"></textarea>
  </div>

  <button class="w-full bg-blue-600 text-white py-2 rounded-lg font-medium
                 disabled:opacity-50 disabled:cursor-not-allowed">
    Submit
  </button>
</form>

Expected output: Form inputs show different border and background colors based on validation state (valid/invalid), required state, and placeholder visibility.

Empty State Variant

<div class="max-w-md mx-auto p-4 space-y-4">
  <div class="p-4 border rounded-lg empty:hidden bg-blue-50">
    This div has content (visible)
  </div>

  <div class="p-4 border rounded-lg empty:hidden bg-green-50">
  </div>
  <p class="text-sm text-gray-500">Empty div above is hidden by empty:hidden</p>

  <div class="empty:p-4 empty:bg-gray-50 empty:border-2 empty:border-dashed empty:border-gray-300 empty:rounded-lg empty:before:content-['No_items_found'] empty:before:text-gray-400">
  </div>
  <p class="text-sm text-gray-500">Empty div with placeholder styling via empty: variant</p>
</div>

Expected output: Empty elements with empty:hidden collapse and are invisible. Empty elements with empty:p-4 show placeholder styling.

Only and Nth-Child Variants

<div class="max-w-md mx-auto p-4 space-y-4">
  <!-- only: styles an element that is the only child -->
  <div>
    <h3 class="font-bold mb-2">only: (single child):</h3>
    <div class="flex gap-2">
      <span class="only:bg-purple-100 only:px-4 only:py-2 only:rounded-lg only:font-bold bg-gray-100 px-4 py-2 rounded">
        Single item
      </span>
    </div>
  </div>

  <!-- Multiple children (only: does not apply) -->
  <div>
    <h3 class="font-bold mb-2">Multiple children (only: not applied):</h3>
    <div class="flex gap-2">
      <span class="only:bg-purple-100 bg-gray-100 px-4 py-2 rounded">Item 1</span>
      <span class="only:bg-purple-100 bg-gray-100 px-4 py-2 rounded">Item 2</span>
    </div>
  </div>
</div>

Expected output: The single child element has purple background (only: variant applies). The multiple children do not get the only: styling.

Combining Pseudo-Classes

<div class="max-w-md mx-auto p-4">
  <ul class="divide-y divide-gray-200 border rounded-lg overflow-hidden">
    <li class="px-4 py-3 first:bg-blue-50 odd:bg-gray-50/50 last:border-b-0 hover:bg-gray-100 transition-colors">
      Item 1 (first + odd)
    </li>
    <li class="px-4 py-3 odd:bg-gray-50/50 hover:bg-gray-100 transition-colors">
      Item 2 (even)
    </li>
    <li class="px-4 py-3 odd:bg-gray-50/50 hover:bg-gray-100 transition-colors">
      Item 3 (odd)
    </li>
    <li class="px-4 py-3 odd:bg-gray-50/50 last:border-b-0 hover:bg-gray-100 transition-colors">
      Item 4 (last + even)
    </li>
  </ul>
</div>

Expected output: A styled list combining first, odd, last, and hover pseudo-class variants for a polished interactive list.

Common Mistakes

1. Forgetting the Semicolon in @apply Chains

When pseudo-classes are combined via @apply, separate them with spaces: @apply odd:bg-gray-50 even:bg-white.

2. Using empty: on Elements With Whitespace

empty: matches elements with zero child nodes. Whitespace or a single space text node means the element is not empty.

3. Confusing first: and first-of-type:

first: matches the first child regardless of type. first-of-type: matches the first child of that element type.

4. Not Testing Form States in Browsers

Form validation pseudo-classes (valid, invalid) require actual form interaction to trigger. Test by submitting with invalid data.

5. Overusing Structural Variants

Not every list needs alternating colors. Use structural variants only when they improve readability or user experience.

Practice Questions

  1. What variant targets every other element? odd: for odd-indexed elements, even: for even-indexed elements.

  2. How do you style only the first child? first: applies to the first child. first-of-type: applies to the first child of that tag name.

  3. What variant styles an element only when it is the sole child? only: applies when the element has no siblings.

  4. How do you hide empty elements? empty:hidden or empty:hidden empty:invisible.

  5. What form validation variants are available? valid:, invalid:, required:, optional:, read-only:, read-write:, placeholder-shown:, default:, indeterminate:, checked:.

Challenge

Build a data table with: odd:row background colors, first: rounded top, last: rounded bottom, hover: row highlighting, empty: cell placeholders, and valid/invalid styling for inline editing.

FAQ

Do pseudo-class variants work with all elements?

Most work with all elements. Some (valid, invalid) only apply to form elements with validation.

Can I use nth-child in Tailwind?

Tailwind does not have nth-child variants. Use arbitrary values: [&:nth-child(3)]:bg-blue-100

What is the difference between first: and last:?

first: applies to the first child of a parent. last: applies to the last child.

Can I combine pseudo-classes with responsive variants?

Yes: md:odd:bg-gray-50 applies odd background only on medium screens and above.

How do I style a placeholder element?

Use placeholder-shown: for styling when the placeholder is visible, and placeholder: for the placeholder text color.

Mini Project

Build an interactive data table with: odd/even row colors, first/last rounded corners, hover highlight, empty row placeholders, valid/invalid inline edit fields, and responsive column hiding.

What's Next

Now master Animations and transitions for motion in Tailwind. Learn animate-* utilities, custom keyframes, and transition controls.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro