Tailwind CSS

Tables & Lists

15 min Lesson 19 of 35

Tables & Lists in Tailwind CSS

Tailwind provides comprehensive utilities for styling tables and lists. From controlling table layouts and borders to customizing list markers, these utilities help create clean, readable data presentations and structured content.

Semantic HTML First

Always use proper semantic HTML for tables (<table>, <thead>, <tbody>, <tr>, <th>, <td>) and lists (<ul>, <ol>, <li>) before applying Tailwind classes. Proper semantics ensure accessibility and SEO benefits.

Table Display Utilities

Control table rendering behavior with display utilities that determine how the browser calculates column widths.

Table Layout Utilities

<!-- Auto layout: columns width based on content -->
<table class="table-auto w-full">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>John Doe</td>
      <td>john@example.com</td>
      <td>Admin</td>
    </tr>
  </tbody>
</table>

<!-- Fixed layout: equal column widths, faster rendering -->
<table class="table-fixed w-full">
  <thead>
    <tr>
      <th class="w-1/3">Name</th>
      <th class="w-1/3">Email</th>
      <th class="w-1/3">Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>John Doe</td>
      <td>john@example.com</td>
      <td>Admin</td>
    </tr>
  </tbody>
</table>

Table Layout Choice

Use table-auto when content length varies significantly (like user-generated content). Use table-fixed when you want predictable column widths and faster rendering. With table-fixed, you must set widths explicitly or they'll be equal.

Border Collapse and Spacing

Control how table borders render and the spacing between table cells.

Border Collapse Utilities

<!-- Collapsed borders: adjacent borders merge into one -->
<table class="border-collapse border border-gray-300 w-full">
  <thead>
    <tr>
      <th class="border border-gray-300 px-4 py-2">Header 1</th>
      <th class="border border-gray-300 px-4 py-2">Header 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="border border-gray-300 px-4 py-2">Cell 1</td>
      <td class="border border-gray-300 px-4 py-2">Cell 2</td>
    </tr>
  </tbody>
</table>

<!-- Separate borders: space between cells -->
<table class="border-separate border-spacing-2 w-full">
  <thead>
    <tr>
      <th class="border border-gray-300 px-4 py-2 bg-gray-100">Header 1</th>
      <th class="border border-gray-300 px-4 py-2 bg-gray-100">Header 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="border border-gray-300 px-4 py-2 bg-white">Cell 1</td>
      <td class="border border-gray-300 px-4 py-2 bg-white">Cell 2</td>
    </tr>
  </tbody>
</table>

Border Spacing Utilities

<!-- Equal spacing in all directions -->
<table class="border-separate border-spacing-0">No spacing</table>
<table class="border-separate border-spacing-1">0.25rem spacing</table>
<table class="border-separate border-spacing-2">0.5rem spacing</table>
<table class="border-separate border-spacing-4">1rem spacing</table>
<table class="border-separate border-spacing-8">2rem spacing</table>

<!-- Different horizontal and vertical spacing -->
<table class="border-separate border-spacing-x-2 border-spacing-y-4">
  <!-- 0.5rem horizontal, 1rem vertical spacing -->
</table>

<!-- Only horizontal spacing -->
<table class="border-separate border-spacing-x-4 border-spacing-y-0">
  <!-- 1rem between columns only -->
</table>

Complete Table Examples

Modern Data Table with Stripes

