Documentation and examples for opt-in styling of tables with OUDS Web.

Watch out!

Draft component

The table component is not yet designed, and this component is only a draft for the moment. It will be updated in the future with the final design and will certainly need adjustments regarding the DOM. Use it as a temporary component to speed up your development.

Please refer to the Tables in the Boosted documentation to have a full view of the table component and its variations.

Overview

Due to the extensive use of <table> elements in third-party widgets such as calendars and date pickers, OUDS Web’s tables are opt-in. This means that you must add the base class .table to any <table>, to benefit from OUDS Web's table styles which you can then extend with our optional modifier classes or custom styles. In OUDS Web, table styles are not inherited, enabling independent styling for nested tables.

Using the most basic table markup, here’s how .table-based tables look in OUDS Web.

OUDS Web basic table
# Heading Heading
1 Cell Cell
2 Cell Cell
3 Cell Cell
<table class="table">
  <caption class="visually-hidden">OUDS Web basic table</caption>
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">1</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">2</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">3</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
html

Accessibility

To make a table accessible, you should respect these main rules:

  • Add a scope="col", scope="row", scope="colgroup", or scope="rowgroup" attribute to the <th> tags when needed to make the content of table readable by screen readers.
  • Add a <caption> on each table. If the table doesn’t have a caption or if the caption is not enough informative to describe the table, add an aria-label attribute to describe the table content. The aria-label should match the following pattern: aria-label="Description of table data - Description of table metadata (e.g.: table with one level of column header)". The metadata is mandatory for complex tables.

See more information about the tables structures.

Variants

Functional colors

To add a background color to a table row, you can use the contextual classes .table-{status} on <tr> elements. These classes will apply a muted background color based on the status you choose, among info, positive, warning, or negative. This can be useful for highlighting specific rows based on their content or status. Another way to convey information must be used in addition to color, such as a badge with a relevant label or icon, to ensure that the information is accessible to all users.

OUDS Web table with functional colored backgrounds
# Status Heading
1 Running Cell
2 New Cell
3 Reloading Cell
4 Error Cell
<table class="table">
  <caption class="visually-hidden">OUDS Web table with functional colored backgrounds</caption>
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Status</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr class="table-info">
      <th scope="row">1</th>
      <td><span class="badge badge-info me-xsmall"></span>Running</td>
      <td>Cell</td>
    </tr>
    <tr class="table-positive">
      <th scope="row">2</th>
      <td><span class="badge badge-positive me-xsmall"></span>New</td>
      <td>Cell</td>
    </tr>
    <tr class="table-warning">
      <th scope="row">3</th>
      <td><span class="badge badge-warning me-xsmall"></span>Reloading</td>
      <td>Cell</td>
    </tr>
    <tr class="table-negative">
      <th scope="row">4</th>
      <td><span class="badge badge-negative me-xsmall"></span>Error</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
html

Striped rows

Use .table-striped to add zebra-striping to any table row within the <tbody>. This will add a background color to even rows (excluding the header) to improve readability.

OUDS Web zebra table
# Heading Heading
1 Cell Cell
2 Cell Cell
3 Cell Cell
4 Cell Cell
<table class="table table-striped">
  <caption class="visually-hidden">OUDS Web zebra table</caption>
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">1</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">2</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">3</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <th scope="row">4</th>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
html

Responsive tables

Responsive tables allow tables to be scrolled horizontally with ease. Make any table responsive across all viewports by wrapping a .table with .table-responsive. Or, pick a maximum breakpoint with which to have a responsive table up to by using .table-responsive{-xs|-sm|-md|-lg|-xl|-2xl|3xl}.

Heads up!

Vertical clipping/truncation

Responsive tables make use of overflow-y: hidden, which clips off any content that goes beyond the bottom or top edges of the table. In particular, this can clip off dropdown menus and other third-party widgets.

Always responsive

Across every breakpoint, use .table-responsive for horizontally scrolling tables.

OUDS Web responsive table
# Heading Heading Heading Heading Heading Heading Heading Heading Heading
1 Cell Cell Cell Cell Cell Cell Cell Cell Cell
2 Cell Cell Cell Cell Cell Cell Cell Cell Cell
3 Cell Cell Cell Cell Cell Cell Cell Cell Cell

<div class="table-responsive">
  <table class="table">
    ...
  </table>
</div>

Breakpoint specific

