Use the table component to let users compare information in rows and columns.
A table is a great way to present things such as:
Do not use the table component for layout.
Furthermore, do not use a table component:
For presenting simple, non-tabular data, consider using a list instead. For presenting teasers of content, use the collection of teasers instead.
A table organizes information in rows and columns:
The table styling features alternating row colors.
On desktop resolutions, when it is desired, individual rows, columns or even individual cells can be highlighted by overriding the background color. The colors that can be used are success
, warning
and error
. To override the background color with one of these colors, apply the desired CSS class to a row or cell.
On tablets in portrait mode and mobile resolutions, the table component is rendered as an HTML description list. Highlighting cannot be used here.
{# Table (desktop) #}
<div class="responsive-table">
<div class="table-wrapper">
<table>
{% for row in table.rows %}
<tr {{ row.attributes }}>
{% for cell in row.cells %}
{% if cell.header %}
<th {{ cell.attributes }}>{{ cell.text }}</th>
{% else %}
<td {{ cell.attributes }}>{{ cell.text }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
<caption>
{{ caption }}
</caption>
</table>
</div>
</div>
{# List (mobile) #}
<div aria-labelledby="{{ _self.name }}-list-description" class="table-list">
<ul>
{% for row in table.rows|slice(table.columnHeaders ? 1 : 0) %}
<li {{ row.attributes }}>
{# Row title #}
{% if table.rowHeaders %}
{% set rowHeader = row.cells|first %}
<h3>{{ rowHeader.text }}</h3>
{% endif %}
{# Cells #}
<dl>
{% for cell in row.cells|slice(table.rowHeaders ? 1 : 0) %}
{% if table.columnHeaders %}
{% set headingIndex = loop.index - (table.rowHeaders ? 0 : 1) %}
<dt>{{ table.rows|first.cells[headingIndex].text }}</dt>
{% endif %}
<dd {{ cell.attributes }}>{{ cell.text }}</dd>
{% endfor %}
</dl>
</li>
{% endfor %}
</ul>
<div class="list-description" id="{{ _self.name }}-list-description">
{{ caption }}
</div>
</div>
<div class="responsive-table">
<div class="table-wrapper">
<table>
<tr>
<th scope="row">Header row 1</th>
<td>Row 1, column 2</td>
<td>Row 1, column 3</td>
<td>Row 1, column 4</td>
<td>Row 1, column 5</td>
<td>Row 1, column 6</td>
</tr>
<tr>
<th scope="row">Header row 2</th>
<td class="success">Row 2, column 2</td>
<td>Row 2, column 3</td>
<td>Row 2, column 4</td>
<td>Row 2, column 5</td>
<td>Row 2, column 6</td>
</tr>
<tr>
<th scope="row">Header row 3</th>
<td>Row 3, column 2</td>
<td>Row 3, column 3</td>
<td>Row 3, column 4</td>
<td>Row 3, column 5</td>
<td>Row 3, column 6</td>
</tr>
<tr>
<th scope="row">Header row 4</th>
<td>Row 4, column 2</td>
<td>Row 4, column 3</td>
<td>Row 4, column 4</td>
<td class="error">Row 4, column 5</td>
<td>Row 4, column 6</td>
</tr>
<tr>
<th scope="row">Header row 5</th>
<td>Row 5, column 2</td>
<td>Row 5, column 3</td>
<td>Row 5, column 4</td>
<td>Row 5, column 5</td>
<td>Row 5, column 6</td>
</tr>
<tr>
<th scope="row">Header row 6</th>
<td>Row 6, column 2</td>
<td>Row 6, column 3</td>
<td class="warning">Row 6, column 4</td>
<td>Row 6, column 5</td>
<td>Row 6, column 6</td>
</tr>
<tr>
<th scope="row">Header row 7</th>
<td>Row 7, column 2</td>
<td>Row 7, column 3</td>
<td>Row 7, column 4</td>
<td>Row 7, column 5</td>
<td>Row 7, column 6</td>
</tr>
<tr>
<th scope="row">Header row 8</th>
<td>Row 8, column 2</td>
<td>Row 8, column 3</td>
<td>Row 8, column 4</td>
<td>Row 8, column 5</td>
<td>Row 8, column 6</td>
</tr>
<tr class="success">
<th scope="row">Header row 9</th>
<td>Row 9, column 2</td>
<td>Row 9, column 3</td>
<td>Row 9, column 4</td>
<td>Row 9, column 5</td>
<td>Row 9, column 6</td>
</tr>
<tr>
<th scope="row">Header row 10</th>
<td>Row 10, column 2</td>
<td>Row 10, column 3</td>
<td>Row 10, column 4</td>
<td>Row 10, column 5</td>
<td>Row 10, column 6</td>
</tr>
<caption>
This is a table caption that describes the table content.
</caption>
</table>
</div>
</div>
<div aria-labelledby="table-without-column-headers-list-description" class="table-list">
<ul>
<li>
<h3>Header row 1</h3>
<dl>
<dd>Row 1, column 2</dd>
<dd>Row 1, column 3</dd>
<dd>Row 1, column 4</dd>
<dd>Row 1, column 5</dd>
<dd>Row 1, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 2</h3>
<dl>
<dd class="success">Row 2, column 2</dd>
<dd>Row 2, column 3</dd>
<dd>Row 2, column 4</dd>
<dd>Row 2, column 5</dd>
<dd>Row 2, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 3</h3>
<dl>
<dd>Row 3, column 2</dd>
<dd>Row 3, column 3</dd>
<dd>Row 3, column 4</dd>
<dd>Row 3, column 5</dd>
<dd>Row 3, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 4</h3>
<dl>
<dd>Row 4, column 2</dd>
<dd>Row 4, column 3</dd>
<dd>Row 4, column 4</dd>
<dd class="error">Row 4, column 5</dd>
<dd>Row 4, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 5</h3>
<dl>
<dd>Row 5, column 2</dd>
<dd>Row 5, column 3</dd>
<dd>Row 5, column 4</dd>
<dd>Row 5, column 5</dd>
<dd>Row 5, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 6</h3>
<dl>
<dd>Row 6, column 2</dd>
<dd>Row 6, column 3</dd>
<dd class="warning">Row 6, column 4</dd>
<dd>Row 6, column 5</dd>
<dd>Row 6, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 7</h3>
<dl>
<dd>Row 7, column 2</dd>
<dd>Row 7, column 3</dd>
<dd>Row 7, column 4</dd>
<dd>Row 7, column 5</dd>
<dd>Row 7, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 8</h3>
<dl>
<dd>Row 8, column 2</dd>
<dd>Row 8, column 3</dd>
<dd>Row 8, column 4</dd>
<dd>Row 8, column 5</dd>
<dd>Row 8, column 6</dd>
</dl>
</li>
<li class="success">
<h3>Header row 9</h3>
<dl>
<dd>Row 9, column 2</dd>
<dd>Row 9, column 3</dd>
<dd>Row 9, column 4</dd>
<dd>Row 9, column 5</dd>
<dd>Row 9, column 6</dd>
</dl>
</li>
<li>
<h3>Header row 10</h3>
<dl>
<dd>Row 10, column 2</dd>
<dd>Row 10, column 3</dd>
<dd>Row 10, column 4</dd>
<dd>Row 10, column 5</dd>
<dd>Row 10, column 6</dd>
</dl>
</li>
</ul>
<div class="list-description" id="table-without-column-headers-list-description">
This is a table caption that describes the table content.
</div>
</div>
{
"table": {
"rows": [
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 1"
},
{
"header": false,
"attributes": null,
"text": "Row 1, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 1, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 1, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 1, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 1, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 2"
},
{
"header": false,
"attributes": "class=\"success\"",
"text": "Row 2, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 2, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 2, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 2, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 2, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 3"
},
{
"header": false,
"attributes": null,
"text": "Row 3, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 3, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 3, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 3, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 3, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 4"
},
{
"header": false,
"attributes": null,
"text": "Row 4, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 4, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 4, column 4"
},
{
"header": false,
"attributes": "class=\"error\"",
"text": "Row 4, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 4, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 5"
},
{
"header": false,
"attributes": null,
"text": "Row 5, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 5, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 5, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 5, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 5, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 6"
},
{
"header": false,
"attributes": null,
"text": "Row 6, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 6, column 3"
},
{
"header": false,
"attributes": "class=\"warning\"",
"text": "Row 6, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 6, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 6, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 7"
},
{
"header": false,
"attributes": null,
"text": "Row 7, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 7, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 7, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 7, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 7, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 8"
},
{
"header": false,
"attributes": null,
"text": "Row 8, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 8, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 8, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 8, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 8, column 6"
}
]
},
{
"attributes": "class=\"success\"",
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 9"
},
{
"header": false,
"attributes": null,
"text": "Row 9, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 9, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 9, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 9, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 9, column 6"
}
]
},
{
"attributes": null,
"cells": [
{
"header": true,
"attributes": "scope=\"row\"",
"text": "Header row 10"
},
{
"header": false,
"attributes": null,
"text": "Row 10, column 2"
},
{
"header": false,
"attributes": null,
"text": "Row 10, column 3"
},
{
"header": false,
"attributes": null,
"text": "Row 10, column 4"
},
{
"header": false,
"attributes": null,
"text": "Row 10, column 5"
},
{
"header": false,
"attributes": null,
"text": "Row 10, column 6"
}
]
}
],
"columnHeaders": false,
"rowHeaders": true
},
"caption": "This is a table caption that describes the table content."
}
div.responsive-table,
div.table-list {
position: relative;
padding-bottom: 1.4rem;
.list-description,
caption {
@include theme('color', 'color-tertiary--lighten-1', 'table-caption-color');
position: absolute;
bottom: 0;
width: 100%;
margin: .4rem 0;
font-size: .6rem;
text-align: right;
}
}
div.responsive-table {
@include focus-style;
@include tablet {
display: inline-block;
max-width: 100%;
}
display: none;
.table-wrapper {
overflow-x: auto;
}
table {
caption-side: bottom;
th {
@include bold-text;
font-size: .9rem;
line-height: 1.7;
&[scope="col"] {
@include theme('border-color', 'color-primary--lighten-5', 'table-header-border-color');
border-bottom: 2px solid;
}
}
td {
font-size: .8rem;
&:nth-of-type(n) {
@include table-backgrounds;
}
}
tr {
&:nth-of-type(odd) th:not([scope="col"]),
&:nth-of-type(odd) td {
@include theme('background-color', 'color-primary--lighten-5', 'table-row-color');
}
&:nth-of-type(n) {
@include table-backgrounds(th);
@include table-backgrounds(td);
}
}
// Tables with a thead have should have the same color scheme.
thead ~ tbody tr {
[class*="cs--"] &:nth-of-type(odd) th:not([scope="col"]),
[class*="cs--"] &:nth-of-type(odd) td {
background-color: transparent;
}
&:nth-of-type(even) th:not([scope="col"]),
&:nth-of-type(even) td {
@include theme('background-color', 'color-primary--lighten-5', 'table-row-color');
}
}
th,
td {
@include theme('color', 'color-tertiary', 'table-content-color');
padding: .3rem .2rem;
text-align: left;
}
}
}
div.table-list {
@include tablet {
display: none;
}
display: block;
&.js-hidden {
display: none;
}
ul {
@extend %list-no-style;
li {
padding: .6rem .3rem;
font-size: .9rem;
&:nth-child(2n) {
@include theme('background-color', 'color-primary--lighten-5', 'table-list-row-color');
}
}
}
dl {
display: flex;
flex-wrap: wrap;
margin: 0;
> dt {
@include phablet {
flex: 0 30%;
}
flex: 0 50%;
line-height: 2.1;
+ dd {
flex: 0 50%;
@include phablet {
flex: 0 70%;
}
}
}
> dd {
flex: 0 100%;
margin: 0;
padding: 0;
font-size: .8rem;
line-height: 2.4;
}
}
}
/* global ResponsiveTable */
'use strict';
(function () {
var tablesNodeList = document.querySelectorAll('.responsive-table .table-wrapper');
// Optimise all tables with a wrapper div.responsive-table
for (var i = 0; i < tablesNodeList.length; i++) {
var table = tablesNodeList[i];
// Adds accessibility support.
new ResponsiveTable(table, {
scrollableText: '(scroll to see more)'
});
}
})();
'use strict';
/* global define, module */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
}
else {
if (typeof exports === 'object') {
module.exports = factory();
}
else {
root.ResponsiveTable = factory();
}
}
}(this || window, function () {
return function (element, options) {
/**
* The table caption.
*/
let caption = null;
/**
* Determine if the table should be focusable.
*/
const determineFocusable = () => {
var scrollableWidth = element.parentNode.querySelector('table').scrollWidth;
var containerWidth = element.parentNode.clientWidth;
// Check if element is scrollable.
if (scrollableWidth <= containerWidth) {
// If not remove the tab focus.
element.removeAttribute('tabindex');
}
else if (caption) {
caption.innerText += ' ' + options.scrollableText;
}
};
/**
* Setup the responsive table.
*/
const setupResponsiveTable = () => {
// Set caption id.
if (caption && !caption.hasAttribute('id')) {
const tableUid = Math.random().toString(36).substr(2, 16);
caption.setAttribute('id', 'responsive-table-caption-caption-' + tableUid);
element.setAttribute('aria-labelledby', caption.getAttribute('id'));
}
// Set table container attributes.
element.setAttribute('tabindex', '0');
element.setAttribute('role', 'group');
// Set th scope attributes.
let firstRow = element.querySelector('tr');
let tableHeadingsNodeList = firstRow.querySelectorAll('th');
if (firstRow.getElementsByTagName('th').length === firstRow.querySelectorAll('*').length) {
for (let i = 0; i < tableHeadingsNodeList.length; i++) {
// If no scope attribute, set it.
if (!tableHeadingsNodeList[i].hasAttribute('scope')) {
tableHeadingsNodeList[i].setAttribute('scope', 'col');
}
}
}
// Set tr scope attributes if first child is a th.
let tableRowsNodeList = element.querySelectorAll('tr');
for (let i = 0; i < tableRowsNodeList.length; i++) {
let firstChild = tableRowsNodeList[i].firstElementChild;
// If no scope attribute, set it.
if (!firstChild.hasAttribute('scope')) {
firstChild.setAttribute('scope', 'row');
}
}
determineFocusable();
};
/**
* Entry point of the script.
*
*/
const init = () => {
if (!element) {
return;
}
caption = element.querySelector('caption');
setupResponsiveTable();
};
init();
return {};
};
}));