Sticky Table Headers and Columns

I've been fighting with this one for a bit. I can apply styles using a custom theme of course. The color takes effect and the Tailwind sticky class is applied. Top 0 is also applied. When I inspect, this is also in the dom and converts properly to position: sticky with top: 0. .filament-tables-header-cell { @apply bg-red-200 sticky top-0 } It's my understanding that TH can be sticky. I can remove the sticky or top 0 from the nav bar and it goes back to normal. I can't seem to get this to work. Any ideas? People want that excel like feature where the table header and first couple columns are "locked" because they will be scrolling/editing this data often.
14 Replies
Chrispian
Chrispian15mo ago
Update: Sticky column (TD) is working so it's something specific to TH.
awcodes
awcodes15mo ago
Try applying it to the thead instead. I don’t think th’s can be sticky since they aren’t scrollable own their own. Might be trickier though since by default the table isn’t vertically scrollable. So you’d have to make that work first.
Chrispian
Chrispian15mo ago
It seems to work on a naked html page with the table makeup. I tried it and got that to work. But I’ll try thead and let you know. I’ll find the link to the example if thead doesn’t work.
Chrispian
Chrispian15mo ago
Ok, so no joy on thead or th so far. I've adding styling to make sure that I'm hitting the right css target. Also, I see that sticky is apply and top: 0 as well. It's just not sticking. You can see in this example I found on Codepen that TH should be able to do this. You can remove all the css and lines 26-31 are the only ones needed to make it work. https://codepen.io/chriscoyier/pen/PrJdxb
awcodes
awcodes15mo ago
The filament table doesn’t have a position. Does it work if you apply position relative to the table element. There would still need to be an explicit height on the table though to make it work. Basically, if it doesn’t over flow then it can’t scroll. And tables in filament don’t have an explicit height so they will never over flow vertically unless you give it a set height.
Chrispian
Chrispian15mo ago
Interesting. The example doesn’t have a height set but the difference is probably that the table is injected via js under the hood. I’ll try play around with adding some position to the table height and see if I can put a min height maybe? We show 100 rows for this use case so it does scroll. I’ll update when I try these. Thanks again!
awcodes
awcodes15mo ago
Yea. Either way sticky has to have a parent with relative at minimum. Otherwise, there’s nothing for it to stick to.
Chrispian
Chrispian15mo ago
No luck on the header. The columns locking is more important anyway because I can just force the paginate for this object to be low enough they can always see the header row as a temporary solution. I tried h-24 all the way to h-96 with position relative (and others). I saw it work for a split second and refreshed and it never worked again lol. It was jacked up looking when it worked so I know not all the CSS or JS loaded so there's no telling why it worked for that one load. Drives me nuts though because I know there has to be a way but it'll have to wait, don't want to burn anymore time on this one as it's not THAT important. It's just a personal quest now lol. Thanks again for the suggestions as always. I couldn't give up. This is pretty simple actually. In case anyone wants to do this, I'll write up a tip this weekend. I've got the sticky header and columns working. I need to add some additional targeting on the css classes etc. Thankfully I don't have to worry about mobile as this is an internal, desktop only app. So if you use this there are likely some additional tweaks that may be needed on mobile. I'll post the details as soon as I finish testing and clean up. I know this one has been requested some so I'm glad I found a way t do it. Might make a good plugin.
spinther
spinther15mo ago
I'm leaving this here in case anyone wants sticky table headers. The dropdown z-index is a bit ugly but there's no other way to show it on top of the header. I only use tables in $table->columns() so I'm not sure if the thead style has side effects anywhere else.
@layer base {
thead {
@apply sticky top-0 z-10 shadow-md dark:bg-gray-800 bg-white;
}
}

@layer components {
.filament-tables-table-container {
@apply relative max-h-[calc(100vh-20rem)];
}

.filament-tables-pagination-container {
@apply sticky bottom-0 top-full dark:bg-gray-800 bg-white rounded-b-xl;
}

div .filament-dropdown-panel {
@apply z-20;
}
}
@layer base {
thead {
@apply sticky top-0 z-10 shadow-md dark:bg-gray-800 bg-white;
}
}

