| |

Tailwind CSS 32 🎨 Structural Variants (first:, last:, odd:, even:, only:, empty:)

A structural variant applies a utility based on the element’s position among its siblings or its content. The first child, the last child, the odd children, the even children, the only child, the empty element. These are the :first-child, :last-child, :nth-child(odd), :nth-child(even), :only-child, and :empty pseudo-classes, and Tailwind exposes each as a variant. They are the tools for lists, tables, and any repeating structure where the position matters.

This chapter covers the structural variants in full. You will learn each variant, the position it targets, the -of-type variants that match by element type rather than position, and the patterns that use them for striped tables, border removal, and spacing adjustments.

Key point: A structural variant applies a utility based on where the element is among its siblings. first: applies to the first child, last: to the last, odd: and even: to alternating children, only: to the single child, and empty: to an element with no children. The variants are the :first-child, :last-child, :nth-child(), :only-child, and :empty pseudo-classes.


Why structural variants matter

The striped table problem. A table with alternating row colors is easier to read than a table with a single color. The odd: and even: variants produce the striping without JavaScript or manual class assignment. Each row gets the variant, and the browser applies it based on the row’s position.

The border problem. A list where each item has a bottom border looks correct except for the last item, whose border is redundant. The last:border-b-0 variant removes the border from the last item. The first:border-t-0 does the same for the top.

The spacing problem. A list of items with a gap between them needs no gap before the first or after the last. The first:mt-0 and last:mb-0 variants remove the spacing at the edges.

The empty problem. A container that is empty because it has no children should look different from a container with children. The empty: variant styles the empty state. It is applied when the element has no child nodes, including text nodes.

The only-child problem. A container with a single child may need different styling from a container with multiple children. The only: variant applies when the element is the only child of its parent. It is used less often than the others, but it has its cases.

The position problem. The variants apply to the element’s position among its siblings, not to the element’s own properties. The first: variant applies to the element that is first among its siblings. The first: on a nested element applies within its own parent, not the outer one.

The -of-type problem. The first-child pseudo-class matches the first child of any type. The first-of-type matches the first child of a specific type. The first: variant uses first-child. The first-of-type: variant uses first-of-type. The distinction matters when the parent has children of different types.


a. The position variants

The first:, last:, and only: variants target the first, last, and only child.

<ul>
  <li class="first:font-bold">Item 1</li>
  <li>Item 2</li>
  <li class="last:text-gray-500">Item 3</li>
</ul>

The first <li> is bold. The last <li> is gray. The middle <li> has no variant.

The only: variant applies when the element is the only child.

<ul>
  <li class="only:bg-yellow-100">Single item</li>
</ul>

The <li> has a yellow background when it is the only child of its parent. If a second <li> is added, the variant no longer applies.

The first: and last: variants are the most common. The only: variant is used when the layout has a single child and needs different styling.

The odd: and even: variants target the odd and even children. The count starts at 1, so the first child is odd.

<ul>
  <li class="odd:bg-white even:bg-gray-50">Item 1</li>
  <li class="odd:bg-white even:bg-gray-50">Item 2</li>
  <li class="odd:bg-white even:bg-gray-50">Item 3</li>
  <li class="odd:bg-white even:bg-gray-50">Item 4</li>
</ul>

The first and third items are white. The second and fourth are gray. The striping is automatic.

The even: variant can also be applied with first: to get the same result.

<li class="bg-white even:bg-gray-50">Item</li>

The default is white, and the even items are gray. The default plus the variant is the standard pattern.

The empty: variant applies when the element has no children. Text nodes count as children, so an element with whitespace is not empty.

<div class="empty:hidden">...</div>

The <div> is hidden when it has no children. The empty: variant is used for containers that should disappear when they are empty.

The empty: variant applies when the element has no child nodes at all. An element with a comment is not empty in some browsers. An element with whitespace is not empty. The variant is strict.


b. The -of-type variants

The first: and last: variants use the first-child and last-child pseudo-classes. These match the first and last child of any type. The first-of-type: and last-of-type: variants match the first and last child of a specific type.

<div>
  <h1>Title</h1>
  <p class="first-of-type:font-bold">First paragraph</p>
  <p>Second paragraph</p>
  <p class="last-of-type:text-gray-500">Last paragraph</p>
</div>

The first <p> is bold, and the last <p> is gray. The <h1> is ignored because it is a different type. The first-of-type: and last-of-type: variants count only the elements of the same type.

The only-of-type: variant applies when the element is the only child of its type.