Use .table-responsive{-xs|-sm|-md|-lg|-xl|-2xl|3xl} as needed to create responsive tables up to a particular breakpoint. From that breakpoint and up, the table will behave normally and not scroll horizontally.

These tables may appear broken until their responsive styles apply at specific viewport widths.

Examples of responsive tables up to particular breakpoint

Resize window to see differences:

OUDS Web responsive table for s breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for m breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for d breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for g breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for l breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for xl breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
OUDS Web responsive table for xl breakpoint and under
#HeadingHeadingHeadingHeadingHeadingHeadingHeadingHeading
1CellCellCellCellCellCellCellCell
2CellCellCellCellCellCellCellCell
3CellCellCellCellCellCellCellCell
<div class="table-responsive">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsivexs">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsivesm">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsivemd">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsivelg">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsivexl">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsive2xl">
  <table class="table">
  ...
  </table>
</div>
<div class="table-responsive3xl">
  <table class="table">
  ...
  </table>
</div>

Row selection

With checkboxes

For the row selection purpose with checkboxes, you should use standalone checkboxes with .visually-hidden labels describing their use.

The first cell of the header should contain a checkbox to select or deselect all rows. When some rows - but not all - are selected, the header checkbox should be in an indeterminate state.

To visually indicate a row is selected, you'll need to apply a data-bs-theme="dark" attribute on the <tr> element (some additional styles will be applied too). This can be done by JavaScript, see the example below. If you want to manually apply the same styles to rows or even cells, you can use the class .table-active with data-bs-theme="dark" on <tr> or <td> elements.

Note that the width of columns containing checkboxes will automatically adjust to the largest cell, and the click area will be extended to the full width of the cell.

OUDS Web table with row selection
Heading Heading Heading
Cell Cell Cell
This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table Cell Cell
Cell Cell Cell
<table class="table" id="tableWithCheckboxes">
  <caption class="visually-hidden">OUDS Web table with row selection</caption>
  <thead>
    <tr>
      <th scope="col">
        <label class="checkbox-standalone">
          <input class="control-item-indicator" type="checkbox" value="all" id="tableSelectAll" />
          <span class="visually-hidden">Select all rows</span>
        </label>
      </th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr data-bs-theme="dark">
      <td>
        <label class="checkbox-standalone">
          <input class="control-item-indicator" type="checkbox" value="1" checked />
          <span class="visually-hidden">Select row 1</span>
        </label>
      </td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <td>
        <label class="checkbox-standalone">
          <input class="control-item-indicator" type="checkbox" value="2" />
          <span class="visually-hidden">Select row 2</span>
        </label>
      </td>
      <td>This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr data-bs-theme="dark">
      <td>
        <label class="checkbox-standalone">
          <input class="control-item-indicator" type="checkbox" value="3" checked />
          <span class="visually-hidden">Select row 3</span>
        </label>
      </td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
html

To manage checkboxes states and active styles (i.e. select/deselect all rows, update header checkbox state, and update data-bs-theme on selected row), here is an example of JavaScript code:

// Manage checkboxes states: select/deselect all rows, update header checkbox state, update data-bs-theme on selected rows
const tableSelectAll = document.querySelector('#tableWithCheckboxes #tableSelectAll')
const allCheckboxes = document.querySelectorAll('#tableWithCheckboxes tbody input[type="checkbox"]')

function updateSelectAllState() {
  const checkedCheckboxes = document.querySelectorAll('#tableWithCheckboxes tbody input[type="checkbox"]:checked')

  if (checkedCheckboxes.length === 0) {
    // None are checked
    tableSelectAll.checked = false
    tableSelectAll.indeterminate = false
  } else if (checkedCheckboxes.length === allCheckboxes.length) {
    // All are checked
    tableSelectAll.checked = true
    tableSelectAll.indeterminate = false
  } else {
    // Some are checked
    tableSelectAll.checked = false
    tableSelectAll.indeterminate = true
  }
}

function updateSelectedRows() {
  const selectedRows = document.querySelectorAll('#tableWithCheckboxes tbody tr:has(input[type="checkbox"]:checked)')

  if (selectedRows.length >= 1) {
    selectedRows.forEach(row => row.setAttribute('data-bs-theme', 'dark'))
  }

  const unselectedRows = document.querySelectorAll('#tableWithCheckboxes tbody tr:has(input[type="checkbox"]:not(:checked))')
  if (unselectedRows.length >= 1) {
    unselectedRows.forEach(row => row.removeAttribute('data-bs-theme'))
  }

  updateSelectAllState()
}

