Illustration by

Web Typography: Designing Tables to be Read, Not Looked At

A note from the editors: We’re pleased to share an excerpt from Chapter 2 of Richard Rutter’s new book, Web Typography.

Good designers spend a great deal of time sweating over typography. They agonise over typefaces, iterate through type scales and meticulously apply white space, all in the service of the reader. Then comes along a table with the temptation to get creative, and all thoughts of the reader go out of the window. And yet tables are there to be read, referenced and used, not merely looked at.

Article Continues Below

Set tables as text to be read#section2

Tables come in many forms. Some contain simple numbers, others are complex with mixtures of numeric data and textual information. Some require reading row by row, others are scanned vertically. The potential use for tables is as varied as the written word. They can be financial statements, bus timetables, multilanguage dictionaries, tables of contents, numerical conversions, pricing options, feature comparisons, technical specifications, and so on.

Despite the huge variation in table size, complexity, contents and purpose, every table shares two simple design principles: they should be readable and support a sense of the data held within. They should not be prettied up to satisfy a sense of aesthetic when simply looked at. That said, a well-designed table can still be a thing of beauty but with the form following the function. Tables are not pictures of data: they are catalogues of data to be perused, parsed, referenced and interrogated. A well-designed table will enable the information to be read and understood, and will reveal the patterns and correlations inherent in the data. As Jan Tschichold, the virtuoso of typography design, put it in Asymmetric Typography1:

Tabular matter need no longer be a rather unpleasant job to design: on the contrary, it can become a really charming and artistic exercise, in no way less interesting than any other area.

Wherever possible plan the readability of every table in advance. Your design process should be an investigation into making the data undemanding to read, simple to follow and easy to extract.

Just as you wouldn’t design body text with the aim of fitting as many words as possible on the screen, so you shouldn’t treat designing a table as an exercise in cramming as much data as possible into one space. You might be tempted to reduce the text size of your table – and if the data is entirely numeric you might be able to get away with it. Your reader should still be able to be comfortably read and interpret the table from their normal position, without needing to lean in.

Don’t stretch tables#section3

Many designers will instinctively apply a width to their tables – just as they might an image – stretching them to fill the text column or page. And that is the appeal of setting tables full-width: you can make them look somewhat image-like when viewed from afar. However, while a table spread across the screen might look preferable from a distance, on closer inspection it will be harder to read as the data will be unnecessarily separated. To add insult to injury, tables set full-width are often replete with background colours and borders to give the table further the texture of an image, when what your reader really requires is the texture of text. For the sake of your readers, avoid these temptations.

You might consider making all the columns an even width. This too does nothing for the readability of the contents. Some table cells will be too wide, leaving the data lost and detached from its neighbours. Other table cells will be too narrow, cramping the data uncomfortably. Table columns should be sized according to the data they contain. Columns of small numbers should be narrow, and columns of paragraphs should be relatively wide. This sounds like a lot of effort, and for a print designer it would be, as they would have to size each column manually in their layout software. Fortunately, web browsers are very clever when it comes to laying out tables and will do all that hard work for you. Browsers have been laying out tables automatically according to complex algorithms since long before CSS came along – just let them do their thing.

Keep table furniture and fills to a minimum#section4

The statistician and information designer Edward Tufte introduced the concept of data-ink in his 1983 classic, The Visual Display of Quantitative Information. He defines data-ink as ‘the non-erasable core of the graphic’, whereas non-data-ink is the ink used in the graphic, not to directly represent data but for scales, labels, fills and edges. Tufte goes on to define the data-ink ratio as the proportion of ink that is used to present actual data compared to the total amount of ink used in the entire graphic. The goal is to design a graphic with the highest possible data-ink ratio (tending towards 1.0) without eliminating what is necessary for effective communication.

