Basics
Data tables in email – Part 1
Welcome to my two part series on data tables in emails – aka – when not to use role="presentation"! This week, we’re talking intro to the main structural elements needed in data tables, where data tables show up in emails, and some basic functional issues we run across in our email marketing efforts. Next week we’re going deep into testing and building these emails in addition to talking through designs where the content isn’t always tabular.
Tables in email
In email we use A LOT of tables, it’s kind of our whole thing. We use tables for layout even though there are lots of different ways to build in html these days. That’s because email clients have historically been really touchy about rendering newer html and css layout solutions.
That’s slowly changing as desktop Outlook usage declines and web/mobile clients with better CSS support become more dominant. (Though I’m still team layout tables but that’s probably a whole other post)
When we use tables for layout, we need to add the aria role of presentation – or none – so that assistive technologies like screen readers can clearly understand that the table is not filled with data. This *can* change how the table is read by the assistive technology, for example, whether headers are announced, or whether the content is read as a grid versus linear text. (Though I’ve written before about how this isn’t as necessary as it used to be, but we should still do it when we can)
But today – I’m not talking about layout tables – I’m talking about data tables!
Data tables primer
Data tables are filled with related content and screen readers share those relationships with users. If you are looking up information in a chart, you will visually reference the table headers to give the data additional context.
| John | James | Julie | |
|---|---|---|---|
| Bananas | Yes | No | No |
| Apples | No | No | Yes |
| Watermelon | Yes | No | Yes |
In the chart above, if you look only at the data but not the table headers, you have no idea what it’s telling you. You have to look at the two table headers to tell you that John has no apples and Julie has apples.
Screen readers share this context with users when they are reading tabular data.
When users move through the content in a data table, the screen reader reads out the table headers along with the data – providing that additional context of the data.
So if the user navigates to the first “No” content, it would also read out “James” and “Bananas” so the screen reader user understands that context. Essentially “James has no bananas”
This is also why we don’t want to use data tables for layout – the content in our table cells are not related in the same way so if a screen reader provided those additional details it would be more confusing – not clarifying – for those users.
That said – these days – screen readers have become very good at determining the difference between data tables and layout tables, especially in email, so the hard requirement for those aria roles are not as intense as they once were. That doesn’t mean we should rely on this behavior – it just means users aren’t completely blocked if the role is missing. I will still call the missing role out in an audit – but I don’t think you need to spend nights fretting about this if you have to use an email builder that doesn’t add that role. (But please submit this as an issue to your email builder because it’s such an easy fix)
Where do we see data tables in email?
What that does mean for us email marketers though, is that we need to make sure that when we *are* putting data into tables, that we’re doing it right.
So there’s one very common transactional message that we all get/make all the time that really does need us to use data tables: Ereceipts!!
Personally – I love ereceipts. Not getting them really, but I LOVE making them. Mostly I love laughing at email designers that design for the hilariously smallest version of the data in their designs.
Among my favorite receipt data design flummoxes are: Fitting all the text in a heading tightly on one line but only using “Name” as the name placeholder. (Both of my kids have nine digit-long names so I tend to throw their names in as placeholders when designers do this) I also love it when the product name in the design is not what it will be in the data – ie. car names in insurance estimates. They’re not using “Ford Bronco” guys, they’re using “2007 Ford Bronco SE touring AWD XL trim Sport” or whatever. It also always delights me when we’re making a receipt for some fancy retail brand that has $1200 purses but the space for our total only has 3 characters of space. I will forever love ereceipts for all of the wild data variations and how they destroy designs that weren’t robust enough to consider the extremes – sorry not sorry.
Back to data tables. Essentially – a receipt is a data table presented to the user which means we need to think a lot about how that data will be presented to screen reader/AT users.
We also need to really think about the content we’re putting into data tables. Ie – we don’t want to nest more tables inside a data table so you’ll want to make sure you’re creating the table correctly. Honestly I will break out my table into a spreadsheet program so I can check whether each column and row actually follows the header. You will likely need a separate table for the products/pricing/taxes section and the address/shipping section, for instance, because the content in those tables are not related to each other.