if (tableSelectAll) {
  tableSelectAll.addEventListener('change', event => {
    allCheckboxes.forEach(checkbox => {
      checkbox.checked = event.target.checked
    })
    updateSelectedRows()
  })

  // Add change listener to all row checkboxes
  allCheckboxes.forEach(checkbox => {
    checkbox.addEventListener('change', updateSelectedRows)
  })

  // Initialize the state on load
  updateSelectAllState()
}

With radio buttons

For the row selection purpose with radio buttons, you should use standalone radio buttons with .visually-hidden labels describing their use.

The same rules apply as with checkboxes, except that there is no "all rows" selection. The JavaScript will only need to update the data-bs-theme on selected rows, as we have done in the example below.

OUDS Web table with radio buttons
Select Heading Heading Heading
This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table Cell Cell
Cell Cell Cell
Cell Cell Cell
<table class="table" id="tableWithRadios">
  <caption class="visually-hidden">OUDS Web table with radio buttons</caption>
  <thead>
    <tr>
      <th scope="col">Select</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <label class="radio-button-standalone">
          <input class="control-item-indicator" type="radio" name="radio1" value="1" />
          <span class="visually-hidden">Select row 1</span>
        </label>
      </td>
      <td>This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr data-bs-theme="dark">
      <td>
        <label class="radio-button-standalone">
          <input class="control-item-indicator" type="radio" name="radio1" value="2" checked />
          <span class="visually-hidden">Select row 2</span>
        </label>
      </td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
    <tr>
      <td>
        <label class="radio-button-standalone">
          <input class="control-item-indicator" type="radio" name="radio1" value="3" />
          <span class="visually-hidden">Select row 3</span>
        </label>
      </td>
      <td>Cell</td>
      <td>Cell</td>
      <td>Cell</td>
    </tr>
  </tbody>
</table>
html

To manage radio buttons active state (i.e. update data-bs-theme on the selected row), here is an example of JavaScript code:

// Manage radio buttons states: update data-bs-theme on selected row
const allRadios = document.querySelectorAll('#tableWithRadios tbody input[type="radio"]')
// Add change listener to all row radios
allRadios.forEach(radio => {
  radio.addEventListener('change', event => {
    const selectedRow = event.target.closest('tr')
    const allRows = document.querySelectorAll('#tableWithRadios tbody tr')

    // Remove data-bs-theme from all rows
    allRows.forEach(row => row.removeAttribute('data-bs-theme'))

    // Add data-bs-theme="dark" to the selected row
    if (selectedRow) {
      selectedRow.setAttribute('data-bs-theme', 'dark')
    }
  })
})

Rich content tables

You can also use other components in tables, such as tags, badges, switches, images or icons.

To align horizontally or vertically, you can use the following:

  • For horizontal alignment, use the .text-center class on <th> and <td> elements.
  • For vertical alignment, use the .align-middle class on the <tr> elements. Some components inside cells may require additional alignment (possibly via a utility class).
OUDS Web table with components and icons
Heading Heading Heading Heading Activated
Cell This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table

Maintenance

Warning

Cell Cell

Running

OK

Cell Cell

Error

Error