Where Tufte talks about graphics he includes charts, diagrams and tables, and where he uses ‘ink’ we can think of pixels. In terms of tables, he’s saying that we should remove almost everything in the design which is not data or white space. Minimise furniture, maximise information. This is an ideal first principle to bear in mind when considering the typographic design of a table.

As a starting point, avoid any border or frame surrounding the table. This is a Victorian embellishment which is entirely unnecessary as text alignment will shape the table just fine.

Try to achieve a readable table using just alignment, spacing and grouping. Avoid zebra striping, tints and fills, and any other backgrounds. These can be superficially pretty but are usually a distraction. They serve to distort the meaning of the data by highlighting every other row to the detriment of neighbouring rows. Only use tints as a subtle means of guiding your reader’s eyes, and then only if you cannot arrange the data to that end. If you choose to tint, do so only in the primary direction of reading: down if lists, across otherwise.

When it comes to lines and borders between rows and columns – typographically referred to as rules – the same applies: use them judiciously and preferably not at all. In Asymmetric Typography Jan Tschichold sums this up wonderfully:

Tables should not be set to look like nets with every number enclosed. Try to do without rules altogether. They should be used only when they are absolutely necessary. Vertical rules are needed only when the space between columns is so narrow that mistakes will occur in reading without rules. Tables without vertical rules look better. Thin rules are better than thick ones.

Avoid using row or column borders unless the data alignment, spacing and grouping are not sufficient to guide your reader’s eye. If you do need to use rules for this purpose, use them in one direction only and employ a lighter colour to reduce the impact of the lines: you are making a distinction, not constructing a barricade.

Left-align text, right-align numbers, and align headings with data#section5

In the spirit of treating tables as artefacts to be read, don’t centre text within tables. Align table text as you would anywhere else; that is, aligned left. As text in tables tends to end up in narrow columns, don’t justify the text either – leave it ragged-right – or you will end up with rivers flowing down the tables, potentially causing confusion and certainly harming readability. You can hyphenate, however, particularly if the table columns would otherwise have a pronounced rag.

Right-align numbers to help your reader make easier comparisons of magnitude when scanning down columns. To aid scanning in this manner you will need consistent precision of your numeric data; that is, use the same number of decimal places.

For consistency and ease of understanding, match the alignment of headings to the alignment of the data. Right-align headings of numeric data and left-align headings of columns with text, for example:

Country Area Population GDP Capital
Austria 83,858 8,169,929 339 Vienna
Belgium 30,528 11,007,000 410 Brussels
Denmark 43,094 5,564,219 271 Copenhagen
France 547,030 66,104,000 2,181 Paris
Germany 357,021 80,716,000 3,032 Berlin
Greece 131,957 11,123,034 176 Athens
Ireland 70,280 4,234,925 255 Dublin
Italy 301,230 60,655,464 1,642 Rome
Luxembourg 2,586 448,569 51 Luxembourg
Netherlands 41,526 16,902,103 676 Amsterdam
Portugal 91,568 10,409,995 179 Lisbon
Spain 504,851 47,059,533 1,075 Madrid
Sweden 449,964 9,090,113 447 Stockholm
United Kingdom 244,820 65,110,000 2,727 London

Align to the decimal point#section6

You may find yourself not having control of numerical precision, or perhaps the data you’re working with is rounded to the same significant number rather than adhering to the same precision. In this case, simply right-aligning a column of numbers will not help your reader scan down the column – small, high-precision numbers will look at first glance like a large number. Instead, align numbers to the decimal point. This will enable your reader to more readily compare magnitudes among a wider variety of data:

+-------------+
| Call charge |
+-------------+
|     $1.30   |
|     $2.50   |
|    $10.80   |
|   $111.01   |
|    $85      |
|    N/A      |
|      $.05   |
|      $.06   |
+-------------+

Aligning to the decimal point was theoretically possible by using the HTML 4 char attribute on a <td> tag, but in reality it was never supported. The modern way to align numbers to a decimal point (or to any character, in fact) is through a new value of the text-align property, although at the time of writing this is languishing in the CSS Text Level 4 Module2 and support is patchy at best.

The syntax of the new value is simple. You include the alignment character (usually a full stop or comma) in quotes, followed by a space and your desired alignment keyword, which defaults to right if you omit it. For example, the following will centre the data and align to a decimal point as in our prior example:

td { text-align: "." center; }

By specifying different alignment characters you can lay out more complex tables in a useful way; in this example, aligning digits to ‘×’ and ‘:’.

Selected display standards
Video standard Resolution Pixels Aspect
QQVGA 160 × 120 19k 4 : 3
HQVGA 240 × 160 38k 3 : 2
QVGA 320 × 240 76k 4 : 3
WQVGA 480 × 272 130k 16 : 9
VGA 640 × 480 307k 4 : 3
SVGA 800 × 600 480k 4 : 3
XGA 1024 × 768 786k 4 : 3
HD 1260 × 768 967k 16 : 9
WXGA 1280 × 800 1,024k 16 : 10
SXGA 1280 × 1024 1,310k 5 : 4
UXGA 1600 × 1200 1,920k 4 : 3
FHD 1920 × 1080 2,073k 16 : 9
DCI 2K 2048 × 1080 2,211k 19 : 10
WQXGA 2560 × 1600 4,096k 16 : 10
4K UHD 3840 × 2160 8,294k 16 : 9
8K UHD 7680 × 4320 33,177k 16 : 9

Use tabular lining numerals in tables of numbers#section7

Many tables, such as financial statements or timetables, are made up mostly of numbers. Generally speaking, their purpose is to provide the reader with numeric data, presented in either columns or rows, and sometimes in a matrix of the two. Your reader may use the table by scanning down the columns, either searching for a data point or by making comparisons between numbers. Your reader may also make sense of the data by simply glancing at the column or row. It is far easier to compare numbers if the ones, tens and hundreds are all lined up vertically; that is, all the digits should occupy exactly the same width.

Digits of the same width can inherently be found in monospaced fonts, and there is nothing wrong with choosing a suitable monospaced font to present a table of data (see ‘Combining typefaces’). However, many proportionally spaced fonts (those where a 1 is narrower than an 8, and a W is wider than an I) also come with additional sets of figures which are monospaced. These are called tabular numerals. As well as being of equal width, tabular numerals will be subtly designed differently from the standard proportional numerals. For example, a 1 will normally have a bar for its base, and a 0 (zero) may be designed slightly narrower to better fit the chosen number width. Tabular numerals are usually available in old-style and lining variations. Use tabular lining numerals to provide your reader with the most effective way to reference vertically and horizontally in tables of data.

A table of data
Different numeral styles compared.

To specify tabular lining numerals, set the font-variant-numeric property with a value of lining-nums and tabular-nums:

table {
    font-variant-numeric: lining-nums tabular-nums;
}

The equivalent properties for legacy browsers requiring font-feature-settings, use the lnum and tnum OpenType feature tags.

Proportional numerals#section8

If you need to specify proportional numerals, set the font-variant-numeric property with a value of proportional-nums. For legacy browsers requiring font-feature-settings, use the pnum OpenType feature tag.

Put white space to work to group and separate#section9

Having eliminated rules and fills (borders and backgrounds) from your table, you will need to apply white space to your table so your reader can make sense of it. It is at this point that you should remove from your mind’s eye all visions of spreadsheets and other such uniform grids, and think instead in terms of typography and simple gestalt grouping principles.

You will primarily need to separate the data so that each element can be individually identified and read as separate from the others. To have more control over the spacing, first collapse the spacing between borders:

table {
    border-collapse: collapse;
}

In traditional HTML tables, adjacent cells each have their own distinct borders which are separated from each other, with the separation still present even if the borders are not. In the collapsed border model, adjacent table cells share borders. As we are removing (almost) all cell borders, and any we retain will be single key lines, the collapsed border model is the most appropriate.

