You need to wrap the table in a <div> and style that with a class and CSS to make the table scrollable.
<div class="table-wrapper">
<table>
<!-- other table stuff here -->
</table>
</div>
All this depends on how you want the tables to be presented (markdown, shortcode or using replaceRE, the latter which is applicable for wrapping all tables).
I found the culprit. I used grid layout to keep footer at the bottom, which caused the problem. This was totally unexpected.
// Stretch <main> to ensure <footer> stays at bottom
.body {
display: grid;
grid-template-rows: auto 1fr auto; // <header> <main> <footer>
min-height: 100vh; // fallback value if dvh is not supported
min-height: 100dvh; // Avoid scrollbar on mobile
}
After removing this code, both wrapper solution and non-wrapper solution work for my theme.