htmlcssfouc

How can I avoid a "Flash of Unstyled Content" using fixed-width cells in CSS Tables?


My web GUI's layout is partially driven by CSS tables. This is mainly because I want the "cells" to have the same height under all situations without any massive headaches surrounding alignment. Overall, this approach has been very successful.

However, I do have a problem whereby the right-hand cell in the table can sometimes take a moment to render, causing the left-hand cell to briefly have 100% of the page width. This causes a noticeable "flicker" effect which, although minor, is kind of annoying. And I've decided to fix it.

Here is a vague representation of how my page works:

#tbl      { display: table; width: 200px; border: 1px solid black; }
#tbl-row  { display: table-row; }
#tbl-col1,
#tbl-col2 { display: table-cell; }

#tbl-col1 { width: 50px; background-color: red; }
#tbl-col2 { background-color: blue; }
<div id="tbl">
    <div id="tbl-row">
        <div id="tbl-col1">LHS</div>
        <div id="tbl-col2">RHS</div>
    </div>
</div>

All's well and good until you use your developer tools to give #tbl-col2 a display: none directive, [I hope accurately] simulating the state of the browser's rendering engine in the moments between #tbl-col1 having been rendered, and #tbl-col2 being rendered.

Notice that #tbl-col1 immediately takes up 100% of the width of the table, despite the width I've given it. I sort of understand why this is happening: after all, I've asked the browser to make the divs behave like tables. Still, it's undesirable here.

I tried to fix this by inserting a "spacer", hoping that without a width it would expand to fill all the space on the right-hand side until such time as the right-hand side got rendered:

#tbl      { display: table; width: 200px; border: 1px solid black; }
#tbl-row  { display: table-row; }
#tbl-col1,
#tbl-spc,
#tbl-col2 { display: table-cell; }

#tbl-col1 { width: 50px;  background-color: red; }
#tbl-col2 { width: 150px; background-color: blue; }
<div id="tbl">
    <div id="tbl-row">
        <div id="tbl-col1">LHS</div>
        <div id="tbl-spc"></div>
        <div id="tbl-col2">RHS</div>
    </div>
</div>

As you can see by again hiding #tbl-col2, it made not a blind bit of difference: #tbl-col1 still took the whole width of the table, rather than the 50px I allowed it.

Assuming I'd rather fix this than abandon the CSS Tables layout altogether, what can I do?

Or am I going to have to replace the layout or, even worse, take a JavaScript approach to resolving the FoUC?


Solution

  • What I like to do in similar cases (for example html emails) is to pre-define column widths using empty cells this way:

    .tbl {
        display: table;
        width: 200px;
        border: 1px solid black;
        color: #fff;
    }
    .tbl-row {
        display: table-row;
    }
    .tbl-cell {
        display: table-cell;
    }
    .tbl-col1 {
        width: 50px;
        background-color: red;
    }
    .tbl-col2 {
        background-color: blue;
    }
    <h3>Pre-define columns width by adding additional row</h3>
    
    <div class="tbl">
        <div class="tbl-row tbl-format">
            <div class="tbl-cell tbl-col1"></div>
            <div class="tbl-cell tbl-col2"></div>
        </div>
        <div class="tbl-row">
            <div class="tbl-cell tbl-col1">LHS</div>
            <div class="tbl-cell tbl-col2">RHS</div>
        </div>
    </div>

    That formatting column is invisible if there is no content inside cells, but still it tells browser the way table should be formatted.