# Table

> Tables lay out rows of data, with variants for responsive, sortable, selectable and sticky-header tables.

Present data in clean, responsive tables. Add hover states, striped rows, sorting, and other options to improve readability.

Source: https://docs.tabler.io/ui/components/table

---

## Basic table

The basic table design has light padding and the presented data is separated with horizontal dividers. It helps provide users with all the necessary information, without overwhelming them with visuals.

The `.table` class adds basic styling to a table:

```html
<div class="table-responsive">
  <table class="table table-vcenter">
    <thead>
      <tr>
        <th>Name</th>
        <th>Title</th>
        <th>Email</th>
        <th>Role</th>
        <th class="w-1"></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Paweł Kuna</td>
        <td class="text-secondary">UI Designer, Training</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:paweluna@howstuffworks.com">paweluna@howstuffworks.com</a>
          </a>
        </td>
        <td class="text-secondary">User</td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
      <tr>
        <td>Jeffie Lewzey</td>
        <td class="text-secondary">Chemical Engineer, Support</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:jlewzey1@seesaa.net">jlewzey1@seesaa.net</a>
          </a>
        </td>
        <td class="text-secondary">Admin</td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
      <tr>
        <td>Mallory Hulme</td>
        <td class="text-secondary">Geologist IV, Support</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:mhulme2@domainmarket.com">mhulme2@domainmarket.com</a>
          </a>
        </td>
        <td class="text-secondary">User</td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
      <tr>
        <td>Dunn Slane</td>
        <td class="text-secondary">Research Nurse, Sales</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:dslane3@epa.gov">dslane3@epa.gov</a>
          </a>
        </td>
        <td class="text-secondary">Owner</td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
      <tr>
        <td>Emmy Levet</td>
        <td class="text-secondary">VP Product Management, Accounting</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:elevet4@senate.gov">elevet4@senate.gov</a>
          </a>
        </td>
        <td class="text-secondary">Admin</td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
    </tbody>
  </table>
</div>
```

## Responsive tables

Use the `.table-responsive` class across each breakpoint for horizontal scrolling tables. If you want to create responsive tables up to a specific breakpoint, use `.table-responsive{-sm|-md|-lg|-xl}`. From that breakpoint and up, the table will behave normally, rather than scroll horizontally.

```html
<table class="table table-responsive">
  <thead>
    <tr>
      <th>#</th>
      <th class="text-nowrap">Heading 1</th>
      <th class="text-nowrap">Heading 2</th>
      <th class="text-nowrap">Heading 3</th>
      <th class="text-nowrap">Heading 4</th>
      <th class="text-nowrap">Heading 5</th>
      <th class="text-nowrap">Heading 6</th>
      <th class="text-nowrap">Heading 7</th>
      <th class="text-nowrap">Heading 8</th>
      <th class="text-nowrap">Heading 9</th>
      <th class="text-nowrap">Heading 10</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1</th>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
```

## No wrap

If you don't want the table cell content to wrap to another line, use the `table-nowrap` class.

```html
<div class="table-responsive">
  <table class="table table-vcenter table-nowrap">
    <thead>
      <tr>
        <th>Name</th>
        <th>Title</th>
        <th>Email</th>
        <th>Role</th>
        <th></th>
        <th class="w-1"></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Paweł Kuna</td>
        <td class="text-secondary">UI Designer, Training</td>
        <td class="text-secondary">
          <a href="#" class="text-reset">
            <a href="mailto:paweluna@howstuffworks.com">paweluna@howstuffworks.com</a>
          </a>
        </td>
        <td class="text-secondary">User</td>
        <td> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi, commodi cupiditate debitis deserunt expedita hic incidunt iste modi molestiae nesciunt non nostrum perferendis perspiciatis placeat praesentium quaerat quo repellendus, voluptates. </td>
        <td>
          <a href="#">Edit</a>
        </td>
      </tr>
    </tbody>
  </table>
</div>
```

