2.5: Tables & Data Presentation
Structure tabular data with HTML tables using proper semantic elements for headers, rows, and cells. Understand when to use tables and how to make them accessible.
1. Basic Table Structure
Simple Table
<table>
<tr>
<th>Name</th>
<th>Age</th>
<th>City</th>
</tr>
<tr>
<td>John Doe</td>
<td>28</td>
<td>New York</td>
</tr>
<tr>
<td>Jane Smith</td>
<td>34</td>
<td>Los Angeles</td>
</tr>
</table>
Elements:
<table>- Container for the table<tr>- Table Row<th>- Table Header cell (bold, centered by default)<td>- Table Data cell (regular text)
Rendered:
| Name | Age | City |
|---|---|---|
| John Doe | 28 | New York |
| Jane Smith | 34 | Los Angeles |
2. Table Sections (thead, tbody, tfoot)
Proper semantic structure with header, body, and footer sections:
<table>
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Laptop</td>
<td>$999</td>
<td>2</td>
<td>$1,998</td>
</tr>
<tr>
<td>Mouse</td>
<td>$25</td>
<td>5</td>
<td>$125</td>
</tr>
<tr>
<td>Keyboard</td>
<td>$75</td>
<td>3</td>
<td>$225</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Grand Total</td>
<td>$2,348</td>
</tr>
</tfoot>
</table>
Benefits of sections:
- Better semantic structure
- Screen readers can navigate by sections
- Can style sections differently
- Header/footer can remain visible when scrolling (with CSS)
Element order: <thead>, <tbody>, <tfoot> (browser will render <tfoot> at bottom regardless)
3. Table Caption
Use <caption> to provide a title for the table:
<table>
<caption>Monthly Sales Report - Q4 2024</caption>
<thead>
<tr>
<th>Month</th>
<th>Revenue</th>
<th>Expenses</th>
<th>Profit</th>
</tr>
</thead>
<tbody>
<tr>
<td>October</td>
<td>$50,000</td>
<td>$30,000</td>
<td>$20,000</td>
</tr>
<tr>
<td>November</td>
<td>$65,000</td>
<td>$35,000</td>
<td>$30,000</td>
</tr>
<tr>
<td>December</td>
<td>$80,000</td>
<td>$40,000</td>
<td>$40,000</td>
</tr>
</tbody>
</table>
Caption placement:
- Must be first child of
<table> - Displayed above table by default
- Can be positioned below with CSS:
caption-side: bottom;
4. Column and Row Spanning
Colspan (Spanning Columns)
Merge cells horizontally:
<table>
<tr>
<th colspan="3">Personal Information</th>
</tr>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>john@example.com</td>
</tr>
</table>
Result:
| Personal Information (spans 3 columns) | |---|---|---| | First Name | Last Name | Email | | John | Doe | john@example.com |
Rowspan (Spanning Rows)
Merge cells vertically:
<table>
<tr>
<th rowspan="2">Name</th>
<th colspan="2">Contact</th>
</tr>
<tr>
<th>Email</th>
<th>Phone</th>
</tr>
<tr>
<td>John Doe</td>
<td>john@example.com</td>
<td>555-1234</td>
</tr>
</table>
Complex Example:
<table>
<caption>Class Schedule</caption>
<thead>
<tr>
<th>Time</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
</tr>
</thead>
<tbody>
<tr>
<td>9:00 AM</td>
<td rowspan="2">Math<br>(Double Period)</td>
<td>English</td>
<td>Science</td>
</tr>
<tr>
<td>10:00 AM</td>
<td>History</td>
<td>Art</td>
</tr>
<tr>
<td>11:00 AM</td>
<td colspan="3" style="text-align: center;">Lunch Break</td>
</tr>
</tbody>
</table>
5. Accessible Tables
Scope Attribute
Define whether header applies to row or column:
<table>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Price</th>
<th scope="col">Stock</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Laptop</th>
<td>$999</td>
<td>15</td>
</tr>
<tr>
<th scope="row">Mouse</th>
<td>$25</td>
<td>50</td>
</tr>
</tbody>
</table>
Scope values:
scope="col"- Header for columnscope="row"- Header for rowscope="colgroup"- Header for group of columnsscope="rowgroup"- Header for group of rows
Headers Attribute (Complex Tables)
For complex tables with multiple header levels:
<table>
<tr>
<th id="name">Name</th>
<th id="math">Math</th>
<th id="science">Science</th>
</tr>
<tr>
<td headers="name">Alice</td>
<td headers="name math">95</td>
<td headers="name science">88</td>
</tr>
<tr>
<td headers="name">Bob</td>
<td headers="name math">82</td>
<td headers="name science">91</td>
</tr>
</table>
When to use:
- Simple tables → Use
scopeattribute - Complex tables → Use
headersattribute with IDs
Summary for Screen Readers
<table>
<caption>
Student Grades - Fall 2024
<details>
<summary>Table Description</summary>
<p>This table shows final grades for students in Math and Science courses.</p>
</details>
</caption>
</table>
6. Styling Tables
Basic Table Styling
<table class="styled-table">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Doe</td>
<td>Developer</td>
<td>Engineering</td>
</tr>
<tr>
<td>Jane Smith</td>
<td>Designer</td>
<td>Creative</td>
</tr>
</tbody>
</table>
<style>
.styled-table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
font-size: 0.9rem;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.styled-table thead tr {
background-color: #0066cc;
color: white;
text-align: left;
}
.styled-table th,
.styled-table td {
padding: 12px 15px;
}
.styled-table tbody tr {
border-bottom: 1px solid #ddd;
}
.styled-table tbody tr:hover {
background-color: #f5f5f5;
}
.styled-table tbody tr:last-of-type {
border-bottom: 2px solid #0066cc;
}
</style>
Striped Rows
.striped-table tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
.striped-table tbody tr:nth-child(even) {
background-color: white;
}
Bordered Table
.bordered-table {
border-collapse: collapse;
}
.bordered-table th,
.bordered-table td {
border: 1px solid #ddd;
padding: 8px 12px;
}
.bordered-table th {
background-color: #f0f0f0;
}
Sticky Header (Scrollable)
.scrollable-table-container {
max-height: 400px;
overflow-y: auto;
position: relative;
}
.scrollable-table thead th {
position: sticky;
top: 0;
background-color: #0066cc;
color: white;
z-index: 10;
}
7. Responsive Tables
Problem: Tables Overflow on Mobile
Tables with many columns don't fit on small screens
Solution 1: Horizontal Scroll
<div class="table-wrapper">
<table>
</table>
</div>
<style>
.table-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
}
.table-wrapper table {
min-width: 600px; /* Prevent compression */
}
</style>
Solution 2: Stacked Layout (Mobile)
<table class="responsive-table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Name">John Doe</td>
<td data-label="Email">john@example.com</td>
<td data-label="Phone">555-1234</td>
</tr>
<tr>
<td data-label="Name">Jane Smith</td>
<td data-label="Email">jane@example.com</td>
<td data-label="Phone">555-5678</td>
</tr>
</tbody>
</table>
<style>
/* Desktop: Normal table */
@media (min-width: 768px) {
.responsive-table {
width: 100%;
border-collapse: collapse;
}
.responsive-table th,
.responsive-table td {
padding: 12px;
border: 1px solid #ddd;
}
}
/* Mobile: Stacked layout */
@media (max-width: 767px) {
.responsive-table thead {
display: none; /* Hide header */
}
.responsive-table,
.responsive-table tbody,
.responsive-table tr,
.responsive-table td {
display: block;
width: 100%;
}
.responsive-table tr {
margin-bottom: 1rem;
border: 1px solid #ddd;
}
.responsive-table td {
text-align: right;
padding: 10px;
border-bottom: 1px solid #eee;
}
.responsive-table td::before {
content: attr(data-label);
float: left;
font-weight: bold;
}
.responsive-table td:last-child {
border-bottom: 0;
}
}
</style>
Solution 3: Card Layout (Mobile)
@media (max-width: 767px) {
.card-table {
display: block;
}
.card-table thead {
display: none;
}
.card-table tbody,
.card-table tr {
display: block;
}
.card-table tr {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
margin-bottom: 1rem;
padding: 1rem;
}
.card-table td {
display: block;
margin-bottom: 0.5rem;
}
.card-table td::before {
content: attr(data-label) ": ";
font-weight: bold;
}
}
8. When to Use Tables
✅ Use Tables For:
Tabular data:
<table>
<caption>Product Comparison</caption>
<thead>
<tr>
<th>Feature</th>
<th>Basic</th>
<th>Pro</th>
<th>Enterprise</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Storage</th>
<td>10 GB</td>
<td>100 GB</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Users</th>
<td>1</td>
<td>5</td>
<td>Unlimited</td>
</tr>
</tbody>
</table>
Data that benefits from comparison:
- Pricing tables
- Product specifications
- Statistical data
- Schedules and calendars
- Financial reports
❌ Don't Use Tables For:
Page layout:
<table>
<tr>
<td></td>
<td></td>
</tr>
</table>
<div class="layout">
<aside></aside>
<main></main>
</div>
Non-tabular content:
- Navigation menus (use
<nav>and lists) - Image galleries (use CSS Grid/Flexbox)
- Form layouts (use proper form elements)
- Page structure (use semantic HTML + CSS)
9. Complete Examples
Financial Report Table
<table class="financial-report">
<caption>
<strong>Quarterly Financial Report</strong>
<span>Q4 2024 - All figures in USD</span>
</caption>
<thead>
<tr>
<th scope="col">Category</th>
<th scope="col">Q3 2024</th>
<th scope="col">Q4 2024</th>
<th scope="col">Change</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Revenue</th>
<td>$2,450,000</td>
<td>$2,890,000</td>
<td class="positive">+18%</td>
</tr>
<tr>
<th scope="row">Expenses</th>
<td>$1,800,000</td>
<td>$1,950,000</td>
<td class="negative">+8.3%</td>
</tr>
<tr>
<th scope="row">Operating Income</th>
<td>$650,000</td>
<td>$940,000</td>
<td class="positive">+44.6%</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Net Profit</th>
<td>$455,000</td>
<td>$658,000</td>
<td class="positive">+44.6%</td>
</tr>
</tfoot>
</table>
<style>
.financial-report {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
font-family: 'Arial', sans-serif;
}
.financial-report caption {
padding: 1rem;
background: #f0f0f0;
border: 1px solid #ddd;
border-bottom: none;
text-align: left;
}
.financial-report caption strong {
display: block;
font-size: 1.2rem;
margin-bottom: 0.5rem;
}
.financial-report caption span {
color: #666;
font-size: 0.9rem;
}
.financial-report th,
.financial-report td {
padding: 12px;
text-align: left;
border: 1px solid #ddd;
}
.financial-report thead th {
background: #2c3e50;
color: white;
font-weight: bold;
}
.financial-report tbody th {
background: #ecf0f1;
font-weight: 600;
}
.financial-report tfoot th {
background: #34495e;
color: white;
font-weight: bold;
}
.financial-report tfoot td {
font-weight: bold;
}
.positive {
color: #27ae60;
font-weight: bold;
}
.negative {
color: #e74c3c;
font-weight: bold;
}
</style>
Pricing Table
<table class="pricing-table">
<caption>Choose Your Plan</caption>
<thead>
<tr>
<th scope="col">Feature</th>
<th scope="col">Free</th>
<th scope="col">Pro</th>
<th scope="col">Enterprise</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Monthly Price</th>
<td>$0</td>
<td><strong>$29</strong></td>
<td>Custom</td>
</tr>
<tr>
<th scope="row">Storage</th>
<td>5 GB</td>
<td>100 GB</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Users</th>
<td>1</td>
<td>Up to 10</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Support</th>
<td>Community</td>
<td>Email</td>
<td>24/7 Phone</td>
</tr>
<tr>
<th scope="row">API Access</th>
<td>❌</td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td><a href="/signup?plan=free" class="btn">Sign Up</a></td>
<td><a href="/signup?plan=pro" class="btn btn-primary">Get Pro</a></td>
<td><a href="/contact" class="btn">Contact Sales</a></td>
</tr>
</tfoot>
</table>
<style>
.pricing-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 2rem 0;
}
.pricing-table caption {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 1rem;
}
.pricing-table th,
.pricing-table td {
padding: 1rem;
text-align: center;
border: 1px solid #ddd;
}
.pricing-table thead th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 1.1rem;
}
.pricing-table tbody th {
text-align: left;
background: #f8f9fa;
font-weight: 600;
}
.pricing-table tbody tr:hover {
background: #f0f0f0;
}
.pricing-table tfoot td {
background: white;
border-top: 2px solid #667eea;
}
.btn {
display: inline-block;
padding: 0.5rem 1.5rem;
background: #e0e0e0;
color: #333;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
}
.btn-primary {
background: #667eea;
color: white;
}
</style>
10. Practical Exercises
Exercise 2.5.1: Basic Data Table
Create a table showing:
- List of 5 programming languages
- Columns: Name, Year Created, Type, Popularity
- Proper
<thead>,<tbody>,<tfoot>sections - Caption describing the table
- Use
scopeattributes for accessibility
Exercise 2.5.2: Complex Table with Spanning
Build a class schedule table with:
- Time slots in first column
- Days of week across top
- Use
rowspanfor double-period classes - Use
colspanfor lunch break across all days - Proper semantic structure
Exercise 2.5.3: Styled Pricing Table
Create a pricing comparison table with:
- Three pricing tiers (columns)
- At least 6 features (rows)
- Styled header with gradient or color
- Hover effects on rows
- Call-to-action buttons in footer
Exercise 2.5.4: Responsive Table
Build a responsive employee directory:
- Columns: Name, Email, Department, Phone, Status
- At least 8 rows of data
- Horizontal scroll on tablets
- Stacked card layout on mobile (using
data-labeltechnique) - Test responsiveness at different breakpoints
11. Knowledge Check
Question 1: What's the difference between <th> and <td>?
Show answer
`` is for table header cells (bold and centered by default, semantically indicates header). `` is for regular table data cells. Use `` for column/row headers to improve accessibility.Question 2: Why use <thead>, <tbody>, and <tfoot>?
Show answer
For semantic structure and accessibility. These sections help screen readers navigate tables, allow different styling, and enable features like sticky headers. Browser renders `` at bottom regardless of code order.Question 3: What does the scope attribute do?
Show answer
`scope` defines whether a header cell applies to a row (`scope="row"`), column (`scope="col"`), or group. This helps screen readers associate data cells with their headers for better accessibility.Question 4: How does colspan="3" work?
Show answer
It makes a cell span across 3 columns horizontally, merging those cells into one. You must reduce the number of cells in that row by 2 (since one cell now takes the space of three).Question 5: When should you NOT use tables?
Show answer
Don't use tables for page layout, navigation menus, image galleries, or any non-tabular content. Use CSS Grid/Flexbox for layouts. Tables are only for actual tabular data.Question 6: What's the best way to make tables responsive on mobile?
Show answer
Multiple approaches: 1) Horizontal scroll wrapper, 2) Stacked/card layout using CSS media queries and `data-label` attributes, 3) Hide less important columns on mobile. Choose based on data complexity and user needs.12. Common Mistakes
Using Tables for Layout
<table>
<tr>
<td width="200">Sidebar</td>
<td>Main content</td>
</tr>
</table>
<div class="layout">
<aside>Sidebar</aside>
<main>Main content</main>
</div>
Missing Semantic Structure
<table>
<tr><th>Name</th><th>Age</th></tr>
<tr><td>John</td><td>28</td></tr>
</table>
<table>
<caption>User List</caption>
<thead>
<tr><th>Name</th><th>Age</th></tr>
</thead>
<tbody>
<tr><td>John</td><td>28</td></tr>
</tbody>
</table>
Forgetting Accessibility
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
</table>
<table>
<tr>
<th scope="col">Product</th>
<th scope="col">Price</th>
</tr>
</table>
Not Handling Mobile
<table style="width: 1200px;">
</table>
<div style="overflow-x: auto;">
<table>
</table>
</div>
13. Key Takeaways
- Use tables for tabular data only - Not for page layout
- Structure with
<thead>,<tbody>,<tfoot>- Better semantics and accessibility - Always add
<caption>- Describes table purpose - Use
scopeattribute - Helps screen readers (scope="col"orscope="row") colspanandrowspan- Merge cells horizontally and vertically- Style with CSS -
border-collapse, hover effects, striped rows - Make responsive - Horizontal scroll, stacked layout, or hide columns on mobile
- First row/column often headers - Use
<th>instead of<td> - Test accessibility - Use screen reader or browser DevTools
- Consider alternatives - CSS Grid for complex layouts, not tables
14. Further Resources
Official Documentation:
Accessibility:
Responsive Tables:
Next Steps
Excellent! You now know how to create accessible, responsive HTML tables for presenting data.
In Lesson 2.6: Forms & Input Elements, you'll learn how to build interactive forms for collecting user input, including text fields, checkboxes, radio buttons, and form validation.