Dealing With Table Column Widths

Controlling column widths in an HTML <table> is notoriously fiddly, especially when you don't know ahead of time how much content will land in each cell. The browser’s default behavior can leave one column bloated while others are squeezed, or distribute space evenly regardless of content needs. A CSS trick can help regain some control, though it requires a bit of lateral thinking.

How Table Layout Works

The CSS table-layout property determines how a browser distributes width across table columns. It accepts two values: auto (the default) and fixed.

With table-layout: auto, the browser runs its own algorithm to allocate the available width between columns based on content. With table-layout: fixed, the browser splits the full available space evenly across all columns, ignoring the content inside them.

When you want explicit column widths, the <colgroup> element with its child <col> elements is the standard tool. But the interaction between these elements and the two table-layout modes creates problems of its own.

Why Widths Aren't Enough

With table-layout: auto, specifying column widths via inline styles on <col> elements often gets ignored when the combined widths exceed the table's available container space. The browser then shrinks columns so all are visible—reasonable default behavior, but not helpful if you had specific layout goals in mind.

Switching to table-layout: fixed makes the browser respect those widths rigidly. The result is often poor: a column containing a large amount of content gets crushed to the same width as its more sparsely populated siblings, which sacrifices readability for consistency.

A more natural solution would be to set a min-width on the column that needs to flex. The column could then release space down to a defined floor while the rest of the table maintains its proportions. The table could overflow its container, and users would scroll horizontally to see all columns. Unfortunately, <col> elements do not respect min-width.

A Faux min-width

The workaround involves a small structural change to the <colgroup>: add an empty second <col> and use a colspan attribute on the first one. The first column then claims space intended for two columns:


<table>
  <colgroup>
    <col class="col-200" />
    <col />
    <col class="col-input" />
    <col class="col-date" />
    <col class="col-edit" />
  </colgroup>
  
  <thead>
    <tr>
      <th colspan="2">Project name</th>
      <th>Amount</th>
      <th>Date</th>
      <th>Edit</th>
    </tr>
  </thead>
  
  <!-- etc. -->
</table>

In this markup, applying a width to the first <col>—say, 200px—sets the baseline the fixed table layout will honor. Because the layout algorithm divides available space across the total number of columns, the empty second column absorbs the reduction that would otherwise hit the first one. The filled column then behaves as if it had a minimum width: it holds its stated size, flexes slightly when container space grows, and yields to an overflowing table when space runs short, enabling horizontal scrolling.

This opens the door for other layout enhancements, such as making the first column sticky.

Accessibility Considerations

Adding a column purely for layout mechanics does affect how assistive technology reads the table. Screen readers like NVDA on Windows and VoiceOver on macOS may announce all five columns even though only four are visually used. When the first column receives focus, users may hear "Column one through two," which is awkward but unlikely to disorient anyone. An aria-hidden attribute could be tossed onto the unused <col>, though ARIA should never be relied on to redeem questionable HTML structure.

The approach is undeniably hacky, but it delivers the intended behavior. It's worth testing thoroughly with your own content and users to see whether the quirk introduces confusion in your specific context.