<div class="overflow-x-auto">
  <table class="table-auto w-full border-collapse">
    <thead>
      <tr class="bg-gray-100">
        <th class="px-6 py-3 text-left text-xs font-medium
                   text-gray-700 uppercase tracking-wider">
          Name
        </th>
        <th class="px-6 py-3 text-left text-xs font-medium
                   text-gray-700 uppercase tracking-wider">
          Email
        </th>
        <th class="px-6 py-3 text-left text-xs font-medium
                   text-gray-700 uppercase tracking-wider">
          Role
        </th>
        <th class="px-6 py-3 text-left text-xs font-medium
                   text-gray-700 uppercase tracking-wider">
          Status
        </th>
      </tr>
    </thead>
    <tbody class="bg-white divide-y divide-gray-200">
      <tr class="hover:bg-gray-50">
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm font-medium text-gray-900">John Doe</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm text-gray-500">john@example.com</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm text-gray-900">Admin</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <span class="px-2 inline-flex text-xs leading-5 font-semibold
                       rounded-full bg-green-100 text-green-800">
            Active
          </span>
        </td>
      </tr>
      <tr class="hover:bg-gray-50">
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm font-medium text-gray-900">Jane Smith</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm text-gray-500">jane@example.com</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <div class="text-sm text-gray-900">User</div>
        </td>
        <td class="px-6 py-4 whitespace-nowrap">
          <span class="px-2 inline-flex text-xs leading-5 font-semibold
                       rounded-full bg-red-100 text-red-800">
            Inactive
          </span>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Card-Style Table

<div class="bg-white rounded-lg shadow overflow-hidden">
  <table class="table-auto w-full">
    <thead class="bg-gray-800 text-white">
      <tr>
        <th class="px-6 py-4 text-left text-sm font-semibold">Product</th>
        <th class="px-6 py-4 text-left text-sm font-semibold">Price</th>
        <th class="px-6 py-4 text-left text-sm font-semibold">Stock</th>
        <th class="px-6 py-4 text-right text-sm font-semibold">Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr class="border-b border-gray-200 hover:bg-gray-50">
        <td class="px-6 py-4">
          <div class="flex items-center">
            <img src="product.jpg" class="w-10 h-10 rounded-lg mr-3" alt="Product">
            <span class="font-medium">Product Name</span>
          </div>
        </td>
        <td class="px-6 py-4 text-gray-700">$99.00</td>
        <td class="px-6 py-4">
          <span class="text-green-600 font-medium">In Stock</span>
        </td>
        <td class="px-6 py-4 text-right">
          <button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>
          <button class="text-red-600 hover:text-red-800">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Comparison Table with Separate Borders

<table class="table-fixed border-separate border-spacing-2 w-full">
  <thead>
    <tr>
      <th class="w-1/4"></th>
      <th class="w-1/4 bg-blue-100 border-2 border-blue-300 rounded-lg px-4 py-3">
        <div class="text-lg font-bold text-blue-800">Basic</div>
        <div class="text-2xl font-bold text-blue-900 mt-2">$9/mo</div>
      </th>
      <th class="w-1/4 bg-purple-100 border-2 border-purple-300 rounded-lg px-4 py-3">
        <div class="text-lg font-bold text-purple-800">Pro</div>
        <div class="text-2xl font-bold text-purple-900 mt-2">$29/mo</div>
      </th>
      <th class="w-1/4 bg-yellow-100 border-2 border-yellow-300 rounded-lg px-4 py-3">
        <div class="text-lg font-bold text-yellow-800">Enterprise</div>
        <div class="text-2xl font-bold text-yellow-900 mt-2">$99/mo</div>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="px-4 py-3 font-medium">Users</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">5</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">25</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">Unlimited</td>
    </tr>
    <tr>
      <td class="px-4 py-3 font-medium">Storage</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">10GB</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">100GB</td>
      <td class="bg-white border border-gray-200 rounded-lg px-4 py-3 text-center">1TB</td>
    </tr>
  </tbody>
</table>

Caption Position

Table Caption Utilities

<!-- Caption at top (default) -->
<table class="w-full caption-top">
  <caption class="text-lg font-bold text-gray-900 mb-3">
    Employee Directory
  </caption>
  <thead>...</thead>
  <tbody>...</tbody>
</table>

<!-- Caption at bottom -->
<table class="w-full caption-bottom">
  <thead>...</thead>
  <tbody>...</tbody>
  <caption class="text-sm text-gray-600 mt-3">
    Last updated: January 2024
  </caption>
</table>

