Scrolling bar in a <Table> with "responsive" columns

I'm trying to design a table with a scroll bar. The problem is that the columns don't adjust according to the information contained in the scrollbar. All the columns keep the same proportion because of the scrollbar located in <tbody>. I therefore used Typescript code to modify the width of each column according to its content. It works fine, but the alignment of columns and values is not good. What's more, the scroll bar is inside the last column and alters the style of the buttons contained in the cells. I don't know how to make the columns proportional and aligned according to the values, so that the style of the buttons isn't altered and the scroll bar is more outwards. You can find the code on GitHub in the link "Click here". And on Stackblitz "Click here" Thanks for your help
StackBlitz
Vitejs - Vite (forked) - StackBlitz
Next generation frontend tooling. It's fast!
44 Replies
ἔρως
ἔρως2mo ago
have you tried using grid? i've made tables responsive before by using grid
Diolix
DiolixOP2mo ago
No I didn't, I will try. But I would like to understand in my case how can I make it works 🙂
ἔρως
ἔρως2mo ago
well, instead of using the table to layout it, use grid for the layout
tbody {
width: calc(100% - 2.5em);
display: block; /* <-- right there */
max-height: 8em;
overflow-y: auto;
}
tbody {
width: calc(100% - 2.5em);
display: block; /* <-- right there */
max-height: 8em;
overflow-y: auto;
}
^ this is what makes the table's layout behave like absolute crap
Diolix
DiolixOP2mo ago
Thank you for your advice, can you give a little bit more about the code ? maybe you mean replace all of the table code by a grid code ?
ἔρως
ἔρως2mo ago
not all of it just the layouting part just for the <tr>
Chris Bolson
Chris Bolson2mo ago
There is so much going on in that CSS it is hard to see the wood for the trees. I suspect that one of your issues is that you have applied display:flex to the td cells with the class "action" - this is messing up your column alignment. My first suggestion would be to place the buttons within a container within the cell, something like this:
<td data-column="actions" >
<div class="actions">
<button class="btn-half-sphere edit-btn"></button>
<button class="btn-half-sphere delete-btn"></button>
</div>
</td>
<td data-column="actions" >
<div class="actions">
<button class="btn-half-sphere edit-btn"></button>
<button class="btn-half-sphere delete-btn"></button>
</div>
</td>
ἔρως
ἔρως2mo ago
it starts with the tbody having display: block it completely ruins the layouting of the table display: flex for the td just makes it even worse and then adding a div really doesn't help it at all
Diolix
DiolixOP2mo ago
@Chris Thanks for your suggestion, it solved part of the problem. @ἔρως Can you to provide more code for your idea, as all the attempts I've made have been unsuccessful.
ἔρως
ἔρως2mo ago
literally display: grid with grid-template-columns set on each tr
Diolix
DiolixOP2mo ago
@ἔρως Sorry, but this doesn't make the trick, or I haven't code your idea correctly. So please provide any complete fixes to be made. Thank you very much. 🙂
Chris Bolson
Chris Bolson2mo ago
As Epic has said, display:block is messing things up. However, in reality there is more to it than that. For example the styling directly on the <th> contents to make them look like buttons is also messing with things (in a similar way to what I mentioned about about the buttons). It would be better to style child elements than the cell itself. I would change the HTML to this:
<table>
<thead>
<tr>
<th data-column="column1" class="cardHeader"><span>Quantity</span></th>
<th data-column="column2" class="cardHeader"><span>Leaflet</span></th>
<th data-column="column3" class="cardHeader"><span>Format</span></th>
<th data-column="actions"></th>
</tr>
</thead>
<tbody>
<tr>
<td data-column="column1">10</td>
<td data-column="column2">A4</td>
<td data-column="column3">Paperback</td>
<td data-column="actions" >
<div class="actions">
<button class="btn-half-sphere edit-btn"></button>
<button class="btn-half-sphere delete-btn"></button>
</div>
</td>
</tr>
...
</tbody>
</table>
<table>
<thead>
<tr>
<th data-column="column1" class="cardHeader"><span>Quantity</span></th>
<th data-column="column2" class="cardHeader"><span>Leaflet</span></th>
<th data-column="column3" class="cardHeader"><span>Format</span></th>
<th data-column="actions"></th>
</tr>
</thead>
<tbody>
<tr>
<td data-column="column1">10</td>
<td data-column="column2">A4</td>
<td data-column="column3">Paperback</td>
<td data-column="actions" >
<div class="actions">
<button class="btn-half-sphere edit-btn"></button>
<button class="btn-half-sphere delete-btn"></button>
</div>
</td>
</tr>
...
</tbody>
</table>
And replace the table related CSS to something like this:
.table-wrapper {
position: relative;
width: 100%;
height: 10em;
overflow-y: auto;
scrollbar-gutter: stable; /* space for scrollbar*/
}
table {
width: 100%;
border-collapse: collapse;
}
thead th{
position: sticky;
top: 0;
background: #f9f9f9;
z-index: 1;
}
th,td {
padding: 8px;
border: none;
text-align: center;
}
/* Styling for table headers (Card-like appearance) */
.cardHeader > span {
padding: 0.5em;
background: white;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
transition: transform 0.2s, box-shadow 0.2s;
background: linear-gradient(145deg, #ffffff, #f5f5f5);
}
.cardHeader:hover > span {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.table-wrapper {
position: relative;
width: 100%;
height: 10em;
overflow-y: auto;
scrollbar-gutter: stable; /* space for scrollbar*/
}
table {
width: 100%;
border-collapse: collapse;
}
thead th{
position: sticky;
top: 0;
background: #f9f9f9;
z-index: 1;
}
th,td {
padding: 8px;
border: none;
text-align: center;
}
/* Styling for table headers (Card-like appearance) */
.cardHeader > span {
padding: 0.5em;
background: white;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
transition: transform 0.2s, box-shadow 0.2s;
background: linear-gradient(145deg, #ffffff, #f5f5f5);
}
.cardHeader:hover > span {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
Note - I haven't tried Epics suggestion of using grid, that may be simpler than what I am suggesting.
ἔρως
ἔρως2mo ago
the grid idea is just for allowing the tbody to be scrollable while keeping a sensible layout for desktop and mobile, and to make it responsive what ive done in the past is to do something like grid-template-columns: 1f 1f auto 50px and when things get tight i just do 1fr 1fr or 1fr auto or something like that, depending on the expectations and obviously, hide the thead, so it doesnt look scuffed that is one of the things that got me the interview at my current job
Diolix
DiolixOP2mo ago
@ἔρως and no need of typescript code ?
ἔρως
ἔρως2mo ago
nope, 100% css
Diolix
DiolixOP2mo ago
Base on what you said I did this coding:
table {
width: 100%;
border-collapse: separate;
border-spacing: 15px 0;
}

thead tr{
display: grid;
grid-template-columns: auto auto auto auto;
}

/* Set fixed height for tbody to show 3 rows */
tbody {
display: block;
overflow-y: auto;
max-height: 8em;
}

tr {
display: contents;
}

tbody tr {
display: grid;
grid-template-columns: auto auto auto auto;
width: 100%;
}
table {
width: 100%;
border-collapse: separate;
border-spacing: 15px 0;
}

thead tr{
display: grid;
grid-template-columns: auto auto auto auto;
}

/* Set fixed height for tbody to show 3 rows */
tbody {
display: block;
overflow-y: auto;
max-height: 8em;
}

tr {
display: contents;
}

tbody tr {
display: grid;
grid-template-columns: auto auto auto auto;
width: 100%;
}
Has you can see on the screenshot the columns are not aligne 😦
No description
ἔρως
ἔρως2mo ago
to have the thead and tbody exactly the same, i think you need to use sub-grids or, as the cost of usability, skip the thead your problem is that your data isnt uniform at all, and you are trying to auto your way into it but you can do it by setting the first and last column to a fixed width and everything else will align, or use subgrid quantity will never be bigger than, say, 80px the column with the buttons? same thing 80px 1fr 1fr 80px and it should look almost the same if the word doesnt fit, 120px for quantity
Diolix
DiolixOP2mo ago
Well the code should be reusable so I need "auto" that's my main point and the difficulty 🙂
ἔρως
ἔρως2mo ago
oh, yeah, that is a pita then have you tried to look into existing solutions? i remember doing this with bootstrap
Diolix
DiolixOP2mo ago
I've tried a lot of things and the best way was to use typescript to do it. I any case I want to tell you thank you to you and to @Chris Bolson for trying to help. I still have to try Chis solution 🙂
ἔρως
ἔρως2mo ago
try his solution go for it my solution, admitedly, isnt very generic by the way, give bootstrap a peek
Diolix
DiolixOP2mo ago
the problem on @Chris Bolson solution is the the scrolling is on the "table-wrapper" and not on <tbody>
ἔρως
ἔρως2mo ago
ignore this, i misremembered and they dont have an horizontally scrolling table
Chris Bolson
Chris Bolson2mo ago
ah, sorry, I didn't realize that that was a requirement 🤦‍♂️ I just assumed (my mistake) that what you needed was to have a sticky thead and scrolling tbody.
Diolix
DiolixOP2mo ago
@Chris Bolson It's because of the scrolling in the <tbody> that I have all this mess
Chris Bolson
Chris Bolson2mo ago
ok, I see. I thought that your issue was the missalignment of the header columns with the body columns, which, as Epic said right from the start, was due to the tbody having display: block. Let me take another look.... (I still think that the it would be better to style the contents as child containers rather than styling the table cells themselves)
ἔρως
ἔρως2mo ago
i told you: it all starts from the display block on the tbody, which nukes the layouting of the table but you need it to have the scroll hey, is the thead always the same height?
Chris Bolson
Chris Bolson2mo ago
If you do go with having display:block on the tbody you should also have it on the thead
Diolix
DiolixOP2mo ago
@ἔρως Yes the height can be the same.
ἔρως
ἔρως2mo ago
how about this dumb idea: - tbody with margin the height of the thead - position sticky on the thead - wrapper with position relative and max height with overflow hidden or, if you dont like that because the scroll is on the wrapper, put another wrapper but with overflow visible, for the scroll to look like it is on the tbody and the thead floats off of the way i will try that later
ἔρως
ἔρως2mo ago
Stack Overflow
HTML table with 100% width, with vertical scroll inside tbody
How can I set for <table> 100% width and put only inside <tbody> vertical scroll for some height? table { width: 100%; display:block; } thead { display: inline-blo...
ἔρως
ἔρως2mo ago
some stuff there might work for you
Chris Bolson
Chris Bolson2mo ago
I got this using your display:block method. Ignore the red dashed lines, they are just to check that the columns are aligned. Is that closer to what you wanted?
Diolix
DiolixOP2mo ago
It looks like 🙂
Chris Bolson
Chris Bolson2mo ago
without red lines
Chris Bolson
Chris Bolson2mo ago
If you want the thead and tbody columns to align, they need to have the same basic properties such as inline padding. They also need to be the same width and have the same display style (eg block). I modified a fair amount of your styling and moved the header and row "buttons" into child containers so that their styling doesn't mess with the main table layout. I didn't touch your JS (Typescript) as I suspect that you are right in that that is the only way to ensure that the header columns and the body columns are the same width (as you are essentially separating them from each other by defining the display: block).
Diolix
DiolixOP2mo ago
All the columns have the same width. 😦 It's the problem I've got and try to resolved with typescript
Chris Bolson
Chris Bolson2mo ago
excuse the silly question but how do you save changes in stackblitz? I have forked your version (I will delete it later) to make my changes but can't find the option to "save". If I copy the URL and open it in a new tab, it doesn't show any of the changes so I assume that there is no auto save. ok, scrub that. The "save" icon has miraculously appeared in the top-left corner - I am sure it wasn't there when I asked the question here.
Chris Bolson
Chris Bolson2mo ago
Here is my forked and modified version in case you are interested.
Chris Bolson
StackBlitz
Vitejs - Vite (forked) - StackBlitz
Next generation frontend tooling. It's fast!
Diolix
DiolixOP2mo ago
@Chris Bolson Beautifull !! Thanks !!
Diolix
DiolixOP2mo ago
I just need to resolve the problem with the quantity title when you shrink the widow to much
No description
Chris Bolson
Chris Bolson2mo ago
smaller font size
Diolix
DiolixOP2mo ago
Yes
Chris Bolson
Chris Bolson2mo ago
and less inline padding
Diolix
DiolixOP2mo ago
I hope that can help other that need the same stuff. Thank you so much

Did you find this page helpful?