## Table variants

Use contextual classes such as `.table-primary` or `.table-danger` on a `<tr>` element to color rows and highlight their meaning.

```html
<table class="table">
  <thead>
    <tr>
      <th scope="col">Class</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Default</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-primary">
      <th scope="row">Primary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-secondary">
      <th scope="row">Secondary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-success">
      <th scope="row">Success</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-danger">
      <th scope="row">Danger</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-warning">
      <th scope="row">Warning</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-info">
      <th scope="row">Info</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-light">
      <th scope="row">Light</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-dark">
      <th scope="row">Dark</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
```

## Table with sticky header

Add the `.sticky-top` class to the `<thead>` element to keep the header row visible while users scroll through a long table.

```html
<table class="table">
  <thead class="sticky-top">
    <tr>
      <th scope="col">Class</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Default</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Primary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Secondary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Success</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Danger</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Warning</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Info</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Light</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-dark">
      <th scope="row">Dark</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Default</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Primary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Secondary</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Success</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Danger</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Warning</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Info</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">Light</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr class="table-dark">
      <th scope="row">Dark</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
```

## Sortable headers

Put a `.table-sort` button inside a `th` to get a sortable-looking header. The button shows a neutral arrow, and adding `.asc` or `.desc` marks the current direction.

Sorting itself is up to you - the classes only style the header. Tabler's own demo uses [List.js](https://listjs.com) and passes the field through `data-sort`.

```html
<table class="table">
  <thead>
    <tr>
      <th>
        <button class="table-sort" data-sort="sort-name">Name</button>
      </th>
      <th>
        <button class="table-sort asc" data-sort="sort-city">City</button>
      </th>
      <th>
        <button class="table-sort" data-sort="sort-score">Score</button>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Elliot Fisher</td>
      <td>Adelaide</td>
      <td>92</td>
    </tr>
    <tr>
      <td>Beverly Mills</td>
      <td>Bristol</td>
      <td>78</td>
    </tr>
  </tbody>
</table>
```

## Selectable rows

Add `.table-selectable` to the table and `.table-selectable-check` to each checkbox. A checked row gets a highlighted background, with no JavaScript involved.

Inside such a row you can swap content with `.on-checked` and `.on-unchecked`: the first only shows while the row is selected, the second while it is not.

```html
<table class="table table-selectable">
  <thead>
    <tr>
      <th class="w-1">
        <input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select all rows" />
      </th>
      <th>Task</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select task" checked />
      </td>
      <td>Prepare the release notes</td>
      <td>
        <span class="on-checked">Selected</span>
        <span class="on-unchecked text-secondary">Not selected</span>
      </td>
    </tr>
    <tr>
      <td>
        <input type="checkbox" class="form-check-input table-selectable-check" aria-label="Select task" />
      </td>
      <td>Review the pull request</td>
      <td>
        <span class="on-checked">Selected</span>
        <span class="on-unchecked text-secondary">Not selected</span>
      </td>
    </tr>
  </tbody>
</table>
```

## Stacked table on small screens

A wide table can stack into a list instead of scrolling sideways. Use `.table-mobile-{breakpoint}` - `.table-mobile-md` stacks below `md` - and give every cell a `data-label`, which becomes the label above its value.

```html
<table class="table table-mobile-md">
  <thead>
    <tr>
      <th>Name</th>
      <th>City</th>
      <th>Score</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td data-label="Name">Elliot Fisher</td>
      <td data-label="City">Adelaide</td>
      <td data-label="Score">92</td>
    </tr>
    <tr>
      <td data-label="Name">Beverly Mills</td>
      <td data-label="City">Bristol</td>
      <td data-label="Score">78</td>
    </tr>
  </tbody>
</table>
```

## Borderless, centered and transparent tables

`.table-borderless` removes the header background, `.table-transparent` clears the header and body backgrounds, and `.table-center` centers every cell.

