tbody

The tbody element groups the body content (data rows) of a table. It contains the main data and enables row hover effects and striping variants.

Description

The <tbody> element contains one or more <tr> elements that make up the data portion of a table. It should appear after <thead> and before <tfoot> in the source order.

Vanilla Breeze applies row hover effects to tbody rows and uses tbody for the .striped variant's alternating background colors.

When to Use

  • Every data table: All tables should use tbody to group data rows
  • Multiple tbody sections: Group related rows for visual or semantic separation
  • Dynamic content: Easier to target and update data rows separately from headers
  • Scripting: JavaScript can easily target tbody for adding/removing rows

Basic Example

Wrap all data rows in a tbody element.

Product Inventory
SKU Product Category Stock
A001 Wireless Mouse Peripherals 45
A002 USB-C Hub Accessories 23
A003 Mechanical Keyboard Peripherals 18
A004 Monitor Stand Furniture 12
<table> <caption>Product Inventory</caption> <thead> <tr> <th scope="col">SKU</th> <th scope="col">Product</th> <th scope="col">Category</th> <th scope="col" data-numeric>Stock</th> </tr> </thead> <tbody> <tr> <td>A001</td> <td>Wireless Mouse</td> <td>Peripherals</td> <td data-numeric>45</td> </tr> <!-- More rows --> </tbody> </table>

Row Hover Effect

Vanilla Breeze applies hover effects to rows within tbody, making it easier to track data across columns.

Hover over rows to see the effect
Name Role Status
Alice Johnson Developer Active
Bob Smith Designer Active
Carol Williams Manager On Leave
/* Vanilla Breeze default styling */ tbody tr:hover { background: var(--color-surface-raised); }

Multiple tbody Sections

Use multiple tbody elements to group related rows, creating visual sections within a single table.

Budget by Department
Item Budget Spent
Engineering
Software licenses $12,000 $10,500
Hardware $25,000 $22,000
Marketing
Advertising $30,000 $28,500
Events $15,000 $14,200
Operations
Facilities $20,000 $19,800
Supplies $5,000 $4,200
<table> <caption>Budget by Department</caption> <thead> <tr> <th scope="col">Item</th> <th scope="col" data-numeric>Budget</th> <th scope="col" data-numeric>Spent</th> </tr> </thead> <tbody> <tr> <th scope="rowgroup" colspan="3">Engineering</th> </tr> <tr> <td>Software licenses</td> <td data-numeric>$12,000</td> <td data-numeric>$10,500</td> </tr> <!-- More rows --> </tbody> <tbody> <tr> <th scope="rowgroup" colspan="3">Marketing</th> </tr> <!-- Marketing rows --> </tbody> </table>

Striped Variant

The .striped class on the table applies alternating backgrounds to tbody rows.

Striped table for better row tracking
ID Name Status
001 Project Alpha Active
002 Project Beta Planning
003 Project Gamma Complete
004 Project Delta On Hold
005 Project Epsilon Active
/* Vanilla Breeze striped variant */ table.striped tbody tr:nth-child(odd) { background: var(--color-surface-raised); }

Accessibility

  • Structural separation: Helps assistive technologies distinguish data from headers and footers
  • Row navigation: Screen readers can navigate between different table sections
  • Implicit role: tbody has an implicit ARIA role of rowgroup
  • Use with th: When rows have headers, use <th scope="row"> in the first cell

JavaScript Interaction

The tbody element is useful for dynamic table manipulation.

// Add a new row to tbody const tbody = document.querySelector('table tbody'); const row = document.createElement('tr'); row.innerHTML = ` <td>New item</td> <td>Description</td> <td data-numeric>$99.00</td> `; tbody.appendChild(row); // Clear all data rows tbody.innerHTML = ''; // Get all data rows const dataRows = tbody.querySelectorAll('tr');

Related Elements

  • <table> - Parent container
  • <thead> - Header row group (appears before tbody)
  • <tfoot> - Footer row group
  • <tr> - Table rows within tbody
  • <td> - Data cells (primary content of tbody)
  • <th> - Row headers (use scope="row")