Now apply padding to the table cells to separate the data. You’ll find that adding a smaller amount of padding to the top of the cell is a useful way to provide a visually balanced separation from the rows above and below. To ensure everything lines up nicely, apply the same padding to heading cells as to data cells. Because line lengths are often very short in tables, you can reduce the line height right down. In the following example, we’ve removed all additional line spacing, but you may need more depending on your choice of font and the amount of text in the table cells.

td, th {
    padding: 0.125em 0.5em 0.25em 0.5em;
    line-height: 1;
}

The gestalt grouping principles most useful in tables are those of proximity and similarity. Move related data closer together to be distinct from other data; in other words, space apart groups of rows or columns. A by-product of grouping rows is that the data becomes much easier to scan and refer to than if the table consisted of a succession of undifferentiated rows. Ensure data of a similar content or meaning look similar at a glance, which you can do through alignment, colour and font style.

Table captions#section10

We will attend to the typographic specifics of table captions in ‘Choosing typefaces for functional text’ but it’s worth noting now how to mark up captions for tables. If you are choosing to place your table inside a <figure> element, which is a perfectly reasonable thing to do, then use a <figcaption> element before or after the table. If your table is not inside a <figure> element, or you have multiple items in the figure, use the aptly named <caption> element, which HTML provides specifically for tables. Always write the <caption> tag immediately after the opening <table> tag and before any table data, like this:

<table>
    <caption>
    Imperial to metric conversion factors
    <p><i>Values are given to three significant figures</i></p>
    </caption>
    <thead> … </thead>
    <tbody> … </tbody>
</table>

You can position the caption either above or below the table using the caption-side property and a corresponding value of either top or bottom.

caption { caption-side: bottom; }

The following table shows a caption and demonstrates gestalt grouping principles by separating the data into related rows:

Imperial to metric conversion factors

Values are given to three significant figures unless exact

To convert into multiply by
Length inches millimetres (mm) 25.4
feet centimetres (cm) 30.48
yards metres (m) 0.91444
miles kilometres (km) 1.61
Area square inches sq. millimetres (mm²) 645
square feet square metres (m²) 0.0929
square yards square metres (m²) 0.836
acres hectares 2.47
Volume cubic inches millitres (ml) 16.4
cubic feet litres 28.3
imperial gallons litres 4.55
US barrels cubic metres (m³) 0.159

Note that, in this example, the numbers do not align to the decimal point. This is because the purpose of the table is for the reader to easily identify and extract a multiplication factor. In this instance there is no obvious use case for comparing the relative magnitudes of the factors, which is when decimal alignment would be useful.

Do not over-stylise tables#section11

The French writer-aviator Antoine de Saint-Exupéry wrote3 perfection is attained not when there is nothing more to add, but when there is no longer anything to take away. Quoting de Saint-Exupéry may have become a cliché, but his idiom is entirely apt when applied to table design. There is no need to make a table look like a spreadsheet. A spreadsheet is a tool unto itself; a table is for presenting data and information that can be read. Spreadsheet software offers a multitude of options for table styles, which add text formatting, borders, background fills and all manner of ornament. They may make pretty pictures but do nothing for table readability, so do not try to emulate them. Tables can be beautiful but they are not works of art. Instead of painting and decorating them, design tables for your reader.

Initial badly designed table
A typical spreadsheet-styled table set full-width with borders, fills and centred alignment.
Table design progressed
1. Remove stretch and size columns to data.
Table design progressed
2. Remove fills, gridlines, border and bolding.
Table design progressed
3. Left-align text, right-align numbers and align headings with data.
Table design progressed
4. Put white space to work to group and separate.
Table design progressed
5. Use tabular lining numerals, consistent precision, and remove repetition.

Adapt tables to small screens#section12

Tables regularly require a fair bit of horizontal space to display the information they contain. Even when judiciously designed and edited, a typical table may need to be wider than the 45–75 characters we normally allow for paragraphs of text. For small screens, such as phones, designing readable tables which work under such cramped conditions presents us with a serious challenge. The best approaches always deal with each table on case-by-case basis, but that’s not always possible if we need to generically style whatever comes out of a CMS database.

One immediate approach is to use either a condensed font or a slightly smaller size (but not both smaller and condensed). In both cases, readability must remain paramount and other options should also be explored.

Consider setting oblique headings to save space#section13

One way to save horizontal space, particularly when you have short pieces of data but long headings, is to set the headings at an oblique angle.

A table showing Fahrenheit against Celsius with oblique headings.
Using oblique headings to save space.

You can use a simple CSS translation to achieve the effect. You will also need to absolutely position the headings so the original width of the columns isn’t retained and they shrink to wrap the data instead.

th {
    transform-origin: bottom left;
    position: absolute;
}
th.degC {
    transform: translate(2.58em,-2em) rotate(-60deg);
}
th.degF {
    transform: translate(5.14em,-2em) rotate(-60deg);
}

Let the browser handle tables with horizontal scrolling#section14

The simplest solution to help tables of any size and complexity is to let the browser lay out the table as best it can and render part of the table off-screen as necessary. Provided you then enable your reader to scroll the table sideways independently of the rest of the text, the table can be relatively easily brought into view.

A table wider than a phone’s screen
Using a crawl bar to scroll a table into view.

To do this, first wrap your table in a <figure> element:

<figure class="fig-table">
<table> … </table>
</figure>

Then apply the following simple rules to hide the portion of the table off-screen and enable your reader to scroll the table without affecting the rest of the text:

.fig-table {
    max-width: 100%;
    overflow-x: scroll;
}

It is important not to set a width on your table; the browser can then compress the table as far as it can before overflowing off the screen. To preserve readability, make good use of non-breaking spaces and white-space:nowrap to limit the amount the data wraps in the cells. It’s better to have a readable table that requires scrolling than an unreadable one which doesn’t.

Linearise simple tables into lists#section15

You can safely linearise simple data tables when space is limited. The tables most suitable for this treatment are lists of structured data; for example, an employee directory:

Name Email Title Phone
Jones, Claire claire.jones​@domain.com Managing Director 01234 567890
Smith, Darren darren.smith​@domain.com Head of Sales 01234 567891

When there is not enough room for the table to render comfortably, we can set it with a completely different layout. This is less compact overall, and takes more space vertically, but it succeeds in fitting the table into a much narrower viewport:

Name: Jones, Claire
Email: claire.jones@domain.com
Title: Managing Director
Phone: 01234 567890
Name: Smith, Darren
Email: darren.smith@domain.com
Title: Head of Sales
Phone: 01234 567891

The two renderings of our employee directory table use exactly the same markup, comprising the conventional HTML elements you would expect in any table. The one addition is a data-title attribute on each cell enabling us to repeat the label in the list view, should we need to.

<th data-title="Name">Jones, Claire</th>
<td data-title="Email">claire.jones@domain.com</td>
<td data-title="Title">Managing Director</td>
<td data-title="Phone">01234&nbsp;567890</td>

There are four simple steps to turning the table into a list, using a media query and CSS (no JavaScript is required).

  1. Identify the viewport width at which the table starts to render poorly.
  2. Apply display:block to all table-related elements so they align vertically instead of as a table.
  3. Hide the header row and any empty cells.
  4. Display labels for each data item (optional).

You will need to apply some additional styling for aesthetics and readability, but the responsiveness described can be accomplished in these few lines of CSS:

@media (max-width: 25em) {
  table, caption, tbody, tr, th, td {
    display: block;
    text-align: left;
  }
  thead, th:empty, td:empty {
    display: none;
    visibility: hidden;
  }
  th[data-title]:before, td[data-title]:before {
    content: attr(data-title) ": ";
    display: inline-block;
    width: 3.5em;
  }
}

