To make an HTML table responsive, pick one of three patterns: scroll it inside a wrapper, stack each row into a block, or hide the secondary columns below a breakpoint.
There is no fourth option worth using, and the choice is decided by how people read the table, not by how wide it is.

Which pattern fits which table
| Pattern | Fits | Breaks |
|---|---|---|
| Scroll sideways | Grids, figures compared across rows and columns | Nothing, but the reader must discover the scroll |
| Stack into blocks | Lists where each row is one item | Any comparison down a column |
| Hide columns | Wide reports with a clear primary and secondary split | Anything the reader needs to act on |
The middle row is the one applied too often. A monthly figures table turned into a stack of cards is no longer a table, and the whole reason someone opened it was to compare February against March.
Pattern one: scroll
The default answer, and one line of CSS.
<div class="table-scroll" tabindex="0" role="region" aria-label="Monthly figures">
<table>...</table>
</div>
.table-scroll { overflow-x: auto; max-width: 100%; }
table { min-width: 640px; }
The wrapper scrolls, not the table, because a table element does not honour overflow. The min-width gives the table a reason to be wider than the box, otherwise it shrinks to fit and the columns crush.
Add a sticky first column and the pattern becomes much better on a phone, because the row labels stay in place while the numbers slide past.
Overflow scroll covers the details, including how to signal that the table scrolls at all.

Pattern two: stack
For a table that is really a list, each row can become a small block below a breakpoint.
@media (max-width: 600px) {
thead { display: none; }
tr { display: block; margin-bottom: 1rem; border: 1px solid #e3e3e3; }
td { display: block; text-align: right; }
td::before {
content: attr(data-label);
float: left;
font-weight: 600;
}
}
Each cell needs data-label="Status" in the markup, because the header row is gone and the value would otherwise be unlabelled.
Two costs to know about. The labels are duplicated into every row, so the markup grows. And display: block on table elements removes their table semantics, which changes what a screen reader announces.
The second cost is the serious one. If the table carries anything a reader depends on, test it with a screen reader before shipping the stack.
Pattern three: hide columns
Wide reports often have two or three columns that carry the answer and several that support it.
@media (max-width: 720px) {
.col-secondary { display: none; }
}
Put the class on the <th> and the matching <td> in every row, or use nth-child if the positions are stable. Keep the row identifier and the primary figures.
Say somewhere that the table is fuller on a wider screen. A silently truncated table looks like missing data.
The settings that apply to all three
Three things are worth doing whichever pattern you pick.
- The viewport tag. Without
<meta name="viewport" content="width=device-width,initial-scale=1">the phone renders a desktop width page and zooms out, and none of the media queries fire. See the viewport meta tag. - Border box sizing. Padding counts inside the declared width, so column widths mean what they say. See box sizing.
- Sensible column widths. A
<colgroup>with percentages adapts on its own. See column width.
What none of the three patterns fixes
A table with eleven columns is hard to read on a laptop too. Responsive CSS moves the problem to a smaller screen rather than solving it.
Before reaching for a media query, check whether the table is doing two jobs at once. A summary table with five columns and a detail table behind a link is often better than one table with sixteen.
The same applies to precision. Figures given to four decimal places widen every column for information almost nobody reads at that resolution.

Testing it properly
Resizing the browser window gets you most of the way. Three checks decide whether it is done.
- At 360 pixels wide, does the page scroll sideways? It should not. Only the table wrapper should.
- Can you reach every value? Scroll the table to the far right and confirm the last column is reachable by touch and by keyboard.
- Does the header still identify the columns? After scrolling, either the header is pinned or the first column is, or the reader is guessing.

Open the file in the HTML file opener and narrow the window there, so nothing from your own project stylesheet props it up.
Getting the responsive table to readers
A responsive table is only responsive if the reader opens the real page. A screenshot or a PDF export fixes one layout, usually the desktop one, which is the layout least likely to be the one they need.
Paste the HTML into a NOS document. It renders as written, media queries included, at an address of its own. Share, then Share link, then Create link produces an unlisted link that opens in one click on a phone.
The address survives edits, so a corrected number reaches everyone who already has the link, with nothing resent. HTML to link is the general version of that route.