The trickiest HTML element to style is probably <table>. The <table> element represents the older way of styling, and that’s much older than modern modules such as flex and grid.

For example, here we get such a task: A <div> element contains a <table>. The table may have many or only a few columns, so its width is uncertain.

We are asked to:

  • Add horizontal scroll to the table if its width is larger than the div.

  • Horizontally center the table if its width is smaller than the div.

Example HTML document:

<div class="mydiv">
    <table class="mytable">
        <thead>
        ...
        </thead>
        <tbody>
        ...
        </tbody>
    </table>
</div>

Failed solutions

This looks a pretty easy task, huh? Many designers know how to add horizontal scroll to an element: overflow-x: scroll;. And to horizontally center an element, we have text-align: center; (for inline elements) and margin: 0 auto; (for block elements). So we can give some quick solutions.

Now let’s see where they fail.

Failed solution 1

Let’s first add the horizontal scroll:

.mytable {
    overflow-x: scroll;
}

CodePen: Failed Solution 1

Problem:

  • There’s no horizontal scroll.

Answer:

  • By default, the <table> element has display: table;. overflow-x: scroll; doesn’t work with a table display.

Failed solution 2

All right. Let’s display the table as block.

.mytable {
    overflow-x: scroll;
    display: block;
}

CodePen: Failed Solution 2

Problem:

  • Now the table has horizontal scroll. But it isn’t centered when there are only a few columns. It isn’t centered even if we add margin: 0 auto; or text-align: center;.

Answer:

  • An element displayed as block will automatically expand to full width. So it can be seen as already centered, even though its contents aren’t. If you check the width of <thead> and <tbody>, you will find these elements are narrower than the <table> element.

  • Setting text-align: center; on the <table> element will only center its descendants, because this CSS property should be set on the container element. Even so, it still has no effect on <table> itself in this case because it has a block display.

  • You may try other display properties (such as flex and grid). However, you are likely to find the columns misalign. This is non-trivial to solve.

Correct solution

So what options do we have right now? Yes we have one more: display: inline-block;.

From Mozilla: display:

The element generates a block element box that will be flowed with surrounding content as if it were a single inline box (behaving much like a replaced element would).

So if we write:

.mytable {
    overflow-x: scroll;
    display: inline-block;
    max-width: 100%;
}

Then horizontal scroll actually works. The remaining job is to center the <table> when it has a smaller width.

In this case, the <table> element behaves much like an inline element to the external. There are both good news and bad news resulting from this:

  • Good: It doesn’t expand to its parent’s full width.

  • Bad: It doesn’t respond to margin: 0 auto;, and there’s no easy way to use text-align: center.

Here’s where the position CSS property comes to rescue. We use position: relative; on the <table> element to horizontally center it.

The benefit of using position: relative; is, the element doesn’t leave its normal flow (this is different from using position: absolute;), so we don’t need another element to hold its place. From Mozilla: position:

The element is positioned according to the normal flow of the document, and then offset relative to itself based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements; thus, the space given for the element in the page layout is the same as if position were static.

We also use left: 50% to place this element at half its parent’s width. From Mozilla: left:

<percentage>

    A <percentage> of the containing block’s width.

This is very close to the final result. The only problem now is, the element is placed with its left boundary in the center of its parent, while what we want is to place its center in the center of its parent. The next CSS property we use is transform. Specifically, we use transform: translate(-50%, 0%); to move it back by 50% of its own width. This finally makes everything good.

Check the final result here:

.mytable {
    display: inline-block;
    max-width: 100%;
    position: relative;
    left: 50%;
    transform: translate(-50%, 0%);
    overflow-x: scroll;
}

CodePen: Correct Solution

This solution not only is correct but also has a few advantages:

  • It is pure CSS solution, not using JS at all.

  • It doesn’t add any wrapper elements and keeps HTML document intact.

  • It may also work with elements other than <table>.

Caveat

Because we have used the position property, this <table> element now becomes a positioned element, so its ordering (relative to other elements in the page) may change. For example, if you have a fixed element (position: fixed;) without z-index, then the <table> element may display on top of it. This is usually not what designers want because fixed elements are usually meant for top-level display. To handle this, set a high z-index on the fixed element.

References