```html
<table class="table table-borderless table-center">
  <thead>
    <tr>
      <th>Plan</th>
      <th>Seats</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Starter</td>
      <td>3</td>
      <td>$9</td>
    </tr>
    <tr>
      <td>Team</td>
      <td>10</td>
      <td>$29</td>
    </tr>
  </tbody>
</table>
```

## Truncated cell

A long value in one column stretches the whole table. Put `td-truncate` on that cell and `text-truncate` on the element inside it, and the text is cut with an ellipsis while the other columns keep their width.

```html
<table class="table">
  <thead>
    <tr>
      <th class="w-1">Name</th>
      <th>Note</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Elliot Fisher</td>
      <td class="td-truncate">
        <div class="text-truncate"> A note long enough that it would otherwise push the first column out of the way and stretch the table past the width of the page. </div>
      </td>
    </tr>
  </tbody>
</table>
```

## Accessibility

A table is read cell by cell, so the structure has to say what each cell means.

- Use `th` with `scope="col"` or `scope="row"` for headers. Without a scope, a screen reader cannot tie a value back to its column.
- Add a `caption` when the table needs a title. It is announced first and stays tied to the table, unlike a heading above it.
- In a selectable table, give every checkbox its own `aria-label` - "Select task" is enough, and "Select all rows" for the one in the header.
- `.table-sort` is a real `button`, so it is reachable by keyboard. Once you wire up sorting, also set `aria-sort` on the `th` to `ascending`, `descending` or `none`.
- A stacked `.table-mobile-*` still keeps its header cells in the markup, so the reading order stays correct while the `data-label` text carries the column name visually.

## SCSS variables

Use these SCSS variables to customize tables. The default values are:

```scss
$table-bg: transparent;
$table-color: inherit;
$table-accent-bg: transparent;

$table-cell-padding-x: 0.75rem;
$table-cell-padding-y: 0.75rem;
$table-cell-padding-y-sm: 0.25rem;
$table-cell-padding-x-sm: 0.25rem;
$table-cell-vertical-align: top;

$table-border-factor: 0.2;
$table-border-width: var(--border-width);
$table-border-color: var(--border-color-translucent);

$table-th-padding-x: $table-cell-padding-x;
$table-th-padding-y: 0.5rem;
$table-th-font-weight: null;
$table-th-bg: var(--bg-surface-tertiary);

$table-striped-color: $table-color;
$table-striped-bg-factor: 0.05;
$table-striped-order: even;
$table-striped-bg: var(--bg-surface-tertiary);
$table-striped-columns-order: even;

$table-group-separator-color: var(--border-color-translucent);

$table-active-color: $table-color;
$table-active-bg-factor: 0.1;
$table-active-bg: var(--active-bg);

$table-hover-color: $table-color;
$table-hover-bg-factor: 0.075;
$table-hover-bg: color-mix(in srgb, var(--emphasis-color) #{math.percentage($table-hover-bg-factor)}, transparent);

$table-caption-color: var(--secondary-color);

$table-bg-scale: -80%;

$table-variants: (
  'primary': shift-color($primary, $table-bg-scale),
  'secondary': shift-color($secondary, $table-bg-scale),
  'success': shift-color($success, $table-bg-scale),
  'info': shift-color($info, $table-bg-scale),
  'warning': shift-color($warning, $table-bg-scale),
  'danger': shift-color($danger, $table-bg-scale),
  'light': $light,
  'dark': $dark,
);

$table-sort-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1'><path d='M5 7l3 -3l3 3'/><path d='M5 10l3 3l3 -3'/></svg>");
$table-sort-asc-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1' d='M5 7l3 3l3 -3'/></svg>");
$table-sort-desc-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1' d='M5 10l3 -3l3 3'/></svg>");
$table-sort-icon-size: 1rem;
$table-sort-icon-margin-start: 0.25rem;
```

_Source: `core/scss/_variables.scss`_
