Tables
Tables present structured data in rows and columns. They are the right choice when readers need to compare values, scan for specific entries, or understand relationships between data points.
The prose table wrapper provides accessible horizontal scrolling for wide tables while preserving native table semantics for screen readers.
When to use
- Data has a clear tabular structure — rows represent items, columns represent attributes.
- Readers need to compare values across rows — bank financial metrics, deposit limits by category, quarterly filing statistics.
- The data has consistent attributes — every row shares the same columns.
- Presenting regulatory data, filing statistics, or financial summaries where precision and scannability matter.
When not to use
- Don't use tables for layout. Tables are for data, not for positioning content side by side.
- Don't use a table for a single column of items. A bulleted or numbered list is simpler and more accessible.
- Don't use a table when each "row" needs rich content (paragraphs, images, nested lists). Use a card layout or definition list instead.
Examples
Best practices
Include a descriptive caption
Include a caption that describes what the table contains — "FDIC-insured deposit balances by ownership category." The caption serves as the table's accessible name.
Don't omit or use vague captions
Don't omit the caption or use vague text like "Data table." Without a meaningful caption, screen reader users cannot tell what data the table presents before navigating into it.
Right-align numeric columns
Right-align numeric columns using the .prose-td-numeric class on data cells and .prose-th-numeric on their headers so decimal points and digit places line up for easy comparison.
Don't left-align financial figures
Left-aligned numbers are harder to compare visually. Misaligned decimal points force readers to count digits manually.
Keep tables scannable
Aim for 3 to 7 columns. Focused tables are easier to read and less likely to require horizontal scrolling.
Don't cram too many columns
Dozens of columns in one table overwhelm readers. Split complex datasets into multiple focused tables, each answering a specific question.
Use the table footer for summary values
Use <tfoot> for totals, averages, or summary values. The footer row receives distinct styling that separates it from individual entries.
Don't put summaries inline as regular rows
Placing summary calculations as regular body rows makes it difficult to distinguish aggregate data from individual entries.
Interaction behavior
- Horizontal scrolling — Tables wider than their container become horizontally scrollable. The wrapper provides keyboard-accessible scrolling: users can Tab to the table area and scroll with arrow keys.
- Row hover highlight — On hover, table rows receive a subtle background highlight to help readers track across columns. The transition is suppressed under
prefers-reduced-motion. - Striped rows — Alternating row backgrounds provide a visual guide for scanning long tables without adding visual noise.
Content guidelines
Headers should label the data clearly and specifically. A header read in isolation should tell the reader what values appear in that column.
Putting units in the header keeps cells clean and avoids redundant text that clutters the table.
All dates should use the same format, all currencies the same precision. Inconsistent formatting forces readers to mentally translate values.
Accessibility
- Every table needs a descriptive label. Use a
<caption>element or give the scrollable wrapper anaria-labelthat describes what data the table contains. - Screen reader users navigate tables cell by cell. Column and row headers tell them where they are — make sure headers are specific enough to be useful in isolation.
- The scrollable wrapper is keyboard accessible. Readers can Tab to it and scroll with arrow keys without a mouse. The wrapper shows a visible focus ring when focused.
- Don't merge cells unless the data genuinely spans multiple columns or rows. Merged cells confuse screen reader navigation and break the predictable row-column grid.