On this page
A data table is a comparison surface, not a collection of miniature cards. Its layout should make repeated values easy to scan while preserving the relationships between columns. Start with the questions readers need to answer, then decide which visual details deserve space.
Prioritize columns before styling rows
List the columns in order of task relevance. In an invoice table, identity, due date, amount, and status may matter more than the internal record identifier. Keep the essential comparison fields visible together. A table becomes harder to use when decorative metadata forces meaningful values outside the initial view.
Use the Flexbox Playground to prototype the toolbar above the table: search, filters, result count, and batch actions. It is useful for those controls, not a reason to replace table semantics with flex rows. The body should retain meaningful relationships between headers and cells, as explained in the WAI tables tutorial.
Define a column priority and a content policy for every field. Names may wrap, identifiers may need copying, and amounts should remain readable in full. Those differences matter more than making all columns equal width.
Choose density without sacrificing scan paths
Set row density according to the content and interaction model. A table with single-line text can be more compact than one containing avatars, descriptions, and two-line statuses. Try a comfortable starting row height around 48 pixels, then compare a compact alternative with real data rather than declaring one universal density.
Align text to the reading direction and comparable numbers to the end of their cells. Use tabular numerals when the chosen font supports them. Currency values with different digit counts become easier to compare when their decimal positions remain visually stable. Keep the currency unit clear instead of relying on the page context alone.
Use quiet horizontal separators or restrained alternate-row backgrounds to support tracking. Strong borders around every cell can overwhelm the actual values. If rows are selectable, ensure the selected treatment remains distinguishable from hover and from any alternating stripe.
Limit status decoration. A small label such as Overdue can be useful; six bright pills in every row create a visual competition. Use status emphasis proportionally, reserving stronger treatment for conditions that actually require attention. Check the appearance when every row has the same status, not only the attractive mixed example.
Build a resilient table surface
The following CSS handles the surface and alignment of a native table. It does not implement sorting, selection, or pagination. Those behaviors need explicit controls and application logic.
.table-scroll {
overflow-x: auto;
max-inline-size: 100%;
}
.records {
inline-size: 100%;
border-collapse: collapse;
}
.records th,
.records td {
padding: .875rem 1rem;
text-align: start;
border-block-end: 1px solid #d7dde5;
}
.records .amount {
text-align: end;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.records .identity { min-inline-size: 12rem; }Use MDN's table reference when deciding which elements express the content. Avoid making every cell a fixed width. Give the main identity field enough space to distinguish records, then constrain predictable values such as short dates or compact counts.
When content overflows, the horizontal scroll area should be discoverable and reachable. Do not hide the scrollbar just to create a cleaner screenshot. Coordinate the table's wrapper with the surrounding container so one long identifier does not stretch the complete page.
Keep actions separate from comparison
Place row actions consistently, usually in a predictable final column. Do not scatter edit, download, and delete controls through unrelated data columns. If one action is very common, showing it directly can be clearer than putting every command behind an overflow menu.
A row-wide click target and independent controls inside the row need careful behavior. Visually, make the primary destination apparent through its name or link treatment. Avoid presenting plain numeric cells as if each were a separate navigation target. Preserve text selection and copying where readers need them.
For batch operations, keep selection controls in the same position across rows. Show the selected count near the batch toolbar and make the scope clear: selected records are not automatically every record matching the current filter. A layout that hides that distinction can create serious mistakes.
Keep sorting controls associated with their headers. Reserve space for the indicator so enabling sorting does not nudge every column. A small arrow alone may not explain whether a column is interactive, especially if unsorted columns look identical to plain labels.
Review real records and constrained screens
Create a stress dataset with a long name, a missing value, a large negative amount, an unfamiliar status, and a date at the edge of the expected range. Decide whether missing information appears as a dash or an explicit phrase, and use that choice consistently.
On narrow screens, horizontal scrolling may preserve comparison better than transforming every row into a tall card. If you do provide an alternate list, decide which fields remain visible and how their labels appear. Do not silently drop the values that distinguish records merely to avoid overflow.
Check colors with the Contrast Checker, especially muted metadata and status text. Review the WCAG quick reference for relevant keyboard, focus, and visibility expectations. A native table supplies useful structure, but custom controls still need careful implementation.
Finally, compare the table at zero, one, and many records. The toolbar should not jump into a confusing new position when pagination appears. Document the overflow policy, alignment rules, truncation behavior, and available density settings so implementation remains predictable as the dataset grows.
Compare density with a timed task
Build two versions of the same twenty-record invoice list, with 48-pixel and 40-pixel rows as trial settings. Ask reviewers to locate the largest overdue balance, copy its invoice identifier, and select three records for export. Keep the dataset and column widths identical so you are evaluating density rather than different content.
Record wrong-row selections and lost scan position as well as completion time. A compact version that reveals more records but increases selection mistakes is not necessarily an improvement. Check the actual button and checkbox targets separately; reducing cell padding must not leave controls difficult to operate.
Include a two-line customer name in both versions. If that row grows, decide whether variable heights are acceptable or whether the identity field needs a different content arrangement. Do not clip the second line just to preserve a tidy screenshot. Offer a density preference only when both settings remain usable, and preserve that choice across navigation so returning users do not repeatedly rebuild their working view.
Key takeaways
- Prioritize comparison fields before choosing decoration.
- Match density to content and interactive controls.
- Align comparable values consistently and preserve important numbers.
- Keep row actions and batch operations visually distinct.
- Test long records and define overflow rather than hiding it.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor