The <table> element is one of the oldest and most-common elements on the web. But it's also been long overlooked in terms of receiving new CSS features and quality-of-life improvements, unlike other elements that tend to get more love and attention.

I think it's time for the <table> element to get a few CSS improvements of its own. Here's three features that I'd like to see.

#1: Native scrollable tables #

Tables aren't really responsive out of the box. The browser's default table layout algorithm does its best, but it often isn't enough for most tables on many devices.

Problem #

Table columns can become quite smushed as they narrow, which causes odd text wrapping, line breaks, and otherwise makes content difficult to read.

In addition, tables reach a point where they force horizontal page overflow, breaking the site design and making users scroll the whole page back and forth to view it. This may also cause other page content to widen and require horizontal page scrolling to view it.

Diagram of a web page with content that flows offscreen, requiring horizontal scrolling. The page scrolling is caused by a table that is wider than the viewport.

As a result, developers feel pressure to make tables "fit", and over-engineer solutions that cause additional issues. One common practice is to re-layout tables into stacked blocks via media queries. This has its own issues:

  • The table's visual layout and semantics are nuked. If the content is truly meant to be consumed as tabular data, then this is a poorer experience.
  • The visual layout and semantics are not consistent across devices. This can confuse and irritate those who revisit the page and get different experiences.
  • The page becomes much longer and requires significantly more scrolling.

Developers may also hide columns or even the entire table as the viewport narrows. This removes content for some users and not others, causing unequal experiences.

Third-party JavaScript libraries may also be leveraged to do things like make collapsible columns and rows, swap tables out for charts and graphics, and other unnecessary "corrections".

If data tables were more responsive-friendly out-of-the-box, users could consume them easier than they can today. Developers also wouldn’t have to do additional work making them accessible or feel pressure to make unnecessary modifications.

Workaround #

The current best-practice workaround to make a normal responsive table is to wrap a <div> element around the <table>. The wrapper then gets an overflow property to make it scrollable while allowing the table to be as wide as it needs. This works, but the wrapper isn’t accessible.

To make it so, it needs to be given a tabindex attribute so it can be navigated to and scrolled via keyboard, and CSS focus styles so it can be recognized once keyboard users land on it.

The wrapper also doesn’t have any sort of accessible name or role, so screen reader users won’t know what it is. So developers also need to add these semantics, usually in the form of the role and aria-labelledby ARIA attributes. Most developers don’t go above and beyond by doing all of these things.

The resulting markup looks like this:

html
<div role="region" aria-labelledby="tableCaption" tabindex="0"> <table> <caption id="tableCaption">My data table</caption> ... </table> </div>
css
div[role="region"][aria-labelledby][tabindex] { max-width: 100%; overflow-x: auto; } div[role="region"][aria-labelledby][tabindex]:focus-visible { outline: solid 2px currentColor; outline-offset: 2px; }

Solution #

The <table> element should be natively scrollable without the need for a wrapping element. Developers should be able to set the overflow property directly on a <table>, which would allow its columns to be more appropriately-sized and scroll as needed.

Diagram of a web page showing a horizontally-scrollable table. The table is readable with appropriate-sized columns. The rest of the web page is intact.

Scrollable tables should also be keyboard-accessible without needing a tabindex attribute. If a table has overflow, users can use the tab key to reach it and scroll with the arrow keys. Scrollable tables would receive the user agent focus styles, which developers can override if they wish.

The markup is now more clear and simplified:

html
<table> <caption>My data table</caption> ... </table>
css
table { max-width: 100%; overflow-x: auto; } table:focus-visible { outline: solid 2px currentColor; outline-offset: 2px; }

Now, there’s no focusable, named <div> to add unnecessary noise and structure for screen reader users. Those users will simply encounter tables and their built-in semantics amongst other content, interacting with them as they normally would.

And developers that care about accessibility won’t need to spend time adding wrapper elements and ARIA attributes to their table markup, simplifying their experience as well. Keyboard users not using screen readers also won’t find themselves moving tab focus to tables that can’t even be scrolled.

Developers can still choose to handle their tables differently if their use case calls for it, but the base experience is now much simpler and nicer.

#2: Native scroll shadows #

When developers make a <table> scrollable using the wrapping <div> workaround, some columns end up being clipped offscreen. As a usability improvement, developers sometimes add scroll shadows — shadows which hint there’s more content to be scrolled to.

A table row with a shadow inside of its right edge, indicating that there’s content that can be scrolled to.

Problem #

To add scroll shadows currently, developers need to employ a “hack” of the CSS background properties, a technique inspired by Roman Komarov’s post "Scrolling shadows" that was improved by Lea Verou with "Pure CSS scrolling shadows…".

We’d use it on the wrapping element to create shadows that are visible when the containing table is large enough to require scrolling. Here’s the code for horizontal scroll shadows.

css
div[role="region"][aria-labelledby][tabindex] { background: linear-gradient(to right, white 30%, rgba(255,255,255,0)), linear-gradient(to right, rgba(255,255,255,0), white 70%) 0 100%, radial-gradient(farthest-side at 0% 50%, rgba(0,0,0,.2), rgba(0,0,0,0)), radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100% ; background-repeat: no-repeat; background-color: white; background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%; background-position: 0 0, 100%, 0 0, 100%; background-attachment: local, local, scroll, scroll; max-width: 100%; overflow-x: auto; }