List Style Type

Control the appearance of list markers (bullets, numbers, etc.) with list-style utilities.

List Style Type Utilities

<!-- No markers -->
<ul class="list-none">
  <li>Item without bullet</li>
  <li>Another item</li>
</ul>

<!-- Disc markers (default for ul) -->
<ul class="list-disc pl-5">
  <li>Bullet point</li>
  <li>Another bullet</li>
</ul>

<!-- Decimal numbers (default for ol) -->
<ol class="list-decimal pl-5">
  <li>First item</li>
  <li>Second item</li>
</ol>

<!-- Other list styles -->
<ul class="list-circle pl-5">
  <li>Circle marker</li>
</ul>

<ul class="list-square pl-5">
  <li>Square marker</li>
</ul>

<ol class="list-lower-alpha pl-5">
  <li>a. First item</li>
  <li>b. Second item</li>
</ol>

<ol class="list-upper-alpha pl-5">
  <li>A. First item</li>
  <li>B. Second item</li>
</ol>

<ol class="list-lower-roman pl-5">
  <li>i. First item</li>
  <li>ii. Second item</li>
</ol>

<ol class="list-upper-roman pl-5">
  <li>I. First item</li>
  <li>II. Second item</li>
</ol>

List Style Position

List Position Utilities

<!-- Inside: marker inside content box (default) -->
<ul class="list-disc list-inside bg-gray-100 p-4">
  <li>Marker is inside the content box</li>
  <li>Text wraps under the marker</li>
</ul>

<!-- Outside: marker outside content box -->
<ul class="list-disc list-outside pl-5 bg-gray-100 p-4">
  <li>Marker is outside the content box</li>
  <li>Text does not wrap under marker</li>
</ul>

Styling List Markers

Custom Marker Colors and Styles

<!-- Colored markers using marker pseudo-element -->
<ul class="list-disc pl-5 space-y-2">
  <li class="marker:text-blue-600">Blue marker</li>
  <li class="marker:text-red-600">Red marker</li>
  <li class="marker:text-green-600">Green marker</li>
</ul>

<!-- Styled markers with size -->
<ul class="list-disc pl-5 space-y-2">
  <li class="marker:text-sky-600 marker:text-lg">
    Large blue marker
  </li>
  <li class="marker:text-purple-600 marker:font-bold">
    Bold purple marker
  </li>
</ul>

<!-- Custom content for markers -->
<ol class="list-none pl-0 space-y-2">
  <li class="flex items-start">
    <span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-600
                 text-white flex items-center justify-center text-sm mr-3">
      1
    </span>
    <span>Custom numbered marker with styling</span>
  </li>
  <li class="flex items-start">
    <span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-600
                 text-white flex items-center justify-center text-sm mr-3">
      2
    </span>
    <span>Another custom marker</span>
  </li>
</ol>

Complete List Examples

Feature List with Icons

<ul class="space-y-3">
  <li class="flex items-start">
    <svg class="w-6 h-6 text-green-500 mr-3 flex-shrink-0" fill="currentColor">
      <path d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
    </svg>
    <span>High-quality products with lifetime warranty</span>
  </li>
  <li class="flex items-start">
    <svg class="w-6 h-6 text-green-500 mr-3 flex-shrink-0" fill="currentColor">
      <path d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
    </svg>
    <span>24/7 customer support</span>
  </li>
  <li class="flex items-start">
    <svg class="w-6 h-6 text-green-500 mr-3 flex-shrink-0" fill="currentColor">
      <path d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
    </svg>
    <span>Free shipping on orders over $50</span>
  </li>
</ul>

Nested Lists with Different Styles

