В гугл-доксах сделано несколько твиков, чтобы производительность не страдала. Но все они требуют активного использования JS
1. table-layout: fixed
Это необходимо для того, чтобы не нарываться на очень медленный алгорим автоматического выравнивания ширины и высоты ячеек. Но сразу начинаются проблемы с внутренним контентом. Если текст не будет помещаться в ячейку, он будет «вываливаться» за его пределы. Решается путем overflow: hidden. Одновременно с этим потребуется механизм ручного изменения размеров ячеек.
2. Заголовки отдельно, данные отдельно
Данные и заголовки к ним разлелены на несколько таблиц. Это позволяет скроллировать не кусочек таблицы, а всю таблицу внутри контейнера.
3. Явно заданная высота
overflow-y: auto требует фиксированной высоты контейнера, в котором будет скроллироваться контент. Чтобы все работало хорошо, нужно не забывать про событие onresize у окна, потому что при изменении размеров браузера все «сломается»
От себя. Я сделал ручное изменение размеров ширины ячейки, как мне казалось, «хитрым» способом. Так как таблица разрезается на несколько физических (заголовки и данные), я каждому TD/TH в первой TR присвоил класс cell_n, где n — индекс ячейки. Потом создал динамический stylesheet, в котором описал эти классы. И при изменении размера я всего лишь менял значение width в классе. На тот момент идея казалась мне отличной, но реальная производительность у данного решения невысокая, ниже чем у инлайнового указания ширины нужной ячейке.