<div>
  <h1>Title</h1>
  <p class="only-of-type:italic">Only paragraph</p>
</div>

The <p> is italic when it is the only <p> in the parent. The <h1> is a different type and does not affect the count.

The -of-type variants are used when the parent has children of different types and the position should be counted per type.

The nth-* variants are not built into Tailwind as named variants. The odd: and even: variants cover the two most common cases. For other values, the arbitrary variant syntax is used.

<li class="[&:nth-child(3)]:bg-blue-100">Third item</li>

The arbitrary variant [&:nth-child(3)]: applies the utility to the third child. The syntax is the & for the current element and the pseudo-class in brackets.

The arbitrary variant works for any pseudo-class, including the ones that do not have a named variant.

<li class="[&:nth-child(2n+1)]:bg-gray-100">Odd item</li>

The 2n+1 is the formula for odd numbers. The arbitrary variant applies to the odd children.


c. The patterns

Striped table.

<table>
  <tbody>
    <tr class="odd:bg-white even:bg-gray-50">
      <td>Row 1</td>
    </tr>
    <tr class="odd:bg-white even:bg-gray-50">
      <td>Row 2</td>
    </tr>
  </tbody>
</table>

The rows alternate between white and gray. The striping improves readability for long tables.

Border removal.

<ul>
  <li class="border-b last:border-b-0">Item 1</li>
  <li class="border-b last:border-b-0">Item 2</li>
  <li class="border-b last:border-b-0">Item 3</li>
</ul>

Each item has a bottom border except the last. The last:border-b-0 removes the final border.

Spacing adjustment.

<div>
  <p class="mt-4 first:mt-0">Paragraph 1</p>
  <p class="mt-4 first:mt-0">Paragraph 2</p>
</div>

Each paragraph has a top margin except the first. The first:mt-0 removes the top margin from the first paragraph, so the spacing is between the paragraphs and not above the first.

Empty state.

<div class="empty:hidden">
  <!-- rendered only when there are items -->
</div>

The container is hidden when it is empty. The empty: variant is used when the container should disappear.

First and last in a navigation.

<nav>
  <a class="first:pl-0 last:pr-0 px-4">Home</a>
  <a class="first:pl-0 last:pr-0 px-4">About</a>
  <a class="first:pl-0 last:pr-0 px-4">Contact</a>
</nav>

The first link has no left padding, and the last has no right padding. The spacing is between the links, not at the edges.

Alternating layout.

<div class="odd:flex-row even:flex-row-reverse flex">
  <div>Text</div>
  <div>Image</div>
</div>

The odd rows have the text on the left and the image on the right. The even rows have the reverse. The odd: and even: variants control the direction.

Table with first column styled.

<tr>
  <td class="first:font-bold">Name</td>
  <td>Value</td>
</tr>

The first <td> is bold. The first: variant applies to the first cell in the row.

The structural variants are the tools for the repeating structures. They are applied to the element that repeats, and the browser determines the position.


Complete Example Session

<!-- ============================================ -->
<!-- PART 1: FIRST AND LAST -->
<!-- ============================================ -->
<ul>
  <li class="first:font-bold">Item 1</li>
  <li>Item 2</li>
  <li class="last:text-gray-500">Item 3</li>
</ul>
<!-- ============================================ -->
<!-- PART 2: ODD AND EVEN STRIPES -->
<!-- ============================================ -->
<ul>
  <li class="odd:bg-white even:bg-gray-50">Item 1</li>
  <li class="odd:bg-white even:bg-gray-50">Item 2</li>
  <li class="odd:bg-white even:bg-gray-50">Item 3</li>
</ul>
<!-- ============================================ -->
<!-- PART 3: STRIPED TABLE -->
<!-- ============================================ -->
<table>
  <tbody>
    <tr class="odd:bg-white even:bg-gray-50"><td>Row 1</td></tr>
    <tr class="odd:bg-white even:bg-gray-50"><td>Row 2</td></tr>
  </tbody>
</table>
<!-- ============================================ -->
<!-- PART 4: LAST BORDER REMOVAL -->
<!-- ============================================ -->
<ul>
  <li class="border-b last:border-b-0">Item 1</li>
  <li class="border-b last:border-b-0">Item 2</li>
  <li class="border-b last:border-b-0">Item 3</li>
</ul>
<!-- ============================================ -->
<!-- PART 5: FIRST MARGIN REMOVAL -->
<!-- ============================================ -->
<div>
  <p class="mt-4 first:mt-0">Paragraph 1</p>
  <p class="mt-4 first:mt-0">Paragraph 2</p>
