Better Tables With Grid CSS (and Subgrid)
Tables with more flexibility in column widths, for example "fr".
What Do I Mean By Better?
Grid CSS gives us much more control over how the column width behaves. For example, the concept of “fr” is unavailable with <table>
. Check out the whole list of column width features in the grid-template-columns documentation.
Tables with more flexibility in column widths.
Don’t Lose What Works
Yet, I don’t want to compromise on functionality. Some functionalities in the table element are not native to Grid CSS. Specifically, I found the following missing features:
Hovering row styles.
Semantics.
Align columns.
Each presented a different challenge to solve with Grid CSS.
Solution
Let me show you the solution, and then we review the styles, explaining how it works. In case you want to see the whole code, visit the Codepen project and feel free to learn about it.
We keep the same HTML to preserve HTML semantics:
<table class="two-expanding">
<thead>
…
</thead>
<tbody>
…
</tbody>
</table>
Then, we apply Grid CSS to the table elements.
There are two parts; first, apply “grid” in the table with three columns.
table {
display: grid;
grid-template-columns: 1fr 1fr max-content;
}
Then, apply styles to the <table>
children: (this part blew my mind)
tbody,
thead,
tr {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
}
The key is subgrid. I can’t claim all the credit because a colleague suggested it to me (thanks, David Aerne!).
With subgrid, the selected elements use the columns’ style set in the parent. It couples the cells of the table together, ensuring that the width of the column is aligned.
Last, we add a hover style in the rows:
tbody > tr:hover {
background-color: #f0f0f0;
}
Discarded Solutions
I'd like to show three possibilities I discarded to help you better understand how I came up with the solution.
Grid Only
I can accomplish the same UI by using a grid only:
<div role=”table” class="grid grid-only">
<div role="columnheader" class="header">First Name</div>
<div role="columnheader" class="header">Last Name</div>
<div role="columnheader" class="header">PhD</div>
<div role="cell">Sheldon</div>
<div role="cell">Cooper</div>
<div role="cell">Yes</div>
…
</div>
And CSS:
.grid {
display: grid;
grid-template-columns: 1fr 1fr max-content;
}
With one “grid” we don’t have table semantics or row hover styles.
Grid On Rows
Another option is to set the grid in the rows instead:
HTML:
<div role=”table” class="grid">
…
<div role="row" class="grid-row">
<div role="cell">Sheldon</div>
<div role="cell">Cooper</div>
<div role="cell">Yes</div>
</div>
…
<div>
CSS:
.grid-row {
display: grid;
grid-template-columns: 1fr 1fr max-content;
}
.grid-row:hover {
background-color: #f0f0f0;
}
It has the table semantics and the row hover styles, but the columns aren’t aligned:
That’s because the column widths of each row are not coupled.
Note: I could have solved this by not giving widths to the columns related to the content, like “max-content”.
Display: Contents
I can fix the alignment and keep the semantics by using display: contents;
in the children:
The HTML is the same as applying a grid on rows, but the CSS is different:
/* PARENT */
.grid {
display: grid;
grid-template-columns: 1fr 1fr max-content;
}
/* ROWS */
.grid-row {
display: display-contents;
}
.grid-row:hover {
/* NOT WORKING */
background-color: #f0f0f0;
}
I don’t have the hover style because the browser ignores the box with display: contents;.
Play With It
I built a Codepen to showcase a few examples and the discarded solutions. Feel free to go and play with the code.
Tables with Grid CSS are more flexible than native table elements.
Thanks to Elina and Bernat for reviewing this article!
Let me know what you think:
Do you have another solution?
How familiar are you with Grid?
Isn’t subgrid cool?
Have you implemented tables at work and never thought of using Grid?
Did you like the discarded examples?
Do you think you’ll use it at work?
Thanks!
Using box-shadow for border is a smart solution! Good idea!
Well, done. I have been trying to figure a way to do this. Borders around cells can get little tricky as they will affect the layout. For me it worked better to use box-shadow to simulate border as it would not affect the layout. I tried using outline for single color, but that had issues too. The border and outline width cannot be counted on to stay as set on mobile at different zoom levels :-(