Search code examples
htmlcssscrollheadercss-grid

Creating a css grid with non-scrolling column headers


I'd like to use a pure CSS grid design to create a table-like display where each column has a "header" cell in the top row that does not scroll (in y) while the body of the grid will scroll.

Here is a (non-working) example HTML as a starting point with some notations indicating my desired result:

<style>
    .main-view {
        display: flex;
        flex-direction: column;
        overflow: auto;
        height: 8rem;
    }
    .grid-body {
        display: grid;
        grid-template-columns: max-content max-content 1fr;
        row-gap: 0.1rem;
        column-gap: 0.5rem;
        height: 8rem;
        margin: 0.5rem;
        border: solid 1px red;
        overflow: auto;
    }
    .grid-header {
        display: contents;
    }
    .grid-content {
        display: contents;
    }
    .grid-header label {
        font-weight: bold;
        text-align: center;
        background-color: lightgray;
        border-bottom: solid 1px black;
    }
    .grid-content label {
        text-align: center;
        height: 3rem;
        background-color: lightblue;
    }
</style>
<div class="main-view"> 
    <div class="grid-body">
        <div class="grid-header">
            <label>Header A</label>
            <label>Header B</label>
            <label>Header C</label>
        </div>
        <div class="grid-content">
            <label>Want header rows</label>
            <label>above this body row</label>
            <label>NOT to scroll but have correct column widths</label>
            <label>Cell Label 01</label>
            <label>Cell Label 02</label>
            <label>Cell Label 03</label>
            <label>Cell Label 04</label>
            <label>Cell Label 05</label>
            <label>Cell Label 06</label>
            <label>Cell Label 07</label>
            <label>Cell Label 08</label>
            <label>Cell Label 09</label>
            <label>Cell Label 10</label>
            <label>Cell Label 11</label>
            <label>Cell Label 12</label>
        </div>
    </div>
</div>

So, basically, I'd like the grid-header elements to be sticky while the grid-content elements can scroll. However, I don't know how to style those elements to make this happen.

If this cannot be accomplished with pure CSS, I wouldn't mind a hand with pure javascript code to create the effect (e.g., using two stacked grids and modifying the column widths of the top grid when the bottom grid column widths change).

Thanks!


Solution

  • Style the grid-header label with position: sticky; and top: 0.

    <style>
        .main-view {
            display: flex;
            flex-direction: column;
            overflow: auto;
            height: 8rem;
        }
        .grid-body {
            display: grid;
            grid-template-columns: max-content max-content 1fr;
            row-gap: 0.1rem;
            column-gap: 0.5rem;
            height: 8rem;
            margin: 0.5rem;
            border: solid 1px red;
            overflow: auto;
        }
        .grid-header {
            display: contents;
            
        }
        .grid-content {
            display: contents;
        }
        .grid-header label {
            position: sticky;
            top:0%;
            font-weight: bold;
            text-align: center;
            background-color: lightgray;
            border-bottom: solid 1px black;
        }
        .grid-content label {
            text-align: center;
            height: 3rem;
            background-color: lightblue;
        }
    </style>
    <div class="main-view"> 
        <div class="grid-body">
            <div class="grid-header">
                <label>Header A</label>
                <label>Header B</label>
                <label>Header C</label>
            </div>
            <div class="grid-content">
                <label>Want header rows</label>
                <label>above this body row</label>
                <label>NOT to scroll but have correct column widths</label>
                <label>Cell Label 01</label>
                <label>Cell Label 02</label>
                <label>Cell Label 03</label>
                <label>Cell Label 04</label>
                <label>Cell Label 05</label>
                <label>Cell Label 06</label>
                <label>Cell Label 07</label>
                <label>Cell Label 08</label>
                <label>Cell Label 09</label>
                <label>Cell Label 10</label>
                <label>Cell Label 11</label>
                <label>Cell Label 12</label>
            </div>
        </div>
    </div>