</div>
<!-- ============================================ -->
<!-- PART 6: ONLY CHILD -->
<!-- ============================================ -->
<ul>
  <li class="only:bg-yellow-100">Single item</li>
</ul>
<!-- ============================================ -->
<!-- PART 7: EMPTY -->
<!-- ============================================ -->
<div class="empty:hidden">
  <!-- hidden when empty -->
</div>
<!-- ============================================ -->
<!-- PART 8: FIRST-OF-TYPE -->
<!-- ============================================ -->
<div>
  <h1>Title</h1>
  <p class="first-of-type:font-bold">First paragraph</p>
  <p>Second paragraph</p>
</div>
<!-- ============================================ -->
<!-- PART 9: ARBITRARY NTH-CHILD -->
<!-- ============================================ -->
<ul>
  <li class="[&:nth-child(3)]:bg-blue-100">Item 1</li>
  <li class="[&:nth-child(3)]:bg-blue-100">Item 2</li>
  <li class="[&:nth-child(3)]:bg-blue-100">Item 3</li>
</ul>
<!-- ============================================ -->
<!-- PART 10: ALTERNATING LAYOUT -->
<!-- ============================================ -->
<div class="flex odd:flex-row even:flex-row-reverse">
  <div>Text</div>
  <div>Image</div>
</div>

The ten parts covered first and last, odd and even stripes, a striped table, last border removal, first margin removal, only child, empty, first-of-type, arbitrary nth-child, and alternating layout.


Quick Reference

Position Variants

VariantMatches
first:First child
last:Last child
only:Only child
odd:Odd-numbered children
even:Even-numbered children

Type Variants

VariantMatches
first-of-type:First child of its type
last-of-type:Last child of its type
only-of-type:Only child of its type

Content Variants

VariantMatches
empty:No child nodes

Arbitrary Variants

SyntaxMatches
[&:nth-child(3)]:Third child
[&:nth-child(2n+1)]:Odd children
[&:nth-child(3n)]:Every third child
[&:nth-last-child(1)]:Last child

Common Patterns

PatternClasses
Striped rowsodd:bg-white even:bg-gray-50
Remove last borderborder-b last:border-b-0
Remove first marginmt-4 first:mt-0
Hide when emptyempty:hidden
Alternating layoutodd:flex-row even:flex-row-reverse

Best Practices

✅ Do This:

<!-- Use odd/even for striping -->
<tr class="odd:bg-white even:bg-gray-50">                              <!-- ✅ -->

<!-- Use last: to remove the final border -->
<li class="border-b last:border-b-0">Item</li>                         <!-- ✅ -->

<!-- Use first: to remove the leading margin -->
<p class="mt-4 first:mt-0">Paragraph</p>                               <!-- ✅ -->

<!-- Use empty: to hide empty containers -->
<div class="empty:hidden">...</div>                                    <!-- ✅ -->

<!-- Use first-of-type for type-specific position -->
<p class="first-of-type:font-bold">First paragraph</p>                 <!-- ✅ -->

<!-- Use arbitrary variants for other nth values -->
<li class="[&:nth-child(3)]:bg-blue-100">Third</li>                    <!-- ✅ -->

❌ Don’t Do This:

<!-- Don't use first: to target the first of a type -->
<p class="first:font-bold">Not necessarily the first paragraph</p>    <!-- ⚠️ -->

<!-- Don't expect empty: to ignore whitespace -->
<div class="empty:hidden"> </div>  <!-- not empty -->                  <!-- ⚠️ -->

<!-- Don't forget that odd starts at 1 -->
<li class="odd:bg-gray-100">  <!-- first item is odd -->               <!-- ✅ -->

<!-- Don't use only: when first: is enough -->
<li class="only:font-bold">  <!-- for a single item -->                <!-- ⚠️ -->

<!-- Don't apply the variant to the wrong element -->
<div class="first:bg-blue-100">  <!-- the child, not the parent -->    <!-- ❌ -->

<!-- Don't forget the arbitrary variant syntax -->
<li class="nth-child(3):bg-blue-100">  <!-- not a variant -->           <!-- ❌ -->

Common Pitfalls

PitfallWhy It HappensFix
Striping reversedOdd starts at 1Use odd: for the first
Empty not applyingWhitespace is a childRemove the whitespace
First-of-type wrongUsed first:Use first-of-type:
Last border still visibleNo last: variantAdd last:border-b-0
Only matches multipleMultiple childrenUse only: correctly
Arbitrary variant syntaxMissing &Use [&:nth-child(n)]:
Variant on parentApplied to the wrong elementApply to the child