What even is all of that? I consider myself very knowledgeable at CSS, but there’s no way I can write all of that on my own. Anytime I add scroll shadows I need to find a post to copy and paste it from. And if you want to tweak it at all — like changing the shadow size, whether it’s round or linear, etc. — you’re rolling the dice on if you break it or not.

Next, because these scroll shadows are part of the element’s background, they appear behind everything else. So if your table contains inputs, buttons, or other elements with background or border colors, they will appear in front of the shadows. Even adding color to any table elements themselves, like rows or cells, will appear over the shadows as well.

A table with horizontal overflow. Shadows appear inside the left and right edges. Text inputs, cell borders, and an alternating row background color hide the shadows in various places.

It’s not a great visual look. You can attempt to work around it by changing all background and border colors to be semi-transparent so that the shadows are visible through the elements. But that can quickly turn into a fool’s errand once you consider the table design, brand colors, support for dark mode, etc. and making all of those things work with varying levels of transparency.

Solution #

Scroll shadows should be a native CSS feature, not a manipulation of the background. This would need a new CSS property, named something like overflow-shadow, that could be applied to any element that can contain overflowed content.

overflow-shadow would be a shorthand property that you could break down into individual and logical properties like overflow-shadow-left, overflow-shadow-right, overflow-shadow-inline-start, overflow-shadow-inline-end, etc.

I imagine a shadow could be defined like this, similar to other CSS shadows:

css
overflow-shadow-left: <offset-x> <offset-y> <blur-radius> <spread-radius> <color> ;

So for our horizontal scrollable table we could create scroll shadows like this:

css
table { max-width: 100%; overflow-x: auto; overflow-shadow-left: 4px 0 8px 0 rgb(0 0 0 / .25); overflow-shadow-right: -4px 0 8px 0 rgb(0 0 0 / .25); }

That’s much easier and way simpler than the scroll shadow background hack from above. We could go a step further and add more options, like the ability to change from a linear shadow to a radial one, or to control the threshold of how fast or slow shadows fade away as hidden content is scrolled into view.

Visually, the native scroll shadows would render at the top of the stacking context, appearing over any child content.

A table with horizontal overflow. Shadows appear inside the left and right edges over everything else below them.

This visual is more in line with what’s expected of a scroll shadow. No child elements appearing in front of shadows, no background colors obstructing shadows, no needing to convert a bunch of colors into transparencies.

#3: Column data alignment #

Tables often contain data that needs to line up with the rest of its column in a specific way, like currency, decimals, or equations. Web developers have wanted the ability to do this for decades.

Problem #

Unfortunately, there’s no native way to align data in a table column aside from basic left/right/center text alignment. If your data needs to align a certain way to be understood best, then your data’s presentation may be unclear.

A table with columns of currency and percentage data whose decimals are misaligned.

Visually, that’s not impossible to read, but would definitely make it clearer and more scannable if the decimals were aligned. All sorts of hacks exist to deal with this issue.

One is to separate the numbers on each side of the decimal into separate columns with a spanned column header; the columns are then styled to appear as if they are one column. This method is a hassle to develop and maintain. Screen reader users will also likely have a confusing time, as they’d need to navigate to two cells to get what should be one cell’s worth of data.

Another is to use CSS Transforms to shift column text left or right so that their decimals line up. This also is an effort to set up and maintain, as each cell needs to have a class or styles manually added based on what it contains. Eric Meyer has a nice write-up of this solution.

Solution #

There’s actually a CSS solution that exists for this. However, it’s sadly not currently supported by any browsers.

In section 7.2. Character-based Alignment in a Table Column in the CSS Text Module Level 4 draft specification, it details using a string as a value to the text-align property to align table cells. An optional keyword value can also be used to specify a secondary method of alignment.

css
td { text-align: "." right; }

The string value must be a single character inside of quotes, while the second argument can be an alignment keyword value. That makes it easy to get a result like this:

A table with columns of currency and percentage data that are aligned via their decimal.

That’s way simpler than splitting up our table markup into multiple columns or adding a bunch of classes to manually adjust the numbers. But as mentioned, this isn’t supported by any browsers at the moment. It’s also possible that it never will be.

The CSS Text Module Level 3 spec, which doesn’t even include using a string value for text-align, is still in draft form and being actively worked on — meaning its features aren’t close to being codified and implemented. So the CSS Text Module Level 4 spec, where it is included, is even further away. I even found a comment in the CSS Working Group’s Github where it’s been proposed removing the feature completely.

I hope the CSS Working Group reconsiders this, either to implement the text-align string value syntax, or as a completely different feature. Either way, for now we'll need to continue using cell alignment hacks for the foreseeable future.

Conclusion #

Those are my 3 most-wanted CSS features for the <table> element currently. They would solve a lot of pain points that I am constantly running into.

How do we get things like this into CSS? There's the CSS Working Group, which is the official group that writes the CSS standard. Any new features would need to be fleshed out by them. If you have any experience or insight into proposing new features to the CSS Working Group, please let me know.

What do you think of these features? Do you agree, or am I way off? Do you have any features you'd prefer to see instead? Hit me up on Mastodon and let me know!