Complex Tables: Styling & Accessibility
Lesson 42 โข Advanced Track
What You'll Learn
- Style tables with zebra striping, hover effects, and status badges
- Make tables responsive with data-label card stacking on mobile
- Create sticky table headers for scrollable data tables
- Use colspan and rowspan for complex multi-level tables
- Apply scope and caption for screen reader accessibility
- Build inline data visualisations inside table cells
๐ก Real-World Analogy
A well-structured HTML table is like a spreadsheet: <caption> is the sheet title, <thead> is the frozen header row, <tbody> is the data, and <tfoot> is the totals row. Without this structure, screen readers can't navigate the data โ it's like reading a spreadsheet with all the column headers removed.
Understanding HTML Table Structure
HTML tables have a rich semantic structure that most developers underuse. The <table> element should contain three sections: <thead> for header rows, <tbody> for data rows, and optionally <tfoot> for summary/total rows. The <caption> element provides a title that screen readers announce before navigating the table.
For accessibility, every <th> should have a scope attribute โ either scope="col" for column headers or scope="row" for row headers. This tells screen readers how to associate data cells with their headers, making the table navigable for visually impaired users.
The biggest styling challenge with tables is making them responsive. Tables are inherently wide, horizontal structures that don't fit on narrow mobile screens. The most common solution is the data-label pattern: on mobile, each row becomes a card, and each cell uses a ::before pseudo-element with content: attr(data-label) to show its column name inline.
๐ Table Element Reference
| Element | Purpose | Required? |
|---|---|---|
| <caption> | Table title/description for screen readers | Recommended |
| <thead> | Header row group (enables sticky headers) | Yes |
| <tbody> | Data row group | Yes |
| <tfoot> | Footer/summary row group | Optional |
| scope="col" | Marks th as a column header | Yes on th |
| colspan/rowspan | Merges cells horizontally/vertically | When needed |
1. Styled Data Table
This table demonstrates zebra striping with :nth-child(even), hover effects, status badges, monospace numbers, and a proper caption/thead/tbody/tfoot structure.
Styled Data Table with Status Badges
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
.table-wrapper { overflow-x: auto; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
table { width: 100%; border-collapse: collapse; background: white; }
caption { padding: 16px; font-weight: 700; font-size: 1.1rem; text-align: left; background: #1976D2; color: white; }
thead th { background: #E3F2FD; color: #1565C0; padding: 12px 16px; text-align: left; font-size: 0.85rem; text-transform
...2. Responsive Card-Stacking Table
On mobile, this table transforms each row into a card. The data-label attribute on each <td> is rendered as inline labels using ::before pseudo-elements. The <thead> is hidden entirely on small screens.
Responsive Table with data-label
<!DOCTYPE html>
<html><head><meta name="viewport" content="width=device-width, initial-scale=1"><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; margin-bottom: 8px; }
.subtitle { color: #999; margin-bottom: 24px; }
.table-wrapper { overflow-x: auto; border-radius: 12px; border: 1px solid #e0e0e0; }
table { width: 100%; border-collapse: collapse; }
thead th { background: #f5f5f5; padding: 12px 16px; text-align: left; font-size: 0.85rem; color: #666; text-tr
...3. Sticky Header with Inline Charts
For long data tables, sticky headers keep column names visible while scrolling. Use position: sticky; top: 0; on <th> elements inside a scrollable container. This example also shows inline progress bars inside table cells.
Sticky Header Table with Progress Bars
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; margin-bottom: 16px; }
.scroll-table { max-height: 300px; overflow-y: auto; border-radius: 12px; border: 1px solid #e0e0e0; }
table { width: 100%; border-collapse: collapse; }
thead th { position: sticky; top: 0; background: #1976D2; color: white; padding: 12px 16px; text-align: left; font-size: 0.85rem; z-index: 1; }
tbody td { padding: 10px 16px; border-bottom: 1px solid #f0f0f0
...4. colspan & rowspan
Complex reports often need merged cells. colspan merges cells horizontally across columns, while rowspan merges cells vertically across rows. This is essential for financial reports, schedules, and grouped data displays.
colspan & rowspan Financial Report
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; margin-bottom: 16px; }
table { width: 100%; border-collapse: collapse; border: 2px solid #1976D2; }
th, td { padding: 12px 16px; border: 1px solid #ddd; text-align: center; }
th { background: #E3F2FD; color: #1565C0; font-size: 0.85rem; }
.group-header { background: #1976D2; color: white; font-weight: 700; }
.row-header { background: #f5f5f5; font-weight: 600; text-align: left; }
...When to Use Tables
- DO use tables for: Tabular data โ financial reports, comparison charts, schedules, employee directories, product specifications, analytics dashboards.
- DON'T use tables for: Page layout. That's what CSS Grid and Flexbox are for. Tables for layout is a 1990s anti-pattern that breaks accessibility and responsiveness.
- Consider alternatives when: Data is simple (1-2 columns) โ a definition list (
<dl>) might be more semantic. On mobile, card layouts often work better than tables.
Common Mistakes
- Using tables for layout โ Tables are for tabular data only. Use Grid/Flexbox for page layout.
- Missing scope on th โ Add
scope="col"orscope="row"so screen readers can associate data with headers. - Not handling horizontal overflow โ Wrap tables in a container with
overflow-x: autoto enable horizontal scrolling on mobile. - Forgetting border-collapse โ Without
border-collapse: collapse, table cells have double borders and spacing between them. - No hover feedback on rows โ Users scanning large tables need visual help. Add
tr:hoverbackground changes. - Missing caption โ Screen reader users rely on
<caption>to understand what data the table contains before navigating it.
๐ Lesson Complete
- โ
Use
<caption>,<thead>,<tbody>,<tfoot>for semantic structure - โ
Zebra striping with
:nth-child(even)improves readability - โ
data-label+ CSS pseudo-elements make tables responsive as cards - โ
position: sticky; top: 0on<th>creates sticky headers - โ
colspanmerges horizontally,rowspanmerges vertically - โ
Always add
scope="col"orscope="row"to<th>elements - โ
Wrap tables in
overflow-x: autocontainers for mobile - โ Next lesson: Print Styles for export-friendly layouts
Sign up for free to track which lessons you've completed and get learning reminders.