@layer components {
.filament-tables-table-container {
@apply relative max-h-[calc(100vh-20rem)];
}

.filament-tables-pagination-container {
@apply sticky bottom-0 top-full dark:bg-gray-800 bg-white rounded-b-xl;
}

div .filament-dropdown-panel {
@apply z-20;
}
}
@Dan Harrin thoughts on making this the default table styling considering that a better user experience can be achieved with minimal styling changes? Or maybe make it optional? We can add a class to the thead instead of targeting it directly and we can make the changes to the other existing classes. I'm not sure if it's a real issue, but one minor UX downside I have identified is that the user might not notice that the table is scrollable when 25+ records are shown by default, the table has multiple elements on top of it, and the user is already scrolling the entire page down. This might be solved by experimenting with the max-h-[calc(100vh-20rem)] subtracted rem value so that at least half of the last record is not entirely visible. Or maybe add a small indicator. @chrispian a plugin for sticky headers and columns would be very useful as well.
Dan Harrin
Dan Harrin15mo ago
i am more than happy for you to PR new filament- classes into the views to make this easier to customize, whatever that takes
Chrispian
Chrispian15mo ago
Here is the CSS I used. I'm not great at CSS but with some tutorials I was able to put this together. I have not tested on mobile but this locks the header and the first column. In this css I also restricted it to just one table so I'm using the selector for the model that Filament includes. I'm not sure a custom class is necessary as I was able to target everything easily this way. I'm sure my code is not 100% the best way but could use some clean up. Also, not tested on mobile as that's not a requirement for the task I did this on so it might need something there. I haven't noticed any bugs with anything overlapping or being odd. First column locks. If you want to do more than one you would need to set widths on each column and do some other funky stuff so I just decided to stick with the first column locking and made a custom view to contain a combination of meta data to identify the row for the people viewing/editing on this table. All that to say, here's the css:
.filament-resources-p-o-s-datas .table.filament-tables-table {
@apply relative h-24;
}
.filament-resources-p-o-s-datas .filament-tables-table th {
@apply bg-blue-500 text-white sticky top-0;
z-index: 1;
}

.filament-resources-p-o-s-datas .filament-tables-table th button {
@apply text-white;
}

.filament-resources-p-o-s-datas .filament-table-cell-record-data {
@apply bg-gray-200 sticky left-0;
min-width: 500px;
}

.filament-resources-p-o-s-datas .filament-table-header-cell-record-data {
@apply bg-gray-200 sticky left-0;
z-index: 2;
}

.filament-resources-p-o-s-datas .filament-tables-table-container {
overflow: auto;
height: 500px;
}


.filament-resources-p-o-s-datas table thead th:first-child {
position: sticky;
left: 0;
z-index: 2;
}
.filament-resources-p-o-s-datas table tbody th {
position: sticky;
left: 0;
background: white;
z-index: 1;
}
.filament-resources-p-o-s-datas .table.filament-tables-table {
@apply relative h-24;
}
.filament-resources-p-o-s-datas .filament-tables-table th {
@apply bg-blue-500 text-white sticky top-0;
z-index: 1;
}

.filament-resources-p-o-s-datas .filament-tables-table th button {
@apply text-white;
}

.filament-resources-p-o-s-datas .filament-table-cell-record-data {
@apply bg-gray-200 sticky left-0;
min-width: 500px;
}

.filament-resources-p-o-s-datas .filament-table-header-cell-record-data {
@apply bg-gray-200 sticky left-0;
z-index: 2;
}

.filament-resources-p-o-s-datas .filament-tables-table-container {
overflow: auto;
height: 500px;
}


.filament-resources-p-o-s-datas table thead th:first-child {
position: sticky;
left: 0;
z-index: 2;
}
.filament-resources-p-o-s-datas table tbody th {
position: sticky;
left: 0;
background: white;
z-index: 1;
}
If anyone has improvements, I'm definitely interested. Low quality gif to show example.
toeknee
toeknee15mo ago
Neat work
dyo
dyo14mo ago
where/how should i store this custom css?
Chrispian
Chrispian14mo ago
Did you get this working? I am using themes (described in the docs). I just followed those to setup the theme and then you add the css to that file. Don’t forget to clear your caches and non run dev or npm run build for prod.