Data table
The data table can be used for displaying data in a structured form. It can be extended with functions and various kinds of content based on the use case.
Data tables meant to display big chunks of data enabling you to include certain data manipulation elements alongside the data itself.
Component consists of 2 elements: flix-data-table-wrapper
and flix-data-table
itself.
Available class modifiers for both of them presented in tables bellow:
Modifiers
Wrapper modifiers
flix-data-table-wrapper--collapsed
- makes table width adapt to it's contents
flix-data-table-wrapper--responsive
- triggers a responsive horizontal scroll on screens bellow `sm` breakpoint
flix-data-table-wrapper--scrollable
- creates a scrollable table wrapper
Table modifiers
flix-data-table--small
- a smaller table with less paddings in the cells
flix-data-table--sticky-header
- makes table header sticky
flix-data-table--plain
- removes "zebra" coloring for rows
Examples
Data table with a fixed header and selectable rows
In order to make things look nice you need to ensure 2 things:
- trigger a "scrollable" variation of
flix-data-table-wrapper
and make sure to specify it's height; - add
flix-data-table--sticky-header
modifier to data-table element.
Selectable rows:
- Highlight the selected row by adding the
flix-data-table__row--selected
modifier; - Add
aria-selected="true|false"
so screen readers can announce the row status correctly.
Checkout provided example for more info.
<div class="flix-data-table-wrapper flix-data-table-wrapper--scrollable" style="height:246px">
<table class="flix-data-table flix-data-table--sticky-header"><caption>Trips departing from your location</caption><thead class="flix-data-table__header"><tr><th scope="col">
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-all" value="select-all"/>
<label class="flix-checkbox__label" for="example-table-checkbox-all">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</th><th scope="col">
<span>Departure</span>
<button aria-label="Sort rows by departure time" class="flix-data-table__header-btn" type="button">
<i class="flix-icon flix-icon-arrow-big-down" aria-hidden="true"></i>
</button>
</th><th scope="col">
<span>Price</span>
<button aria-label="Sort rows by price" class="flix-data-table__header-btn" type="button">
<i class="flix-icon flix-icon-expand" aria-hidden="true"></i>
</button>
</th><th scope="col">
<span>Seats</span>
<button aria-label="Sort rows by available seats" class="flix-data-table__header-btn" type="button">
<i class="flix-icon flix-icon-expand" aria-hidden="true"></i>
</button>
</th><th scope="col">
<span>Links</span>
<button aria-label="Sort rows by links" class="flix-data-table__header-btn" type="button">
<i class="flix-icon flix-icon-expand" aria-hidden="true"></i>
</button>
</th></tr></thead><tbody><tr class="flix-data-table__row" aria-selected="false"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-1" value="select-1"/>
<label class="flix-checkbox__label" for="example-table-checkbox-1">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">07:05 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row flix-data-table__row--selected" aria-selected="true"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-2" checked="" value="select-2"/>
<label class="flix-checkbox__label" for="example-table-checkbox-2">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">07:00 h</td><td>22.99€</td><td>29</td><td><a class="flix-link flix-element-has-hint" href="#" data-hint="Uh oh... I am a hint">Details</a></td></tr><tr class="flix-data-table__row" aria-selected="false"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-3" value="select-3"/>
<label class="flix-checkbox__label" for="example-table-checkbox-3">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">06:50 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row" aria-selected="false"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-4" value="select-4"/>
<label class="flix-checkbox__label" for="example-table-checkbox-4">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">06:35 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row" aria-selected="false"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-5" value="select-5"/>
<label class="flix-checkbox__label" for="example-table-checkbox-5">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">06:25 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row" aria-selected="false"><td>
<div class="flix-checkbox flix-checkbox--sm">
<input class="flix-checkbox__input" type="checkbox" id="example-table-checkbox-6" value="select-6"/>
<label class="flix-checkbox__label" for="example-table-checkbox-6">
<span class="flix-sr-only">Select row</span>
</label>
</div>
</td><td scope="row">06:05 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr></tbody></table>
</div>
Smaller plain data table with pagination
You can associate a pager component to the data table to create a paginated table. To do so both components must be wrapper with a flix-data-table-wrapper
container to ensure proper spacing and alignment.
In this case, if the data table pagination action happens inside of the same page, you should use pagination with buttons instead of links. Activating the buttons will update the table state with new items.
As a good practice for accessibility you should announce any live changes on the table, you can do so by making the table a live region. In this case, adding aria-live="polite"
will ensure the screen readers will announce that the table has been updated when pagination occurs.
Additionally, adding a table caption informing which page is currently active is advised:
<div class="flix-data-table-controls">
<div class="flix-data-table-wrapper">
<table aria-live="polite" class="flix-data-table flix-data-table--small flix-data-table--plain"><caption>Trips: Page 2 of 5</caption><thead class="flix-data-table__header"><tr><th scope="col">Departure</th><th scope="col">Price</th><th scope="col">Seats</th><th scope="col">Links</th></tr></thead><tbody><tr class="flix-data-table__row"><td scope="row">07:00 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row"><td scope="row">08:00 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row"><td scope="row">09:05 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr><tr class="flix-data-table__row"><td scope="row">09:30 h</td><td>22.99€</td><td>29</td><td><a class="flix-link" href="#">Details</a></td></tr></tbody></table>
</div>
<nav class="flix-pager" aria-label="Data table pagination">
<ul class="flix-pager__list">
<li class="flix-pager__item">
<button class="flix-pager__link flix-pager__link--prev flix-pager__link--disabled" aria-disabled="true" disabled="">
<span class="flix-sr-only">Previous page</span>
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link">
1
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link" aria-current="true">
2
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link">
3
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link">
4
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link">
5
</button>
</li>
<li class="flix-pager__item">
<button type="button" class="flix-pager__link flix-pager__link--next">
<span class="flix-sr-only">Next page</span>
</button>
</li>
</ul>
</nav>
</div>
Smaller and collapsed data table with a footer
Departure | Seats | Bikes |
---|---|---|
06:05 h | 40 | 11 |
07:00 h | 38 | 2 |
08:10 h | 29 | 6 |
10:15 h | 29 | 2 |
12:35 h | 14 | 0 |
Totals | 150 | 21 |
<div class="flix-data-table-wrapper flix-data-table-wrapper--collapsed">
<table class="flix-data-table flix-data-table--small"><caption>Available space per trip</caption><thead class="flix-data-table__header"><tr><th scope="col">Departure</th><th scope="col">Seats</th><th scope="col">Bikes</th></tr></thead><tbody><tr class="flix-data-table__row"><td scope="row">06:05 h</td><td>40</td><td>11</td></tr><tr class="flix-data-table__row flix-data-table__row--selected"><td scope="row">07:00 h</td><td>38</td><td>2</td></tr><tr class="flix-data-table__row"><td scope="row">08:10 h</td><td>29</td><td>6</td></tr><tr class="flix-data-table__row"><td scope="row">10:15 h</td><td>29</td><td>2</td></tr><tr class="flix-data-table__row"><td scope="row">12:35 h</td><td>14</td><td>0</td></tr></tbody><tfoot class="flix-data-table__footer"><tr><th scope="col">Totals</th><th scope="col">150</th><th scope="col">21</th></tr></tfoot></table>
</div>
Scopes
The declaration of the scope
attribute on the table headings (the th
tags) helps people who are using screen readers while they navigate the table. If your table has headers, please include the adequate scope for them.
The scope="col"
should be applied to the cells inside the <thead>
and identify the data of a column. E.g.: "This column has Prices".
The scope="row"
should be applied in header cells that identify the row (if there are any). E.g.: "This row is the 6am ride."