Skip to main content

User testing

Help us improve this service. Take part in a short online exercise

Table

We use the table element to present tabular data in two dimensions.

Basic table

<table id="basic-table" class="ons-table">
  <caption class="ons-table__caption">A basic table with a caption</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header">
        <span>Column A</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column B</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column C</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell" name="cell-name">
        Cell A1
      </td>
      <td class="ons-table__cell">
        Cell B1
      </td>
      <td class="ons-table__cell">
        Cell C1
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Cell A2
      </td>
      <td class="ons-table__cell">
        Cell B2
      </td>
      <td class="ons-table__cell">
        Cell C2
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "caption": "A basic table with a caption",
        "id": "basic-table",
        "ths": [
            {
                "value": "Column A"
            },
            {
                "value": "Column B"
            },
            {
                "value": "Column C"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Cell A1",
                        "name": "cell-name"
                    },
                    {
                        "value": "Cell B1"
                    },
                    {
                        "value": "Cell C1"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Cell A2"
                    },
                    {
                        "value": "Cell B2"
                    },
                    {
                        "value": "Cell C2"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}
<table class="ons-table">
  <caption class="ons-table__caption">A basic table with a footer</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header">
        <span>Column A</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column B</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column C</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Cell A1
      </td>
      <td class="ons-table__cell">
        Cell B1
      </td>
      <td class="ons-table__cell">
        Cell C1
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Cell A2
      </td>
      <td class="ons-table__cell">
        Cell B2
      </td>
      <td class="ons-table__cell">
        Cell C2
      </td>
    </tr>
  </tbody>
  <tfoot class="ons-table__foot">
    <tr class="ons-table__row">
      <td class="ons-table__cell ons-u-fs-s">Column summary</td>
      <td class="ons-table__cell ons-u-fs-s">Column summary</td>
      <td class="ons-table__cell ons-u-fs-s">Column summary</td>
    </tr>
  </tfoot>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "caption": "A basic table with a footer",
        "ths": [
            {
                "value": "Column A"
            },
            {
                "value": "Column B"
            },
            {
                "value": "Column C"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Cell A1"
                    },
                    {
                        "value": "Cell B1"
                    },
                    {
                        "value": "Cell C1"
                    }
                ]
            },
            {
                "tds": [
                    {
                    "value": "Cell A2"
                    },
                    {
                    "value": "Cell B2"
                    },
                    {
                    "value": "Cell C2"
                    }
                ]
            }
        ],
        "tfoot": [
            {
                "value": "Column summary"
            },
            {
                "value": "Column summary"
            },
            {
                "value": "Column summary"
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

When to use this component

Use the table component to let users compare information in rows and columns.

How to use this component

The “basic table” is the default style for a table. The following variants can be set using the variants parameter.

Compact table

Setting "variants": 'compact' will reduce the font size of the table content. This can be used to compact a table with too many columns, to fit into the viewport.

Setting an additional row-hover to the array will highlight a row when hovered, to help users focus on the content in it.

<table class="ons-table ons-table--compact ons-table--row-hover">
  <caption class="ons-table__caption">A compacted table with a large number of columns</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header">
        <span>Column A</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column B</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column C</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column D</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column E</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Column F</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Cell A1
      </td>
      <td class="ons-table__cell">
        Cell B1
      </td>
      <td class="ons-table__cell">
        Cell C1
      </td>
      <td class="ons-table__cell">
        Cell D1
      </td>
      <td class="ons-table__cell">
        Cell E1
      </td>
      <td class="ons-table__cell">
        Cell F1
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Cell A2
      </td>
      <td class="ons-table__cell">
        Cell B2
      </td>
      <td class="ons-table__cell">
        Cell C2
      </td>
      <td class="ons-table__cell">
        Cell D2
      </td>
      <td class="ons-table__cell">
        Cell E1
      </td>
      <td class="ons-table__cell">
        Cell F1
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "variants": ['compact', 'row-hover'],
        "caption": "A compacted table with a large number of columns",
        "ths": [
            {
                "value": "Column A"
            },
            {
                "value": "Column B"
            },
            {
                "value": "Column C"
            },
            {
                "value": "Column D"
            },
            {
                "value": "Column E"
            },
            {
                "value": "Column F"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Cell A1"
                    },
                    {
                        "value": "Cell B1"
                    },
                    {
                        "value": "Cell C1"
                    },
                    {
                        "value": "Cell D1"
                    },
                    {
                        "value": "Cell E1"
                    },
                    {
                        "value": "Cell F1"
                    }
                ]
            },
            {
                "tds": [
                    {
                    "value": "Cell A2"
                    },
                    {
                    "value": "Cell B2"
                    },
                    {
                    "value": "Cell C2"
                    },
                    {
                    "value": "Cell D2"
                    },
                    {
                        "value": "Cell E1"
                    },
                    {
                        "value": "Cell F1"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

Numeric table

Setting "numeric": true to any table cell will right-align it’s figures to make them easier to compare with other rows.

<table class="ons-table">
  <caption class="ons-table__caption">A basic table with numeric values</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header">
        <span>Country</span>
      </th>
      <th scope="col" class="ons-table__header ons-table__header--numeric">
        <span>Population mid-2020</span>
      </th>
      <th scope="col" class="ons-table__header ons-table__header--numeric">
        <span>% change 2019 to 2020</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        England
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        67,081,000
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        0.43
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Northern Ireland
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        1,896,000
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        0.10
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Scotland
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        5,466,000
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        0.05
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        Wales
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        3,170,000
      </td>
      <td class="ons-table__cell ons-table__cell--numeric">
        0.53
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "caption": "A basic table with numeric values",
        "ths": [
            {
                "value": "Country"
            },
            {
                "value": "Population mid-2020",
                "numeric": true
            },
            {
                "value": "% change 2019 to 2020",
                "numeric": true
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "England"
                    },
                    {
                        "value": "67,081,000",
                        "numeric": true
                    },
                    {
                        "value": "0.43",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Northern Ireland"
                    },
                    {
                        "value": "1,896,000",
                        "numeric": true
                    },
                    {
                        "value": "0.10",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Scotland"
                    },
                    {
                        "value": "5,466,000",
                        "numeric": true
                    },
                    {
                        "value": "0.05",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Wales"
                    },
                    {
                        "value": "3,170,000",
                        "numeric": true
                    },
                    {
                        "value": "0.53",
                        "numeric": true
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

Responsive table

Setting "variants": 'responsive' will stack the cells of each table row vertically when viewing on devices less than 500px wide. The associated table header will display repeatedly alongside each cell.

<table class="ons-table ons-table--responsive">
  <caption class="ons-table__caption">Responsive table with stacked rows for small viewports</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header">
        <span>Country</span>
      </th>
      <th scope="col" class="ons-table__header">
        <span>Highest mountain</span>
      </th>
      <th scope="col" class="ons-table__header ons-table__header--numeric">
        <span>Height in metres</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell" data-th="Country">
        Scotland
      </td>
      <td class="ons-table__cell" data-th="Highest mountain">
        Ben Nevis
      </td>
      <td class="ons-table__cell ons-table__cell--numeric" data-th="Height">
        1,345
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell" data-th="Country">
        Wales
      </td>
      <td class="ons-table__cell" data-th="Highest mountain">
        Snowdon
      </td>
      <td class="ons-table__cell ons-table__cell--numeric" data-th="Height">
        1,085
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell" data-th="Country">
        England
      </td>
      <td class="ons-table__cell" data-th="Highest mountain">
        Scafell Pike
      </td>
      <td class="ons-table__cell ons-table__cell--numeric" data-th="Height">
        978
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell" data-th="Country">
        Northern Ireland
      </td>
      <td class="ons-table__cell" data-th="Highest mountain">
        Slieve Donard
      </td>
      <td class="ons-table__cell ons-table__cell--numeric" data-th="Height">
        850
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "variants": 'responsive',
        "caption": "Responsive table with stacked rows for small viewports",
        "ths": [
            {
                "value": "Country"
            },
            {
                "value": "Highest mountain"
            },
            {
                "value": "Height in metres",
                "numeric": true
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Scotland",
                        "data": "Country"
                    },
                    {
                        "value": "Ben Nevis",
                        "data": "Highest mountain"
                    },
                    {
                        "value": "1,345",
                        "data": "Height",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Wales",
                        "data": "Country"
                    },
                    {
                        "value": "Snowdon",
                        "data": "Highest mountain"
                    },
                    {
                        "value": "1,085",
                        "data": "Height",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "England",
                        "data": "Country"
                    },
                    {
                        "value": "Scafell Pike",
                        "data": "Highest mountain"
                    },
                    {
                        "value": "978",
                        "data": "Height",
                        "numeric": true
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Northern Ireland",
                        "data": "Country"
                    },
                    {
                        "value": "Slieve Donard",
                        "data": "Highest mountain"
                    },
                    {
                        "value": "850",
                        "data": "Height",
                        "numeric": true
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

Scrollable table

Setting "variants": 'scrollable' will create a full-width, scrollable table.

<div class="ons-table-scrollable ons-table-scrollable--on">
  <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="Scrollable table. There are 7 columns in this table. Some of the table may be off screen. Scroll or drag horizontally to bring into view. ">
    <table class="ons-table ons-table--scrollable">
      <caption class="ons-table__caption">Scrollable table</caption>
      <thead class="ons-table__head">
        <tr class="ons-table__row">
          <th scope="col" class="ons-table__header">
            <span>ID</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Title</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Abbreviation</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Legal basis</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Frequency</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Date</span>
          </th>
          <th scope="col" class="ons-table__header">
            <span>Status</span>
          </th>
        </tr>
      </thead>
      <tbody class="ons-table__body">
        <tr class="ons-table__row">
          <td class="ons-table__cell">
            023
          </td>
          <td class="ons-table__cell">
            Monthly Business Survey - Retail Sales Index
          </td>
          <td class="ons-table__cell">
            RSI
          </td>
          <td class="ons-table__cell">
            Statistics of Trade Act 1947
          </td>
          <td class="ons-table__cell">
            Monthly
          </td>
          <td class="ons-table__cell">
            20 Jan 2018
          </td>
          <td class="ons-table__cell">
            <span class='ons-status ons-status--success'>Ready</span>
          </td>
        </tr>
        <tr class="ons-table__row">
          <td class="ons-table__cell">
            112
          </td>
          <td class="ons-table__cell">
            Annual Inward Foreign Direct Investment Survey
          </td>
          <td class="ons-table__cell">
            AIFDI
          </td>
          <td class="ons-table__cell">
            Statistics of Trade Act 1947
          </td>
          <td class="ons-table__cell">
            Annually
          </td>
          <td class="ons-table__cell">
            26 Feb 2018
          </td>
          <td class="ons-table__cell">
            <span class='ons-status ons-status--dead'>Not ready</span>
          </td>
        </tr>
        <tr class="ons-table__row">
          <td class="ons-table__cell">
            332
          </td>
          <td class="ons-table__cell">
            Business Register and Employment Survey
          </td>
          <td class="ons-table__cell">
            BRES
          </td>
          <td class="ons-table__cell">
            Statistics of Trade Act 1947
          </td>
          <td class="ons-table__cell">
            Annually
          </td>
          <td class="ons-table__cell">
            23 Jan 2013
          </td>
          <td class="ons-table__cell">
            <span class='ons-status ons-status--info'>In progress</span>
          </td>
        </tr>
        <tr class="ons-table__row">
          <td class="ons-table__cell">
            654
          </td>
          <td class="ons-table__cell">
            Quartely Survey of Building Materials Sand and Gravel
          </td>
          <td class="ons-table__cell">
            QBMS
          </td>
          <td class="ons-table__cell">
            Statistics of Trade Act 1947 - BEIS
          </td>
          <td class="ons-table__cell">
            Quartely
          </td>
          <td class="ons-table__cell">
            24 Jan 2015
          </td>
          <td class="ons-table__cell">
            <span class='ons-status ons-status--error'>Issue</span>
          </td>
        </tr>
        <tr class="ons-table__row">
          <td class="ons-table__cell">
            765
          </td>
          <td class="ons-table__cell">
            Monthly Survey of Building Materials Concrete Building Blocks
          </td>
          <td class="ons-table__cell">
            MSBB
          </td>
          <td class="ons-table__cell">
            Voluntary
          </td>
          <td class="ons-table__cell">
            Monthly
          </td>
          <td class="ons-table__cell">
            25 Jan 2014
          </td>
          <td class="ons-table__cell">
            <span class='ons-status ons-status--success'>Ready</span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "variants": 'scrollable',
        "caption": "Scrollable table",
        "ariaLabel": "There are 7 columns in this table. Some of the table may be off screen. Scroll or drag horizontally to bring into view.",
        "ths": [
            {
                "value": "ID"
            },
            {
                "value": "Title"
            },
            {
                "value": "Abbreviation"
            },
            {
                "value": "Legal basis"
            },
            {
                "value": "Frequency"
            },
            {
                "value": "Date"
            },
            {
                "value": "Status"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "023"
                    },
                    {
                        "value": "Monthly Business Survey - Retail Sales Index"
                    },
                    {
                        "value": "RSI"
                    },
                    {
                        "value": "Statistics of Trade Act 1947"
                    },
                    {
                        "value": "Monthly"
                    },
                    {
                        "value": "20 Jan 2018"
                    },
                    {
                        "value": "<span class='ons-status ons-status--success'>Ready</span>"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "112"
                    },
                    {
                        "value": "Annual Inward Foreign Direct Investment Survey"
                    },
                    {
                        "value": "AIFDI"
                    },
                    {
                        "value": "Statistics of Trade Act 1947"
                    },
                    {
                        "value": "Annually"
                    },
                    {
                        "value": "26 Feb 2018"
                    },
                    {
                        "value": "<span class='ons-status ons-status--dead'>Not ready</span>"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "332"
                    },
                    {
                        "value": "Business Register and Employment Survey"
                    },
                    {
                        "value": "BRES"
                    },
                    {
                        "value": "Statistics of Trade Act 1947"
                    },
                    {
                        "value": "Annually"
                    },
                    {
                        "value": "23 Jan 2013"
                    },
                    {
                        "value": "<span class='ons-status ons-status--info'>In progress</span>"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "654"
                    },
                    {
                        "value": "Quartely Survey of Building Materials Sand and Gravel"
                    },
                    {
                        "value": "QBMS"
                    },
                    {
                        "value": "Statistics of Trade Act 1947 - BEIS"
                    },
                    {
                        "value": "Quartely"
                    },
                    {
                        "value": "24 Jan 2015"
                    },
                    {
                        "value": "<span class='ons-status ons-status--error'>Issue</span>"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "765"
                    },
                    {
                        "value": "Monthly Survey of Building Materials Concrete Building Blocks"
                    },
                    {
                        "value": "MSBB"
                    },
                    {
                        "value": "Voluntary"
                    },
                    {
                        "value": "Monthly"
                    },
                    {
                        "value": "25 Jan 2014"
                    },
                    {
                        "value": "<span class='ons-status ons-status--success'>Ready</span>"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

Sortable table

Setting "variants": 'sortable' will apply a JavaScript enhancement to allow each column to be sorted. By default, the column will be sorted alphabetically, but an optional parameter "dataSort": '<number>' can be added to each table cell to control it’s position for sorting.

<table class="ons-table ons-table--sortable" data-aria-sort="Sort by" data-aria-asc="ascending" data-aria-desc="descending">
  <caption class="ons-table__caption">Javascript enhanced sortable table</caption>
  <thead class="ons-table__head">
    <tr class="ons-table__row">
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">ID</span>
        <svg id="sort-sprite-id" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Title</span>
        <svg id="sort-sprite-title" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Abbreviation</span>
        <svg id="sort-sprite-abbreviation" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Legal basis</span>
        <svg id="sort-sprite-legal basis" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Frequency</span>
        <svg id="sort-sprite-frequency" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Date</span>
        <svg id="sort-sprite-date" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
      <th scope="col" class="ons-table__header" aria-sort="none">
        <span class="ons-u-vh">Status</span>
        <svg id="sort-sprite-status" class="ons-svg-icon " viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
          <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
          <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
        </svg>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        023
      </td>
      <td class="ons-table__cell">
        Monthly Business Survey - Retail Sales Index
      </td>
      <td class="ons-table__cell">
        RSI
      </td>
      <td class="ons-table__cell">
        Statistics of Trade Act 1947
      </td>
      <td class="ons-table__cell" data-sort-value="1">
        Monthly
      </td>
      <td class="ons-table__cell" data-sort-value="2018-01-20">
        20 Jan 2018
      </td>
      <td class="ons-table__cell" data-sort-value="0">
        <span class='ons-status ons-status--success'>Ready</span>
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        112
      </td>
      <td class="ons-table__cell">
        Annual Inward Foreign Direct Investment Survey
      </td>
      <td class="ons-table__cell">
        AIFDI
      </td>
      <td class="ons-table__cell">
        Statistics of Trade Act 1947
      </td>
      <td class="ons-table__cell" data-sort-value="12">
        Annually
      </td>
      <td class="ons-table__cell" data-sort-value="2018-02-26">
        26 Feb 2018
      </td>
      <td class="ons-table__cell" data-sort-value="1">
        <span class='ons-status ons-status--dead'>Not ready</span>
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        332
      </td>
      <td class="ons-table__cell">
        Business Register and Employment Survey
      </td>
      <td class="ons-table__cell">
        BRES
      </td>
      <td class="ons-table__cell">
        Statistics of Trade Act 1947
      </td>
      <td class="ons-table__cell" data-sort-value="12">
        Annually
      </td>
      <td class="ons-table__cell" data-sort-value="2013-01-23">
        23 Jan 2013
      </td>
      <td class="ons-table__cell" data-sort-value="2">
        <span class='ons-status ons-status--info'>In progress</span>
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        654
      </td>
      <td class="ons-table__cell">
        Quartely Survey of Building Materials Sand and Gravel
      </td>
      <td class="ons-table__cell">
        QBMS
      </td>
      <td class="ons-table__cell">
        Statistics of Trade Act 1947 - BEIS
      </td>
      <td class="ons-table__cell" data-sort-value="3">
        Quartely
      </td>
      <td class="ons-table__cell" data-sort-value="2015-01-24">
        24 Jan 2015
      </td>
      <td class="ons-table__cell" data-sort-value="3">
        <span class='ons-status ons-status--error'>Issue</span>
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell">
        765
      </td>
      <td class="ons-table__cell">
        Monthly Survey of Building Materials Concrete Building Blocks
      </td>
      <td class="ons-table__cell">
        MSBB
      </td>
      <td class="ons-table__cell">
        Voluntary
      </td>
      <td class="ons-table__cell" data-sort-value="1">
        Monthly
      </td>
      <td class="ons-table__cell" data-sort-value="2014-01-25">
        25 Jan 2014
      </td>
      <td class="ons-table__cell" data-sort-value="0">
        <span class='ons-status ons-status--success'>Ready</span>
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}

{{
    onsTable({
        "variants": 'sortable',
        "caption": "Javascript enhanced sortable table",
        "sortBy": "Sort by",
        "ariaAsc": "ascending",
        "ariaDesc": "descending",
        "ths": [
                {
                    "value": "ID",
                    "ariaSort": "none"
                },
                {
                    "value": "Title",
                    "ariaSort": "none"
                },
                {
                    "value": "Abbreviation",
                    "ariaSort": "none"
                },
                {
                    "value": "Legal basis",
                    "ariaSort": "none"
                },
                {
                    "value": "Frequency",
                    "ariaSort": "none"
                },
                {
                    "value": "Date",
                    "ariaSort": "none"
                },
                {
                    "value": "Status",
                    "ariaSort": "none"
                }
            ],
            "trs": [
                {
                    "tds": [
                        {
                            "value": "023"
                        },
                        {
                            "value": "Monthly Business Survey - Retail Sales Index"
                        },
                        {
                            "value": "RSI"
                        },
                        {
                            "value": "Statistics of Trade Act 1947"
                        },
                        {
                            "value": "Monthly",
                            "dataSort": "1"
                        },
                        {
                            "value": "20 Jan 2018",
                            "dataSort": "2018-01-20"
                        },
                        {
                            "value": "<span class='ons-status ons-status--success'>Ready</span>",
                            "dataSort": "0"
                        }
                    ]
                },
                {
                    "tds": [
                        {
                            "value": "112"
                        },
                        {
                            "value": "Annual Inward Foreign Direct Investment Survey"
                        },
                        {
                            "value": "AIFDI"
                        },
                        {
                            "value": "Statistics of Trade Act 1947"
                        },
                        {
                            "value": "Annually",
                            "dataSort": "12"
                        },
                        {
                            "value": "26 Feb 2018",
                            "dataSort": "2018-02-26"
                        },
                        {
                            "value": "<span class='ons-status ons-status--dead'>Not ready</span>",
                            "dataSort": "1"
                        }
                    ]
                },
                {
                    "tds": [
                        {
                            "value": "332"
                        },
                        {
                            "value": "Business Register and Employment Survey"
                        },
                        {
                            "value": "BRES"
                        },
                        {
                            "value": "Statistics of Trade Act 1947"
                        },
                        {
                            "value": "Annually",
                            "dataSort": "12"
                        },
                        {
                            "value": "23 Jan 2013",
                            "dataSort": "2013-01-23"
                        },
                        {
                            "value": "<span class='ons-status ons-status--info'>In progress</span>",
                            "dataSort": "2"
                        }
                    ]
                },
                {
                    "tds": [
                        {
                            "value": "654"
                        },
                        {
                            "value": "Quartely Survey of Building Materials Sand and Gravel"
                        },
                        {
                            "value": "QBMS"
                        },
                        {
                            "value": "Statistics of Trade Act 1947 - BEIS"
                        },
                        {
                            "value": "Quartely",
                            "dataSort": "3"
                        },
                        {
                            "value": "24 Jan 2015",
                            "dataSort": "2015-01-24"
                        },
                        {
                            "value": "<span class='ons-status ons-status--error'>Issue</span>",
                            "dataSort": "3"
                        }
                    ]
                },
                {
                    "tds": [
                        {
                            "value": "765"
                        },
                        {
                            "value": "Monthly Survey of Building Materials Concrete Building Blocks"
                        },
                        {
                            "value": "MSBB"
                        },
                        {
                            "value": "Voluntary"
                        },
                        {
                            "value": "Monthly",
                            "dataSort": "1"
                        },
                        {
                            "value": "25 Jan 2014",
                            "dataSort": "2014-01-25"
                        },
                        {
                            "value": "<span class='ons-status ons-status--success'>Ready</span>",
                            "dataSort": "0"
                        }
                    ]
                }
            ]
    })
}}
Name Type Required Description
variants array or string false An array of values or single value (string) to adjust the table using available variants: compact, responsive,scrollable, sortable, and row-hover
tableClasses string false Classes to add to the table component
id string false ID to add to the table component
caption string false The caption for the table component
hideCaption boolean false Visually hides the caption
ariaLabel string false The ARIA label to be added if scrollable variant set, to inform screen reader users that the table can be scrolled. Defaults to "Scrollable table"
ths Array<th> true An array of th elements for table
trs Array<tr> true An array of tr elements for table
tfoot Array<tfootCell> false An array of td elements for tdfoot
ariaAsc string false Sets the data-aria-asc attribute for the table. Used to set aria labels when table is sorted
ariaDesc string false Sets the data-aria-desc attribute for the table. Used to set aria labels when table is sorted

th

Name Type Required Description
thClasses string false Classes to add to the th element
ariaSort string false Default is “none”. Accepts “ascending” or “descending”
value string true The content for the th cell
numeric boolean false Aligns the cell content to the right when set to true

tr

Name Type Required Description
tds Array<td> true An array of td elements for each tr
highlight boolean false Adds a class to the table row to highlight the row

td

Name Type Required Description
tdClasses string false Classes to add to the td element
name string false Name to add to the td element
data string false The corresponding th for the td for responsive tables
dataSort integer false numerical ordering of a column of td elements for sortable table
value string false The content for the td cell
numeric boolean false Aligns the cell content to the right when set to true
form object false Form attributes information for method, action and the button

form

Name Type Required Description
method string false Default is post if no value is provided
action string true The action for the form
button Button (ref) false Configuration object for the form button
hiddenFormField object false Configuration object for hidden form fields

hiddenFormField

Name Type Required Description
name string false Hidden field name
value string false Hidden field value

tfootCell

Name Type Required Description
value string true The content for the td cell of tdfoot
{% macro onsTable(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    {% from "components/icons/_macro.njk" import onsIcon %}

    {% set variants = params.variants if params.variants else '' %}

    {% if 'scrollable' in variants %}
    <div class="ons-table-scrollable ons-table-scrollable--on">
        <div class="ons-table-scrollable__content" tabindex="0" role="region" aria-label="{{ params.caption }}. {{ params.ariaLabel | default("Scrollable table") }} ">
    {% endif %}
            <table {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-table{% if params.tableClasses is defined and params.tableClasses %} {{ params.tableClasses }}{% endif %}{% if variants is defined and variants %}{% if variants is not string %}{% for variant in variants %} ons-table--{{ variant }}{% endfor %}{% else %} ons-table--{{ variants }}{% endif %}{% endif %}" {% if params.sortBy is defined and params.sortBy and 'sortable' in variants %}data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"{% endif %}>
                {% if params.caption is defined and params.caption %}
                <caption class="ons-table__caption{{ " ons-u-vh" if params.hideCaption }}">{{ params.caption }}</caption>
                {% endif %}
                <thead class="ons-table__head">
                    <tr class="ons-table__row">
                        {% for th in params.ths %}
                        <th scope="col" class="ons-table__header{{ ' ' + th.thClasses if th.thClasses is defined and th.thClasses }}{{ " ons-table__header--numeric" if th.numeric is defined and th.numeric }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if 'sortable' in variants %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if 'sortable' in variants %}
                                {{
                                    onsIcon({
                                        "iconType": "sort-sprite",
                                        "id": th.value
                                    })
                                }}
                            {% endif %}
                        </th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody class="ons-table__body">
                    {% for tr in params.trs %}
                    <tr class="ons-table__row{{ " ons-table__row--highlight" if tr.highlight }}" {% if tr.name is defined and tr.name %} name="{{ tr.name }}"{% endif %} {% if tr.id is defined and tr.id %} id="{{ tr.id }}"{% endif %}>
                        {% for td in tr.tds %}
                        <td class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses is defined and td.tdClasses }}{{ " ons-table__cell--numeric" if td.numeric is defined and td.numeric }}" {% if td.id is defined and td.id %} id="{{ td.id }}"{% endif %} {% if td.name is defined and td.name %} name="{{ td.name }}"{% endif %} {% if td.data is defined and td.data %} data-th="{{ td.data }}"{% endif %} {% if td.dataSort is defined and td.dataSort %} data-sort-value="{{ td.dataSort }}"{% endif %}>
                            {% if td.form is defined and td.form %}
                                <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST')}}">
                                    {{
                                        onsButton({
                                            "text": td.form.button.text,
                                            "id": td.form.button.id if td.form.button.id,
                                            "classes": td.form.button.classes if td.form.button.classes,
                                            "url": td.form.button.url if td.form.button.url,
                                            "value": td.form.button.value | safe if td.form.button.value,
                                            "name": td.form.button.name if td.form.button.name
                                        })
                                    }}
                                    {% if td.form.hiddenFormField is defined and td.form.hiddenFormField %}
                                        {% for hiddenField in td.form.hiddenFormField %}
                                            <input type="hidden" {% if hiddenField.name is defined and hiddenField.name %} name="{{ hiddenField.name }}"{% endif %} {% if hiddenField.value is defined and hiddenField.value %} value="{{ hiddenField.value }}"{% endif %} />
                                        {% endfor %}
                                    {% endif %}
                                </form>
                            {% endif %}
                            {% if td.value is defined and td.value %}
                                {{ td.value | safe }}
                            {% endif %}
                        </td>
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
                {% if params.tfoot is defined and params.tfoot %}
                <tfoot class="ons-table__foot">
                    <tr class="ons-table__row">
                        {% for tfootCell in params.tfoot %}
                        <td class="ons-table__cell ons-u-fs-s">{{ tfootCell.value }}</td>
                        {% endfor %}
                    </tr>
                </tfoot>
                {% endif %}
            </table>
        {% if 'scrollable' in variants %}
        </div>
    </div>
    {% endif %}
{% endmacro %}
.ons-table {
  border-collapse: collapse;
  border-spacing: 0;
  margin-bottom: 1rem;
  width: 100%;

  &__caption {
    font-weight: 700;
    text-align: left;
  }

  &__header,
  &__cell {
    @include nth-element(1, 0);

    border-bottom: 2px solid $color-grey-100;
    overflow: hidden;
    padding: 0.5rem 0 0.5rem 1rem;
    text-align: left;
    vertical-align: top;
    &--numeric {
      text-align: right;
    }
  }

  &__cell,
  &__header--row {
    border-bottom: 1px solid $color-borders;
  }

  &__row--highlight {
    background: $color-highlight;
  }

  &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
    .ons-table__cell,
    .ons-table__header--row {
      border: 0;
    }
  }

  &__foot .ons-table__cell {
    border-bottom: 0;
    border-top: 1px solid $color-borders;
  }

  &--compact {
    .ons-table__head,
    .ons-table__body,
    .ons-table__foot {
      font-size: 81.25%;
    }
  }

  &--row-hover {
    .ons-table__body .ons-table__row:hover {
      background: $color-highlight;
    }
  }

  &--responsive {
    @include mq(xxs, s) {
      .ons-table__header {
        display: none;
      }

      .ons-table__body .ons-table__row {
        border-bottom: 2px solid $color-grey-100;
        display: block;
        margin-bottom: 1rem;
      }

      .ons-table__cell {
        display: block;
        padding-left: 0;
        text-align: right;
        &:last-child {
          border: 0;
        }
        &::before {
          content: attr(data-th);
          float: left;
          font-weight: 700;
          padding-right: 1rem;
        }
      }
    }
  }

  &-scrollable {
    position: relative;
    ::-webkit-scrollbar {
      height: 7px;
    }
    ::-webkit-scrollbar-thumb {
      background: $color-grey-75;
      border-radius: 20px;
    }
    &--on {
      .ons-table__header,
      .ons-table__cell {
        white-space: nowrap;
      }
    }
    &__content {
      overflow: visible;
      overflow-x: scroll;
      width: 100%;
      &:focus {
        outline: 3px solid $color-focus;
        outline-offset: 3px;
      }
      .ons-table__header,
      .ons-table__cell {
        @include mq(xxs, m) {
          white-space: nowrap;
        }
      }
      .ons-table__right-shadow,
      .ons-table__left-shadow {
        height: 100%;
        position: absolute;
        top: 0;
        width: 5px;
        z-index: 200;

        &.ons-with-transition {
          transition: box-shadow 0.4s ease-out;
        }
      }
      .ons-table__right-shadow {
        right: 0;
        &.ons-visible {
          box-shadow: inset -1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
      .ons-table__left-shadow {
        left: 0;
        &.ons-visible {
          box-shadow: inset 1px 0 0 0 #bfc1c3, inset -5px 0 0 0 rgba(191, 193, 195, 0.4);
        }
      }
    }
  }

  &--sortable {
    [aria-sort='descending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-grey-35;
        }
        .ons-bottomTriangle {
          fill: $color-text;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-topTriangle {
            fill: #b69502;
          }
        }
      }
    }

    [aria-sort='ascending'].ons-table__header {
      .ons-svg-icon {
        .ons-topTriangle {
          fill: $color-text;
        }
        .ons-bottomTriangle {
          fill: $color-grey-35;
        }
      }
      .ons-table__sort-button:focus {
        .ons-svg-icon {
          .ons-bottomTriangle {
            fill: #b69502;
          }
        }
      }
    }

    .ons-table__header {
      position: relative;

      .ons-table__sort-button {
        background-color: transparent;
        border: 0;
        box-shadow: none;
        color: $color-text-link;
        display: inline-block;
        font-family: $font-sans;
        font-weight: 700;
        line-height: 1rem;
        padding: 0 0 0.2rem;
        text-decoration: underline;
        text-underline-position: under;
        white-space: nowrap;

        &:hover {
          color: $color-text-link-hover;
          cursor: pointer;
          text-decoration: underline solid $color-text-link-hover 2px;
        }

        .ons-svg-icon {
          fill: $color-grey-35;
          height: 0.8rem;
          padding-bottom: 0.1rem;
          width: 0.8rem;
        }

        &:focus {
          @extend %a-focus;
          .ons-svg-icon {
            fill: $color-black;
          }
        }
      }
    }
  }
}

Help improve this component

Let us know how we could improve this component or share your user research findings. Discuss the ‘Table’ component on GitHub