Overview
Tables are available in light and dark styles. They can be borderless or contained and can have the option for vertical and horizontal scrolling.
Examples
Borderless
When seeking to integrate and expand the information that accompanies a text, a borderless table can prove advantageous. It imparts a sense of continuity and cohesion, making the data feel more seamlessly integrated with the surrounding text.
Header | Header | Header |
---|---|---|
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Footer | Footer | Footer |
<table class="qld__table">
<caption>Table caption
<span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th scope="col">Header</th>
<th scope="col">Header</th>
<th scope="col">Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
Contained
A contained table style features visible borders around the table, its rows, and columns, creating a clear and well-defined structure. The borders help separate data and improve readability by emphasising individual cells.
Header | Header | Header |
---|---|---|
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Footer | Footer | Footer |
<div class="qld__table--contained">
<table class="qld__table">
<caption>Table caption
<span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th scope="col">Header</th>
<th scope="col">Header</th>
<th scope="col">Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
</div>
Vertical Scrolling
A scrollable table is ideal for dense data. Tables that are extremely long can have vertical scroll turned on via the vertical scroll class. On desktop these tables will scroll vertically after 1000px and on mobile they will scroll vertically after 640px.
Header | Header | ||
---|---|---|---|
Header | Header | Header | |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Cell | Cell | Cell | Cell |
Footer | Footer | Footer | Footer |
<div class="qld__table--scroll" tabindex="0">
<table class="qld__table qld__table--striped qld__table__col-2-left-border qld__table__col-3-left-border"><caption>Table caption <span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th rowspan="2" scope="col">Header</th>
<th colspan="3" class="qld__table__cell-left-border qld__table__cell--middle">Header</th>
</tr>
<tr>
<th scope="col" class="qld__table__cell-left-border">Header</th>
<th scope="col" class="qld__table__cell-left-border">Header</th>
<th scope="col" class="qld__table__cell-left-border">Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
</div>
Custom widths
You can apply custom widths based on the expected length of the data within a corresponding column by applying a utlity class to the <th>
elements.
By default tables are fluid and scale to the full width of the page, however a table can set to a desired width using the grid system and then use the qld__table__wrapper
which will turn on horizontal scrolling on smaller devices.
Header | Header | Header |
---|---|---|
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Footer | Footer | Footer |
<!--
15% col width: <th class="qld__table__header--width-15">
20% col width: <th class="qld__table__header--width-20">
25% col width: <th class="qld__table__header--width-25">
33% col width: <th class="qld__table__header--width-33">
40% col width: <th class="qld__table__header--width-40">
50% col width: <th class="qld__table__header--width-50">
75% col width: <th class="qld__table__header--width-75">
-->
<div class="qld__table__wrapper col-xs-10">
<table class="qld__table qld__table__col-2-left-border qld__table__col-3-left-border">
<caption>Table caption
<span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th scope="col" class="qld__table__header--width-10">Header</th>
<th scope="col">Header</th>
<th scope="col" class="qld__table__header--width-50">Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
</div>
Striped/Banded
For data tables with more rows, use the striped alternative. Styling even and odd rows in a different way can be helpful to people who have reading difficulties or who enlarge text. It acts as a visual guide. Highlighting the cell (and row/column) on mouseover and keyboard focus to support people to see where they are.
Header | Header | Header |
---|---|---|
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Cell | Cell | Cell |
Footer | Footer | Footer |
<div class="qld__table--contained">
<table class="qld__table qld__table--striped">
<caption>Table caption
<span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th scope="col">Header</th>
<th scope="col">Header</th>
<th scope="col">Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
</div>
Multilevel headings
Tables with multiple headers may also need to have a caption to identify them and a summary to describe the layout of the table, see Caption & Summary. In many cases, it is worth considering restructuring the information in such tables to make them less complex for all readers.
Header | Header | ||
---|---|---|---|
Header | Header | Number % | |
Cell | Cell | Cell | 5 |
Cell | Cell | Cell | 10 |
Cell | Cell | Cell | 15 |
Cell | Cell | Cell | 20 |
Cell | Cell | Cell | 25 |
Footer | Footer | Footer | Footer |
<div class="qld__table--contained">
<table class="qld__table qld__table--striped qld__table__col-2-left-border qld__table__col-3-left-border qld__table__col-4-num"><caption>Table caption<span class="qld__caption">Table is ordered by</span></caption>
<thead>
<tr>
<th rowspan="2" scope="col">Header</th>
<th colspan="3"
class="qld__table__cell-left-border qld__table__cell--middle">Header</th>
</tr>
<tr>
<th scope="col" class="qld__table__cell-left-border">Header</th>
<th scope="col" class="qld__table__cell-left-border">Header</th>
<th scope="col" class="qld__table__cell-left-border qld__table__cell--numeric">Number %</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>5</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>10</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>15</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>20</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
<td>25</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
<td>Footer</td>
</tr>
</tfoot>
</table>
</div>
Usage guidelines
When to use
- Displaying structured data, where each item has multiple properties or attributes.
- Comparing and contrasting multiple data points.
- When listing locations or resources that have similarly structured content for many items (US Government, 2022).
When not to use
- Presenting a small number of items with only one or two properties
- Don’t create a table for only one or two items. Report them in the body of the text instead (Australian Government, 2022).
- Displaying data that's better suited for visualisation, such as charts or graphs
- Don’t make tables with other tables inside them (known as ‘nested’ tables) (Australian Government, 2022).
- Tables shouldn't be used for layout purposes. Use Cascading Style Sheets (CSS) for layout. If there are already layout tables present, don’t use structural elements (like
<th>
or<caption>
) and attributes discussed in this tutorial, and do addrole="presentation"
to the<table>
element (WebAIM ,2018). - Long-form content. Table cell content should be brief and scannable. If you find yourself drafting multiple bullet points or paragraphs within a single table cell, the content is likely better off under conventional page headers or in an accordion.
- Groups of items with different structures.
How to use
Some people will look at tables before they read the text. For this reason, design tables so they're self-explanatory. You must still refer to the table to in the body of the text. Place the table immediately after the reference to it in the text (Australian Government, 2022).
You must give a table:
- a title (also called a caption)
- row and column headings
- information (entries)
- a cross-reference in the text.
You may also need to add notes below the table to help users understand the information and where it comes from.
Do
- Align text to the left and numeric data to the right (in left-to-right languages).
- The first column should be a human-readable record identifier instead of a “mystery meat” automatically generated ID (Laubheimer, 2022). This design will allow users to scan and locate a record of interest.
- Ensure the default order of the columns reflects the importance of the data. Once a user has located a potential record of interest, don’t force them to move their eyes back and forth between column 1 and column 20 because those are the most relevant columns (Laubheimer, 2022).
- Keep it simple, complex tables are more work for content creators as well as being harder to interpret for users. It’s usually better to break up complex tables into simple individual tables, each containing the data for one sub-topic (US Government, 2022).
- Keep the number of columns to a minimum as its easier for users to read down a long list of rows than it is to read across a long list of columns (US Government, 2022).
Don't
- Rely on colour as the only visual means of conveying information in tables.
- Leave cells empty. Use ‘zero’ or ‘nil’ or 'n/a' where there is no data. If it's numeric data, use the numeric zero (0). Only use zero if that is the true value.
- Do not to vary units or formatting within the same column (US Government, 2022).
Tips
For more detailed guidelines on table content see Australian Government Style manual for table design.
For more table design tips see W3C Table tips and ticks.
Research and rationale
Our table design is based on the original Digital Transformation Agency (DTA) table component. While this component worked well we found we needed to add more complex table designs such as tables within vertical scrolling and tables that could incorporate figures, captions and footers.
We drew inspirations for our updated table styles from the USA Design System and from the W3Cs comprehensive table guidelines.
Striping alternate rows
We inherited the functionality research by the DTA for striped tables. Striped tables work well for tables with large amounts of rows. The process of highlighting alternate rows helps increase readability and scanning speed. We implemented the same approach as the DTA which was to highlight the bottom row using the nth-last-child(odd)
selector as this provides the table with visual completeness.
Accent colours
WC3 recommends styling header <th> cells so they are visually distinct from standard cells (World Wide Web Consortium, n.d.). To add more visual distinctiveness to our table component we have used the accent colour as the bottom border for the header cells. This makes the header stand out more on the page and allowed as to incorporate more brand colours into the overall design.
Contained tables
When investigating table patterns we noticed that tables often came in two formats a borderless and a contained style. The original DTA design was based on a borderless approach which can be useful when you want the information to feel more a part of the text it accompanies. However what we found was that when there were multiple tables on a page or when the table needed to appear as self contained piece of information a contained style was more appropriate.
Further to this when designing tables for complex reporting websites such as the CHO Report we found that contained tables can provide better structure and organization, especially for complex data. Borders clearly delineate each cell, making it easier to read and comprehend the information.
Classes
Classes | Description |
---|---|
| This is added to the <table> element and is the class that sets all default styling for tables in our design system. |
qld__table--scroll | Add the qld__table--scroll class to a container around any qld__table to apply a vertical scrollbar if the columns exceed a maximum height. This is ideal for tables where vertical height is an important. |
qld__table--contained | Add the qld__table--contained class to a container around any qld__table to apply the contained style. |
qld__table__wrapper | Add the qld__table__wrapper class to a container around any qld__table to apply a horizontal scrollbar this should be combined with a specified width. |
| Reduces the row height and vertical spacing to display more table rows within a limited space. Should only be used with dense, numerical data, not text content. Pairs well with scrollable and striped variants, but isn't suitable for use with stacked variants. |
| This class needs to be added to the table <caption> it applies the default heading style for tables. |
| Applies alternating horizontal striping to help the eye track across table rows. Pairs well with the scrollable variant. |
qld__table__cell--numeric | This is an utility class that applies tabular numbers and right align text within a specific cell. |
qld__table__cell--middle | This is an utility class that centre align text within a specific cell. |
qld__table__col-[#]-num replace [#] with a number 1-10 | This is an utility class that can is appled the the <table> element it will apply tabular numbers and right align text within a specific column. For example the code below applies to all cels in the 4 column from the left. <table class="qld__table qld__table__col-4-num"> |
qld__table__cell-left-border | This is an utility class that add a left border to a specific cell. |
qld__table__cell-right-border | This is an utility class that add a left border to a specific cell. |
qld__table__col-[#]-left-border repace [#] with a number 1-10 |
This is an utility class that can is applied the the <table> element it will apply a left border to all cells within a specific column. For example the code below applies to all cels in the 4 column from the left. <table class="qld__table qld__table__col-2-left-border> |
qld__table__col-[#]-right-border replace [#] with a number 1-10 | This is an utility class that can is applied the the <table> element it will apply a right biorder to all cells within a specific column. For example the code below applies to all cels in the 4 column from the left. <table class="qld__table qld__table__col-2-right-border> |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 10%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 15%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 20%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 25%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 33%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 40%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 50%. |
| This utility class must be appled to <th>. It will override the default width and set the corresponding col to a width of 75%. |
Accessible table requirements
Keep these considerations in mind if you're modifying the Design System or creating a custom component.
For more complex table structures, review the WCAG accessibility recommendations for tables.
WCAG guielines
1.3.1 Info and relationships
Logical structure should be conveyed through table markup with proper table elements (e.g., <table>
, <thead>
, <tbody>
, <th>
, and <td>
).
2.1.1 Keyboard
Ensure all table functionality is accessible via keyboard (W3C, 2018).
2.4.7 Focus visible
Scrollable tables need to be focusable. When you use the .qld__table--scroll
variant with a table, you must add the tabindex="0"
attribute to the scrollable element. This attribute assures that users navigating with a keyboard are able to select and scroll the table. Tabindex="0"
enables focus on elements that don't get focus by default. This attribute doesn't change the tab order. It places the element in the logical navigation flow.
2.4.6 Headings and labels
- Descriptive column headers should be used
- Simple tables can have up to 2 rows of headers. Each header cell should have
scope="col"
orscope="row"
- Complex tables have more than 2 levels of headers. Each header should have a unique id and each data cell should have a headers attribute with each related header cell’s id listed. (W3C, 2018)
3.3.2:Labels or instructions
- Provide clear instructions for any interactive table elements
- Add title and attribution in a caption. When adding a title, attribution, or a last-updated date to a table, include it in the
<caption>
tag inside of the<table>
element
4.1.2 Name, role, value
Use ARIA roles and attributes (e.g., role="table"
or role="grid"
) and relevant ARIA attributes (e.g., aria-labelledby
, aria-describedby
, aria-sort
, and aria-controls
) to improve the accessibility of tables.
4.1.3 Status Messages
For tables with dynamic content or interactive features like sorting, use aria-live
regions to announce changes to screen reader users.
References
Australian Government (2022). Tables Australian Government Style Manual, accessed 7 May 2023.
WebAIM (2018) Creating accessible tables, WebAIM website, accessed 7 May 2023.
GOV.UK (2020) ‘Tables: when to use tables and how to make them accessible’, Content design: planning, writing and managing content, GOV.UK, accessed 7 May 2023.
GOV.UK (n.d.) ‘Table’, Design system, GOV.UK, accessed 7 May 2023.
W3C (2018) Web Content Accessibility Guidelines (WCAG) 2.1, World Wide Web Consortium, accessed 10 April 2023.
US Government (2022)Table, USWDS (US Web Design System), accessed 10 April 2023.
World Wide Web Consortium (W3C) (n.d.) Tables Tutorial, W3C, accessed 2 August 2023.
Laubheimer P (2022) Data Tables: Four Major User Tasks, Nielsen Norman Group, accessed 2 August 2023.
Last updated: May 2024