Lists Are ul/li, Tables Are th/td
MUST: Lists are `<ul>`/`<ol>` + `<li>`; tables are `<table>` with `<th scope="col|row">` for headers. A `<div className="space-y-2">` of divs announces nothing — no "list, 5 items", no way to skip it — and a div grid with a bold header row reads a cell as a naked "42" instead of "Revenue, Q3, 42". Bold is not a header. Tailwind's `list-none` drops the marker without dropping the semantics, so there is no styling reason to use divs.
A stack of divs looks like a list and a grid of divs looks like a table, but neither announces any structure to a screen reader
Bad
Good
Why it matters
These are the two most common div-soup offences in generated markup, because a `<div className="space-y-2">` of `<div>`s and a flexbox grid with a bold header row *look* exactly right. What they lose is everything a non-visual user navigates by. A real `<ul>` announces "list, 5 items" and gives the reader a way to jump to the next item or skip the list entirely; a stack of divs announces nothing, and the reader has no idea how much is left.
The table case is worse, because the loss is per-cell: ibelick's companion rule is "tables must use th for headers when applicable", and a `<th scope="col">` is what makes a screen reader read a cell in the middle of the grid as "Revenue, Q3, 42" instead of a naked "42" with no idea which column or row it belongs to. That association cannot be recovered from visual weight — bold is not a header. content-semantics-first states the principle in general; this is what it actually costs in the two places it is most often ignored.
Note that Tailwind's `list-none` removes the marker without removing the semantics, so there is no styling reason to reach for divs.