<ol class="list-decimal pl-5 space-y-2">
  <li>
    <strong>Getting Started</strong>
    <ul class="list-disc pl-5 mt-2 space-y-1">
      <li>Create an account</li>
      <li>Verify your email</li>
      <li>Complete your profile</li>
    </ul>
  </li>
  <li>
    <strong>Setting Up</strong>
    <ul class="list-disc pl-5 mt-2 space-y-1">
      <li>Configure preferences</li>
      <li>Connect integrations</li>
      <li>Invite team members</li>
    </ul>
  </li>
  <li>
    <strong>Advanced Features</strong>
    <ul class="list-disc pl-5 mt-2 space-y-1">
      <li>Custom workflows</li>
      <li>API access</li>
      <li>Analytics dashboard</li>
    </ul>
  </li>
</ol>

Card List (Remove Default Markers)

<ul class="list-none space-y-4">
  <li class="bg-white rounded-lg shadow p-6 border-l-4 border-blue-500">
    <h3 class="text-xl font-bold text-gray-900 mb-2">Step 1: Plan</h3>
    <p class="text-gray-600">
      Define your goals and create a detailed project plan.
    </p>
  </li>
  <li class="bg-white rounded-lg shadow p-6 border-l-4 border-green-500">
    <h3 class="text-xl font-bold text-gray-900 mb-2">Step 2: Build</h3>
    <p class="text-gray-600">
      Implement your solution with best practices.
    </p>
  </li>
  <li class="bg-white rounded-lg shadow p-6 border-l-4 border-purple-500">
    <h3 class="text-xl font-bold text-gray-900 mb-2">Step 3: Launch</h3>
    <p class="text-gray-600">
      Deploy and monitor your project in production.
    </p>
  </li>
</ul>

Accessibility Considerations

When removing list markers with list-none, ensure alternative visual cues exist. Lists convey semantic meaning to screen readers. If styling as cards or removing markers, the underlying HTML should still use proper list elements for accessibility.

Exercise 1: Product Comparison Table

Create a responsive product comparison table:

  • 3-4 products with different pricing tiers
  • Use table-fixed layout with equal column widths
  • Header row with product names and prices
  • Feature rows comparing specifications
  • Use checkmarks/crosses for feature availability
  • Add hover effects on rows
  • Wrap in scrollable container for mobile

Exercise 2: Task List with Custom Markers

Build a task list with custom styled markers:

  • Use list-none and create custom numbered markers
  • Each marker: circular badge with number
  • Different colors for completed vs. pending tasks
  • Include task title, description, and due date
  • Add checkboxes for task completion
  • Nested subtasks under main tasks

Exercise 3: Data Table with Actions

Create an admin dashboard data table:

  • User management table with avatar, name, email, role, status
  • Status badges (active/inactive) with appropriate colors
  • Action buttons (edit, delete) in last column
  • Striped rows (alternating backgrounds)
  • Hover effects on rows
  • Sortable column headers (show sort icon)
  • Responsive: horizontal scroll on mobile

Best Practices for Tables and Lists

  • Always wrap tables in containers with overflow-x-auto for responsive design
  • Use whitespace-nowrap to prevent cell text wrapping when needed
  • Add hover effects to table rows for better interactivity
  • Use divide-y utility for row separators instead of individual borders
  • Consider sticky positioning for table headers on long tables
  • Use proper padding (px-6 py-4) for comfortable cell spacing
  • Add space-y utilities between list items for better readability
  • Use semantic HTML (<thead>, <tbody>, <th>, <td>) for accessibility

Summary

Tailwind provides comprehensive utilities for tables and lists:

  • Table Layout: Control column width calculation (table-auto, table-fixed)
  • Border Collapse: Merge or separate cell borders (border-collapse, border-separate)
  • Border Spacing: Control spacing between cells (border-spacing-{n})
  • Caption Position: Place table captions (caption-top, caption-bottom)
  • List Style Type: Choose marker styles (list-disc, list-decimal, list-none)
  • List Position: Control marker placement (list-inside, list-outside)
  • Marker Styling: Customize marker appearance (marker:text-{color})

These utilities enable you to create professional, accessible data tables and structured lists that work beautifully across all devices. Remember to prioritize accessibility by using semantic HTML and providing clear visual hierarchies.