Table headers and what they do
Now that we’ve planned ahead on how we’re going to organize those data tables, it’s time for the most important part: The table headers!!
The table headers are basically stating what is in that column or row of the data. Usually we have “Products”; “Price”; “Subtotal”; “Tax”; or “Address” – anything that tells us what that text contained within that column/row portrays.
It’s important to have them in there to give that additional context, and it’s important that they are programmatically connected to the data in the table.
There are two requirements to make headers work correctly:
1) By making sure we are using the <th> tag to semantically tell assistive technologies that this content is the table header.
2) By making sure that the content in that <th> tag is in the same table as the data we’re pointing it to. This means that we’re not making a separate table per line or using nested tables to divide the content – we really need all of that data in that collection of data to be in the same table.
Let me go into details here because in layout tables – this doesn’t matter – but in data tables this matters quite a bit.
If you have a data table with equipment you’re purchasing with their prices, tax and totals, you need all of that data in one single table so that the product name, price, tax, and total remain programmatically associated with each other.

I’ll also just call this out – this is one of the bigger reasons why I always recommend clients do html coded receipts vs email builders – email builders just can’t do this. Each row ends up being its own table and that gets really sloppy for users.
Another issue – designers will exact control over the content in these blocks of content that can be hard to replicate in a single data table. The biggest issue here is padding changes throughout the table – Outlook is generally a real pain on padding adjustments at the table cell level. You’ll want to be cautious of this and allow for graceful degradation if you must.
This is also one of the only times I’ll recommend the use of colspans and rowspans! Just make sure that the information is still related to each other the way it needs to be. (More on that in Part 2 when we talk about testing)
Note: If you’re suddenly stressed because you use table headers to construct your mobile stacks – don’t worry! This is part of what screen readers look for in the layout vs data tables – this doesn’t cause problems.
What if you don’t have table headers?
Lots of ereceipts don’t have proper table headers – but as I just mentioned – they are very important for end users.
So – add table headers and make sure they’re in there. You can use a visibility-hidden class and hide them in most apple mail and iphone mail clients if you’d like to.
It’s a very minor thing that has huge impacts on users – add those table headers!
Another hiccup – Images of products
In many of these ereceipts we provide an image of the product next to the product name/description. This is a perfect example of a decorative image if ever there was one! So please make sure you are not linking these images and that you are using an empty alt attribute in those scenarios.
HOWEVER – I was messing around with different ways to lay these out within a data table and we have a pretty significant problem with this setup because nesting a table breaks the header-to-data association:

In this scenario – you have the table header “Item” but within the data cell of that item we have an image and text.
If I was just making a regular-old email I would just nest a table in that cell to control that layout… BUT… this pulls the content in that nested table out of the accessible data table.
So you are left with three options:
- Best option: Use colspan on the table header – again, I’m usually not a fan of using colspan and rowspan because they can cause content to be read out of order, but in this case we’re actually making the content easier to understand by allowing both of those columns to fit under the table header.
- Good compromise: Use a div to control the layout inside that table cell, and let that content stack in Outlook (flexible rendering!) This is another good option because it keeps the image and product together which is the best scenario for screen reader users… but the rendering can get complicated, especially if you are B2C or have lots of text that extends that section out even more.
- Can work okay: Use an additional column for that image and don’t put a header on it, keep that image decorative (not linked, no alt text) and let it be largely ignored by screen reader users. When users navigate via tables they still will encounter an empty table cell, which is annoying and redundant, so not the best case but if you must have perfect rendering, it’s better than nothing. Here’s an example:

Repeating this again – nested tables within the data table are going to cause problems so anything we do within the data table must be contained within the data table itself. Divs work, spans work, but other tables do not.
So that’s all the content I can fit in for this week! Hopefully you have a better idea of some of the experiential aspects of data tables and how we can set it up to work better for AT users.
If there’s one thing to remember: receipts are data, and data deserves real data tables.
Next week we’re going to hop right into some of the nitty-gritty details that ensure you’re doing it right – including how to test! See you then!