<table class="table">
  <caption class="visually-hidden">OUDS Web table with components and icons</caption>
  <thead>
    <tr>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col" class="text-center">Heading</th>
      <th scope="col">Activated</th>
    </tr>
  </thead>
  <tbody>
    <tr class="align-middle">
      <td>
        <svg class="bm-large-icon me-xsmall" aria-hidden="true">
          <use xlink:href="/orange-compact/docs/1.2/assets/img/ouds-web-sprite.svg#file-earmark-richtext"></use>
        </svg>Cell
      </td>
      <td>This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table</td>
      <td>
        <p class="tag tag-warning tag-small">
          <span class="tag-status-icon"></span>
          Maintenance
        </p>
      </td>
      <td class="text-center">
        <p class="badge badge-warning badge-large align-middle">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">Warning</span>
        </p>
      </td>
      <td>
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="1" />
          <span class="visually-hidden">Select row 1</span>
        </label>
      </td>
    </tr>
    <tr class="align-middle">
      <td>
        <img src="/orange-compact/docs/1.2/assets/img/thumbnail.png" alt="" class="bm-large-icon me-xsmall" />Cell
      </td>
      <td>Cell</td>
      <td>
        <p class="tag tag-positive tag-small">
          <span class="tag-status-icon"></span>
          Running
        </p>
      </td>
      <td class="text-center">
        <p class="badge badge-positive badge-large align-middle">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">OK</span>
        </p>
      </td>
      <td>
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="2" checked />
          <span class="visually-hidden">Select row 2</span>
        </label>
      </td>
    </tr>
    <tr class="align-middle">
      <td>
        <svg class="bm-large-icon me-xsmall" aria-hidden="true">
          <use xlink:href="/orange-compact/docs/1.2/assets/img/ouds-web-sprite.svg#file-earmark-richtext"></use>
        </svg>Cell
      </td>
      <td>Cell</td>
      <td>
        <p class="tag tag-negative tag-small">
          <span class="tag-status-icon"></span>
          Error
        </p>
      </td>
      <td class="text-center">
        <p class="badge badge-large align-middle">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">Error</span>
        </p>
      </td>
      <td>
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="3" />
          <span class="visually-hidden">Select row 3</span>
        </label>
      </td>
    </tr>
  </tbody>
</table>
html

Row height and alignment

To align vertically and preserve the row height, use the .align-middle class on the <tr> elements and .table-cell-component on <td> elements containing components that could cause vertical overflow.

OUDS Web table with components and icons preserving the row height
Heading Heading Heading Heading Activated
Cell This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table

Maintenance

Warning

Cell Cell

Running

OK

Cell Cell

Error

Error

<table class="table">
  <caption class="visually-hidden">OUDS Web table with components and icons preserving the row height</caption>
  <thead>
    <tr>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col">Heading</th>
      <th scope="col" class="text-center">Heading</th>
      <th scope="col" class="text-center">Activated</th>
    </tr>
  </thead>
  <tbody>
    <tr class="align-middle">
      <td>
        <svg class="bm-large-icon me-xsmall" aria-hidden="true">
          <use xlink:href="/orange-compact/docs/1.2/assets/img/ouds-web-sprite.svg#file-earmark-richtext"></use>
        </svg>Cell
      </td>
      <td>This is a much longer section of text that demonstrates what happens when text wraps within a cell in a table</td>
      <td class="table-cell-component">
        <p class="tag tag-warning tag-small">
          <span class="tag-status-icon"></span>
          Maintenance
        </p>
      </td>
      <td class="text-center table-cell-component">
        <p class="badge badge-warning badge-large">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">Warning</span>
        </p>
      </td>
      <td class="table-cell-component">
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="1" />
          <span class="visually-hidden">Select row 1</span>
        </label>
      </td>
    </tr>
    <tr class="align-middle">
      <td>
        <img src="/orange-compact/docs/1.2/assets/img/thumbnail.png" alt="" class="bm-large-icon me-xsmall" />Cell
      </td>
      <td>Cell</td>
      <td class="table-cell-component">
        <p class="tag tag-positive tag-small">
          <span class="tag-status-icon"></span>
          Running
        </p>
      </td>
      <td class="text-center table-cell-component">
        <p class="badge badge-positive badge-large">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">OK</span>
        </p>
      </td>
      <td class="table-cell-component">
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="2" checked />
          <span class="visually-hidden">Select row 2</span>
        </label>
      </td>
    </tr>
    <tr class="align-middle">
      <td>
        <svg class="bm-large-icon me-xsmall" aria-hidden="true">
          <use xlink:href="/orange-compact/docs/1.2/assets/img/ouds-web-sprite.svg#file-earmark-richtext"></use>
        </svg>Cell
      </td>
      <td>Cell</td>
      <td class="table-cell-component">
        <p class="tag tag-negative tag-small">
          <span class="tag-status-icon"></span>
          Error
        </p>
      </td>
      <td class="text-center table-cell-component">
        <p class="badge badge-large">
          <span class="badge-status-icon"></span>
          <span class="visually-hidden">Error</span>
        </p>
      </td>
      <td class="table-cell-component">
        <label class="switch-standalone">
          <input class="control-item-indicator" type="checkbox" role="switch"  value="3" />
          <span class="visually-hidden">Select row 3</span>
        </label>
      </td>
    </tr>
  </tbody>
</table>
html