Real-World Examples

1. Striped Table

<tr class="odd:bg-white even:bg-gray-50">...</tr>

2. Border Removal

<li class="border-b last:border-b-0">Item</li>

3. First Margin

<p class="mt-4 first:mt-0">Paragraph</p>

4. Empty State

<div class="empty:hidden">...</div>

5. Navigation Spacing

<a class="px-4 first:pl-0 last:pr-0">Link</a>

6. Alternating Layout

<div class="odd:flex-row even:flex-row-reverse">...</div>

7. Bold First Cell

<td class="first:font-bold">Name</td>

8. Only Child

<li class="only:bg-yellow-100">Single</li>

9. Third Item Highlight

<li class="[&:nth-child(3)]:bg-blue-100">Third</li>

10. Every Third Item

<li class="[&:nth-child(3n)]:bg-gray-100">Every third</li>

Visual

Position Variants

┌─────────────────────────────────────────────────────────────┐
│  <ul>                                                       │
│    <li>1</li>  ← first:  odd:                               │
│    <li>2</li>  ← even:                                      │
│    <li>3</li>  ← odd:                                       │
│    <li>4</li>  ← even: last:                                │
│  </ul>                                                      │
│                                                             │
│  first: → item 1                                            │
│  last:  → item 4                                            │
│  odd:   → items 1, 3                                        │
│  even:  → items 2, 4                                        │
│  only:  → if there is exactly one item                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

first vs first-of-type

┌─────────────────────────────────────────────────────────────┐
│  <div>                                                      │
│    <h1>Title</h1>       ← first: matches this               │
│    <p>Para 1</p>        ← first-of-type: matches this       │
│    <p>Para 2</p>        ← last: matches this                │
│    <p>Para 3</p>        ← last-of-type: matches this        │
│  </div>                                                     │
│                                                             │
│  first:      the first child (h1)                           │
│  first-of-type: the first p                                 │
│  last:       the last child (p)                             │
│  last-of-type: the last p                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Striped Table

┌─────────────────────────────────────────────────────────────┐
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Row 1 (odd)   white                                │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Row 2 (even)  gray-50                              │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Row 3 (odd)   white                                │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Row 4 (even)  gray-50                              │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  odd:bg-white even:bg-gray-50                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Border Removal

┌─────────────────────────────────────────────────────────────┐
│  WITHOUT last:border-b-0                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Item 1                                             │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Item 2                                             │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Item 3                                             │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  (extra border at the bottom)                       │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  WITH last:border-b-0                                       │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Item 1                                             │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Item 2                                             │    │
│  ├─────────────────────────────────────────────────────┤    │
│  │  Item 3                                             │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  No border after the last item.                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Summary

VariantMatches
first:First child
last:Last child
only:Only child
odd:Odd-numbered children
even:Even-numbered children
first-of-type:First child of its type
last-of-type:Last child of its type
only-of-type:Only child of its type
empty:No children
[&:nth-child(n)]:Arbitrary nth-child

Key takeaways:

  • Structural variants apply based on position among siblings. first:, last:, odd:, even:, and only: target the position. first-of-type: and last-of-type: target the position within the element’s type.
  • The odd: and even: variants stripe a list or table. The count starts at 1, so the first child is odd. The striping is automatic; each element gets the variant, and the browser applies it based on the position.
  • The last:border-b-0 pattern removes the final border. A list where each item has a bottom border looks correct except for the last. The last: variant removes the redundant border.
  • The first:mt-0 pattern removes the leading margin. A list where each item has a top margin has an extra margin above the first. The first: variant removes it.
  • The empty: variant applies when there are no children. Text nodes and whitespace count as children. An element with a space is not empty. The variant is strict.
  • The -of-type variants count per type. The first: variant counts all children. The first-of-type: counts only the children of the same type. The distinction matters when the parent has mixed children.
  • The arbitrary variant handles other nth-child values. The [&:nth-child(3)]: syntax applies the utility to the third child. The [&:nth-child(3n)]: applies to every third child. The syntax works for any pseudo-class that does not have a named variant.

Remember: Structural variants are the tools for repeating structures. The list, the table, the navigation, the alternating layout. Each repeats an element, and the position of the element among its siblings determines the styling. The variants are the browser’s pseudo-classes, and the browser applies them based on the DOM. Use odd: and even: for striping, first: and last: for edge adjustments, empty: for empty states, and the -of-type variants when the parent has mixed children. The structure is the interface, and the variants are how the interface reflects it.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!