Every hardware or electronics product page ends up with a spec table. Thread size, length, material, voltage range, what's in the box. It's the part of the page people scroll straight to when they already know what they want.
It's also the part that gets built as a pile of <div>s, or a two-column grid that falls apart below 400px. A few notes on doing it in a way that holds up.
Use a table. It is tabular data.
Key/value specs are exactly what <table> is for. You get row and header relationships for free, and screen readers can announce "Material, stainless steel" instead of two unrelated words.
<table class="specs"> <caption>Specifications</caption> <tbody> <tr> <th scope="row">Material</th> <td>Stainless steel A2</td> </tr> <tr> <th scope="row">Thread</th> <td>M8</td> </tr> <tr> <th scope="row">Length</th> <td>60 mm</td> </tr> </tbody> </table> The two details people skip: scope="row" on the label cell, and a <caption>. The caption can be visually hidden if the section already has a heading, but having it means the table has a name when someone jumps between tables.
A description list (<dl>) is also a fair choice for a short, simple list of specs. Once you have more than a handful of rows, or anything that compares variants, go with the table.
Comparing variants is where it gets harder
When one product comes in several sizes, the spec table often turns into a grid: sizes across the top, properties down the side.
<table class="specs specs--compare"> <caption>Sizes</caption> <thead> <tr> <td></td> <th scope="col">6 mm</th> <th scope="col">8 mm</th> <th scope="col">10 mm</th> </tr> </thead> <tbody> <tr> <th scope="row">Drill bit</th> <td>6 mm</td><td>8 mm</td><td>10 mm</td> </tr> <tr> <th scope="row">Screw size</th> <td>4–5 mm</td><td>5–6 mm</td><td>6–8 mm</td> </tr> </tbody> </table> (The values here are just example data to show the shape.)
With both scope="col" and scope="row", a screen reader can announce the cell with both headers. Without them, someone tabbing through hears a list of numbers with no context.
Making it fit on a phone
There are two honest options. Pick based on the table's shape.
Two-column key/value tables: let them wrap. Most of the time the only CSS you need is to stop the label column from being squeezed:
.specs { width: 100%; border-collapse: collapse; } .specs th, .specs td { padding: .5rem .75rem; text-align: start; vertical-align: top; } .specs th { width: 40%; font-weight: 600; } .specs tr + tr > * { border-top: 1px solid #e5e7eb; } Note text-align: start instead of left. If the site also runs in a right-to-left language, it flips on its own.
Wide comparison tables: let them scroll horizontally inside a container, and don't fight it.
<div class="table-scroll" role="region" aria-labelledby="sizes-cap" tabindex="0"> <table class="specs specs--compare"> <caption id="sizes-cap">Sizes</caption> ... </table> </div> .table-scroll { overflow-x: auto; } .specs--compare th[scope="row"] { position: sticky; inset-inline-start: 0; background: #fff; } The tabindex="0" and role="region" with a label let keyboard users focus the scroll area and move it with the arrow keys. The sticky first column keeps the row labels visible while the values scroll.
What I'd avoid is the "turn every row into a card with display: block" trick. It looks neat, but changing a table's display can strip its table semantics in some browser and screen reader combinations, and then you've lost the thing that made it a table in the first place. If you go that route, test it with an actual screen reader.
Small things that make specs easier to read
- Units in the value, consistently. "60 mm" in every row, not "60" in one row and "6 cm" in the next. If you show two unit systems, pick one as primary and keep the other in a smaller span.
- Non-breaking space between number and unit.
60 mmso the unit never wraps onto its own line. - Ranges with an en dash.
4–5 mmreads better than4-5 mm, and a screen reader is more likely to read it as a range. - Don't hide the important rows behind "Show more". If people come to the page to check one dimension, that dimension should be visible without a click.
- Keep the spec text in the HTML. Specs baked into an image can't be searched, translated, zoomed cleanly, or read aloud.
A quick check
Open a product page with lots of specs, for example a category like screws and anchors on ramo or any store that sells similar items, and try three things:
- Narrow the window to 360px. Does anything overflow the page instead of its own container?
- Tab through. Can you reach and scroll the wide table with the keyboard?
- Turn on a screen reader and move into a value cell. Do you hear the row header with it?
If all three are fine, the table is in good shape. If not, the fix is usually a few attributes and a dozen lines of CSS, not a new component.