This technique was first popularised by Aaron Gustafson4.

Make tables responsive according to their purpose#section16

There are many different techniques5 available for making data tables responsive. Some are simple CSS-only methods (we’ve covered two already); others are complex, enhanced by JavaScript. When considering which technique to use, ask yourself how your reader will use the table. In particular, consider if your reader is likely to compare either rows or columns – these kinds of tables need extra attention owing to the way they are used.

When being able to compare columns is important, one method is to hide non-essential fields and provide an option to turn them back on. This technique was popularised by Filament Group6 using a stocks table as an example:

Table with 7 rows and 9 columns of stocks data
A data-rich table as rendered on a large screen.
Table with 7 rows, 2 columns of stocks data
The same table with hidden columns and options to toggle.

Tables are a frequently overlooked aspect of reading, sometimes overstyled, sometimes poorly thought out. Responsiveness is a particularly thorny issue as the best solutions depend very much on the utility of the table. Tables can be packed with data, rich in content and meaning. Give them the attention they deserve.

Want to read more?#section17

This excerpt from Web Typography will help you get started. Order the full copy today.

Cover of Web Typography

Notes

  • 1. Asymmetric Typography by Jan Tschichold (1967, after 1935 original).
  • 2. Character-based Alignment in a Table Column (http://wbtyp.net/103) in the CSS Text Module Level 4.
  • 3. Terre des Hommes (translated into English as Wind, Sand and Stars) by Antoine de Saint-Exupéry (1939).
  • 4. Responsive Tables (http://wbtyp.net/16) by Aaron Gustafson on Easy Designs blog (2013).
  • 5. See CSS-Tricks’Responsive Tables (http://wbtyp.net/148) for the latest options.
  • 6. Filament Group’s ‘Tablesaw (http://wbtyp.net/15) responsive table plug-ins.

11 Reader Comments

  1. Wonderful article, thank you!

    I think that the oblique column headers could be simplified a bit, by using max-width:0; overflow:visible; to avoid absolute positioning. This way you don’t have to hardcode different x-offsets for each column.

    th {
      transform-origin: left bottom;
      transform: translatex(1em) rotate(-60deg);
      max-width: 0;
      overflow: visible;
      white-space: nowrap;
    }
    

    Codepen: https://codepen.io/donmccurdy/pen/PONbVP?editors=1100

  2. Great article, thank you.

    Some European countries switch the comma and period characters around, so that periods separate thousands, and the comma denotes the decimal place. Is it possible to cope with this (without separate CSS rules for language) when attempting to align by decimal with the method you describe?

  3. TLDR; “The Ultimate Table Guide”

    Thanks for the bookmark, I look forward to using: td { text-align: “.” center; }

  4. Great article, just commenting that tables in it don’t work in mobile view. I’m using updated Chrome on Android. If I switch to desktop view they are fine.

  5. Great article, neat snippets, me like very much!
    I like the breakdown in “Do not over-stylise tables”(section 10) very much. But how do you deal with the possibility to increase/decrease the column-width by mouse? You can’t see the border between the columns to change the width of a column.
    Can you say it’s common sense to do so – even without seeing a styled divider?

  6. Thanks for this article. I must admit I hate tables… They always destroy a nice layout and it’s really time consuming to have them display the way we want… 🙁

  7. Hey Richard,

    great article, I agree that since the early

    days many people have forgotten some basics of how to use actual tables effectively and in a user friendly way — on and off the web 🙂

    You might want to check your definitions for numerals. Your illustrations show correct examples, but the labels contradict correct usage of the terms. You are actually comparing old-style versus lining (lines up to the height of the caps), and proportional (variable width) versus tabular (equal width numbers, look neat and comparable in a table in several rows).

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career