Skip to main content

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
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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. There are variations available, added via class modifiers which can improve the usability of the ons-table element.

Dense table

<table class="ons-table ons-table--dense ons-table--row-hover">
  <caption class="ons-table__caption">A basic table that compacts if more columns are required</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>
    </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>
    </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 E2
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "table_class": "ons-table--dense ons-table--row-hover",
        "caption": "A basic table that compacts if more columns are required",
        "ths": [
            {
                "value": "Column A"
            },
            {
                "value": "Column B"
            },
            {
                "value": "Column C"
            },
            {
                "value": "Column D"
            },
            {
                "value": "Column E"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Cell A1"
                    },
                    {
                        "value": "Cell B1"
                    },
                    {
                        "value": "Cell C1"
                    },
                    {
                        "value": "Cell D1"
                    },
                    {
                        "value": "Cell E1"
                    }
                ]
            },
            {
                "tds": [
                    {
                    "value": "Cell A2"
                    },
                    {
                    "value": "Cell B2"
                    },
                    {
                    "value": "Cell C2"
                    },
                    {
                    "value": "Cell D2"
                    },
                    {
                    "value": "Cell E2"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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;
        }
      }
    }
  }
}
  • Class - ons-table--dense
  • Output - This reduces the ons-font-size to compact the table for basic tables which have many rows but only a few columns.

Numeric table

<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  table__header--numeric">
        <span>Column A</span>
      </th>
      <th scope="col" class="ons-table__header  table__header--numeric">
        <span>Column B</span>
      </th>
      <th scope="col" class="ons-table__header  table__header--numeric">
        <span>Column C</span>
      </th>
    </tr>
  </thead>
  <tbody class="ons-table__body">
    <tr class="ons-table__row">
      <td class="ons-table__cell table__cell--numeric">
        200
      </td>
      <td class="ons-table__cell table__cell--numeric">
        365
      </td>
      <td class="ons-table__cell table__cell--numeric">
        24
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell table__cell--numeric">
        345
      </td>
      <td class="ons-table__cell table__cell--numeric">
        33
      </td>
      <td class="ons-table__cell  table__cell--numeric">
        13
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "caption": "A basic table with numeric values",
        "ths": [
            {
                "value": "Column A",
                "class": " table__header--numeric"
            },
            {
                "value": "Column B",
                "class": " table__header--numeric"
            },
            {
                "value": "Column C",
                "class": " table__header--numeric"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "200",
                        "class": "table__cell--numeric"
                    },
                    {
                        "value": "365",
                        "class": "table__cell--numeric"
                    },
                    {
                        "value": "24",
                        "class": "table__cell--numeric"
                    }
                ]
            },
            {
                "tds": [
                    {
                    "value": "345",
                    "class": "table__cell--numeric"
                    },
                    {
                    "value": "33",
                    "class": "table__cell--numeric"
                    },
                    {
                    "value": "13",
                    "class": " table__cell--numeric"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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;
        }
      }
    }
  }
}
  • Class - ons-table__header--numeric + ons-table__cell--numeric
  • Output - This aligns the content to the right for tables with all numeric values for best practice.

Responsive table

<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>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 " data-th="Column A">
        Cell A1
      </td>
      <td class="ons-table__cell " data-th="Column B">
        Cell B1
      </td>
      <td class="ons-table__cell " data-th="Column C">
        Cell C1
      </td>
    </tr>
    <tr class="ons-table__row">
      <td class="ons-table__cell " data-th="Column A">
        Cell A2
      </td>
      <td class="ons-table__cell " data-th="Column B">
        Cell B2
      </td>
      <td class="ons-table__cell " data-th="Column C">
        Cell C2
      </td>
    </tr>
  </tbody>
</table>
{% from "components/table/_macro.njk" import onsTable %}
{{
    onsTable({
        "table_class": "ons-table--responsive",
        "caption": "Responsive table with stacked rows for small viewports",
        "ths": [
            {
                "value": "Column A"
            },
            {
                "value": "Column B"
            },
            {
                "value": "Column C"
            }
        ],
        "trs": [
            {
                "tds": [
                    {
                        "value": "Cell A1",
                        "data": "Column A"
                    },
                    {
                        "value": "Cell B1",
                        "data": "Column B"
                    },
                    {
                        "value": "Cell C1",
                        "data": "Column C"
                    }
                ]
            },
            {
                "tds": [
                    {
                        "value": "Cell A2",
                        "data": "Column A"
                    },
                    {
                        "value": "Cell B2",
                        "data": "Column B"
                    },
                    {
                        "value": "Cell C2",
                        "data": "Column C"
                    }
                ]
            }
        ]
    })
}}
Name Type Required Description
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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;
        }
      }
    }
  }
}
  • Class - ons-table--responsive
  • Output - Stacks the rows on viewports that are 500px and lower. Displays the corresponding header value with each cell.

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 ">
      <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({
        "scrollable": true,
        "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
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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;
        }
      }
    }
  }
}
  • Class - ons-table--scrollable ons-table-scrollable--on
  • Output - Creates a scrollable full width table on viewports that are 740px and lower. Optionally allows for the table to be set at 100% on all viewports (shown in the example above).

Sortable table

<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({
        "sortable": true,
        "table_class": "ons-table--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
table_class 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
scrollable boolean false Sets the component to render as a scrollable table
ariaLabel string false The aria label added to the table if it is scrollable. Defaults to Scrollable table
sortable boolean false Sets the component to render as a sortable 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
class 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

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
class 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
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 %}
    {% if params.scrollable is defined and params.scrollable %}
    <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 {{ params.table_class }}" {% if params.sortable is defined and params.sortable %}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.class }}"{% if th.ariaSort is defined and th.ariaSort %} aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}>
                            <span {% if params.sortable is defined and params.sortable %}class="ons-u-vh"{% endif %}>{{- th.value -}}</span>
                            {% if params.sortable is defined and params.sortable %}
                                {{
                                    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.class }}" {% 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 params.scrollable is defined and params.scrollable %}
        </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;
  }

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

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

  &--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;
        }
      }
    }
  }
}

.ons-table-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);
      }
    }
  }
}

.ons-table--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;
      text-decoration: underline;
      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;
        }
      }
    }
  }
}
  • Class - ons-table--sortable
  • Output - Applies JS enhancement to allow each column to be sorted. By default it will sort alphabetically, an optional data-sort-value=foo can be added to each td to control it’s position for sorting.

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