⚠ Serious caveats below, don't skip them, please. ⚠
Not sure what exactly are your objections against true HTML tables (and have some doubts about their soundness), but since your HTML structure is pretty close to a regular table and the intended visual fits that, I don't see a reason for using grid and not using display that is specifically indended for this purpose:
## `display: table`
Applying just
```CSS
.wrapper { display: table; }
.box { display: table-cell; }
.row { display: table-row; }
```
at your HTML will get you there without other tweaks, and even provies some flexibility:
<!-- begin snippet: js hide: false console: true babel: false babelPresetReact: false babelPresetTS: false -->
<!-- language: lang-css -->
.wrapper { display: table; }
.box { display: table-cell; }
.row { display: table-row; }
/* Embelishments… */
.box {
border: solid 1ch transparent;
border-radius: .5lh;
background-color: canvas;
color: canvasText;
}
.spacious {
border-collapse: separate; /* Default */
border-spacing: 1ch;
margin: -1ch;
}
.condensed {
border-collapse: collapse;
margin: -.5ch;
}
html {
background-color: #666;
color-scheme: light dark;
}
<!-- language: lang-html -->
<div class="wrapper" id="w">
<div class="box a">col 1</div>
<div class="box b">col 2</div>
<div class="box c">col 3</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
</div>
<hr>
<div class="wrapper spacious">
<script>document.write(w.innerHTML)</script>
</div>
<hr>
<div class="wrapper condensed">
<script>document.write(w.innerHTML)</script>
</div>
<!-- end snippet -->
Note the "magical" adoption of the colum "headers" into virtual table-row-group. This is not hack, it is really specified to work like this.
### Caveats
(This applies even for the `grid`, `flex` and whatever other solutions.)
In short: avoid this, if possible.
Problems:
- Unintuitive reading order: since the sample suggests that cells semantically belong under their header, the actual semantinc order would be confusing for screen readers and text selection.
- As a result, broken accessibility.
In order to have this at least somehow accessible variant, we'd need to annotate every single element with Aria roles (`[role="table"]`, `[role="columnheader"]`, `[role="row"]`, `[role="cell"]`) and referrences (`[id]` on "headers", `[aria-labelledby]` on cells):
```HTML
<div role="table" aria-label="What is this table about?">
<div class="a" role="columnheader" id="hdr-1">col 1</div>
<div class="b" role="columnheader" id="hdr-2">col 2</div>
<div class="c" role="columnheader" id="hdr-3">col 3</div>
<div role="row">
<div class="d" role="cell" aria-labelledby="hdr-1">short data</div>
<div class="e" role="cell" aria-labelledby="hdr-2">a really long piece of data</div>
<div class="f" role="cell" aria-labelledby="hdr-3">short data</div>
</div>
<div role="row">
<div class="d" role="cell" aria-labelledby="hdr-1">short data</div>
<div class="e" role="cell" aria-labelledby="hdr-2">a really long piece of data</div>
<div class="f" role="cell" aria-labelledby="hdr-3">short data</div>
</div>
</div>
```
Since the pseudo-THs (`columnheader`s) are _floating in a vaccuum_, they cannot have `[scope="col"]` and we really should use explicit labelledby→id bindings.
There is a solutions for your question
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.wrapper {
display: grid;
grid-template-columns: repeat(3, auto);
background-color: #fff;
color: #444;
max-width: 800px;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 10px;
font-size: 150%;
}
.row {
display: contents;
}
<!-- language: lang-html -->
<div class="wrapper">
<div class="box a">col 1</div>
<div class="box b">col 2</div>
<div class="box c">col 3</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of dataa really long piece of data</div>
<div class="box f">short data</div>
</div>
<!-- Table Row -->
<div class="row">
<div class="box d">short data</div>
<div class="box e">a really long piece of data</div>
<div class="box f">short data</div>
</div>
</div>
<!-- end snippet -->